Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.sentry.sqoop.binding; import java.lang.reflect.Constructor; import java.util.List; import java.util.Set; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.security.UserGroupInformation; import org.apache.sentry.core.common.exception.SentryUserException; import org.apache.sentry.core.common.ActiveRoleSet; import org.apache.sentry.core.common.Authorizable; import org.apache.sentry.core.common.Model; import org.apache.sentry.core.common.Subject; import org.apache.sentry.core.model.sqoop.Server; import org.apache.sentry.core.model.sqoop.SqoopActionConstant; import org.apache.sentry.core.model.sqoop.SqoopActionFactory; import org.apache.sentry.core.model.sqoop.SqoopPrivilegeModel; import org.apache.sentry.policy.common.PolicyEngine; import org.apache.sentry.provider.common.AuthorizationComponent; import org.apache.sentry.provider.common.AuthorizationProvider; import org.apache.sentry.provider.common.ProviderBackend; import org.apache.sentry.provider.common.ProviderBackendContext; import org.apache.sentry.provider.db.generic.SentryGenericProviderBackend; import org.apache.sentry.api.generic.thrift.SentryGenericServiceClient; import org.apache.sentry.api.generic.thrift.SentryGenericServiceClientFactory; import org.apache.sentry.api.generic.thrift.TAuthorizable; import org.apache.sentry.api.generic.thrift.TSentryGrantOption; import org.apache.sentry.api.generic.thrift.TSentryPrivilege; import org.apache.sentry.api.generic.thrift.TSentryRole; import org.apache.sentry.api.common.ApiConstants; import org.apache.sentry.api.tools.GenericPrivilegeConverter; import org.apache.sentry.sqoop.conf.SqoopAuthConf.AuthzConfVars; import org.apache.sqoop.common.SqoopException; import org.apache.sqoop.model.MPrivilege; import org.apache.sqoop.model.MResource; import org.apache.sqoop.model.MRole; import org.apache.sqoop.security.SecurityError; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Lists; import com.google.common.collect.Sets; public class SqoopAuthBinding { private static final Logger LOG = LoggerFactory.getLogger(SqoopAuthBinding.class); private static final String COMPONENT_TYPE = AuthorizationComponent.SQOOP; private final Configuration authConf; private final AuthorizationProvider authProvider; private final Server sqoopServer; private final Subject bindingSubject; private ProviderBackend providerBackend; private final SqoopActionFactory actionFactory = new SqoopActionFactory(); private final String SQOOP_POLICY_ENGINE_OLD = "org.apache.sentry.policy.sqoop.SimpleSqoopPolicyEngine"; public SqoopAuthBinding(Configuration authConf, String serverName) throws Exception { this.authConf = authConf; this.authConf.set(AuthzConfVars.AUTHZ_SERVER_NAME.getVar(), serverName); this.sqoopServer = new Server(serverName); this.authProvider = createAuthProvider(); /** The Sqoop server principal will use the binding */ this.bindingSubject = new Subject(UserGroupInformation.getCurrentUser().getShortUserName()); } /** * Instantiate the configured authz provider * @return {@link AuthorizationProvider} */ private AuthorizationProvider createAuthProvider() throws Exception { /** * get the authProvider class, policyEngine class, providerBackend class and resources from the sqoopAuthConf config */ String authProviderName = authConf.get(AuthzConfVars.AUTHZ_PROVIDER.getVar(), AuthzConfVars.AUTHZ_PROVIDER.getDefault()); String resourceName = authConf.get(AuthzConfVars.AUTHZ_PROVIDER_RESOURCE.getVar(), AuthzConfVars.AUTHZ_PROVIDER_RESOURCE.getDefault()); String providerBackendName = authConf.get(AuthzConfVars.AUTHZ_PROVIDER_BACKEND.getVar(), AuthzConfVars.AUTHZ_PROVIDER_BACKEND.getDefault()); String policyEngineName = authConf.get(AuthzConfVars.AUTHZ_POLICY_ENGINE.getVar(), AuthzConfVars.AUTHZ_POLICY_ENGINE.getDefault()); String serviceName = authConf.get(AuthzConfVars.AUTHZ_SERVER_NAME.getVar()); // for the backward compatibility if (SQOOP_POLICY_ENGINE_OLD.equals(policyEngineName)) { policyEngineName = AuthzConfVars.AUTHZ_POLICY_ENGINE.getDefault(); } if (LOG.isDebugEnabled()) { LOG.debug("Using authorization provider " + authProviderName + " with resource " + resourceName + ", policy engine " + policyEngineName + ", provider backend " + providerBackendName); } // the SqoopProviderBackend is deleted in SENTRY-828, this is for the compatible with the // previous Sentry. if ("org.apache.sentry.sqoop.binding.SqoopProviderBackend".equals(providerBackendName)) { providerBackendName = SentryGenericProviderBackend.class.getName(); } // for convenience, set the PrivilegeConverter. if (authConf.get(ApiConstants.ClientConfig.PRIVILEGE_CONVERTER) == null) { authConf.set(ApiConstants.ClientConfig.PRIVILEGE_CONVERTER, GenericPrivilegeConverter.class.getName()); } //Instantiate the configured providerBackend Constructor<?> providerBackendConstructor = Class.forName(providerBackendName) .getDeclaredConstructor(Configuration.class, String.class); providerBackendConstructor.setAccessible(true); providerBackend = (ProviderBackend) providerBackendConstructor .newInstance(new Object[] { authConf, resourceName }); if (providerBackend instanceof SentryGenericProviderBackend) { ((SentryGenericProviderBackend) providerBackend).setComponentType(COMPONENT_TYPE); ((SentryGenericProviderBackend) providerBackend).setServiceName(serviceName); } // Create backend context ProviderBackendContext context = new ProviderBackendContext(); context.setAllowPerDatabase(false); context.setValidators(SqoopPrivilegeModel.getInstance().getPrivilegeValidators(serviceName)); providerBackend.initialize(context); //Instantiate the configured policyEngine Constructor<?> policyConstructor = Class.forName(policyEngineName) .getDeclaredConstructor(ProviderBackend.class); policyConstructor.setAccessible(true); PolicyEngine policyEngine = (PolicyEngine) policyConstructor.newInstance(new Object[] { providerBackend }); //Instantiate the configured authProvider Constructor<?> constrctor = Class.forName(authProviderName).getDeclaredConstructor(Configuration.class, String.class, PolicyEngine.class, Model.class); constrctor.setAccessible(true); return (AuthorizationProvider) constrctor.newInstance( new Object[] { authConf, resourceName, policyEngine, SqoopPrivilegeModel.getInstance() }); } /** * Authorize access to a Sqoop privilege * @param subject * @param authorizable * @param action * @return true or false */ public boolean authorize(Subject subject, MPrivilege privilege) throws SentryUserException { List<Authorizable> authorizables = toAuthorizable(privilege.getResource()); if (!hasServerInclude(authorizables)) { authorizables.add(0, sqoopServer); } return authProvider.hasAccess(subject, authorizables, Sets.newHashSet(actionFactory.getActionByName(privilege.getAction())), ActiveRoleSet.ALL); } public boolean hasServerInclude(List<Authorizable> authorizables) { for (Authorizable authorizable : authorizables) { if (authorizable.getTypeName().equalsIgnoreCase(sqoopServer.getTypeName())) { return true; } } return false; } /** * The Sentry-296(generate client for connection pooling) has already finished development and reviewed by now. When it * was committed to master, the getClient method was needed to refactor using the connection pool */ private SentryGenericServiceClient getClient() throws Exception { return SentryGenericServiceClientFactory.create(authConf); } public void createRole(final Subject subject, final String role) throws SqoopException { execute(new Command<Void>() { @Override public Void run(SentryGenericServiceClient client) throws Exception { client.createRole(subject.getName(), role, COMPONENT_TYPE); return null; } }); } public void dropRole(final Subject subject, final String role) throws SqoopException { execute(new Command<Void>() { @Override public Void run(SentryGenericServiceClient client) throws Exception { client.dropRole(subject.getName(), role, COMPONENT_TYPE); return null; } }); } public List<MRole> listAllRoles(final Subject subject) throws SqoopException { Set<TSentryRole> tSentryRoles = execute(new Command<Set<TSentryRole>>() { @Override public Set<TSentryRole> run(SentryGenericServiceClient client) throws Exception { return client.listAllRoles(subject.getName(), COMPONENT_TYPE); } }); List<MRole> roles = Lists.newArrayList(); for (TSentryRole tRole : tSentryRoles) { roles.add(new MRole(tRole.getRoleName())); } return roles; } public List<MRole> listRolesByGroup(final Subject subject, final String groupName) throws SqoopException { Set<TSentryRole> tSentryRoles = execute(new Command<Set<TSentryRole>>() { @Override public Set<TSentryRole> run(SentryGenericServiceClient client) throws Exception { return client.listRolesByGroupName(subject.getName(), groupName, COMPONENT_TYPE); } }); List<MRole> roles = Lists.newArrayList(); for (TSentryRole tSentryRole : tSentryRoles) { roles.add(new MRole(tSentryRole.getRoleName())); } return roles; } public List<MPrivilege> listPrivilegeByRole(final Subject subject, final String role, final MResource resource) throws SqoopException { Set<TSentryPrivilege> tSentryPrivileges = execute(new Command<Set<TSentryPrivilege>>() { @Override public Set<TSentryPrivilege> run(SentryGenericServiceClient client) throws Exception { if (resource == null) { return client.listAllPrivilegesByRoleName(subject.getName(), role, COMPONENT_TYPE, sqoopServer.getName()); } else if (resource.getType().equalsIgnoreCase(MResource.TYPE.SERVER.name())) { return client.listAllPrivilegesByRoleName(subject.getName(), role, COMPONENT_TYPE, resource.getName()); } else { return client.listPrivilegesByRoleName(subject.getName(), role, COMPONENT_TYPE, sqoopServer.getName(), toAuthorizable(resource)); } } }); List<MPrivilege> privileges = Lists.newArrayList(); for (TSentryPrivilege tSentryPrivilege : tSentryPrivileges) { privileges.add(toSqoopPrivilege(tSentryPrivilege)); } return privileges; } public void grantPrivilege(final Subject subject, final String role, final MPrivilege privilege) throws SqoopException { execute(new Command<Void>() { @Override public Void run(SentryGenericServiceClient client) throws Exception { client.grantPrivilege(subject.getName(), role, COMPONENT_TYPE, toTSentryPrivilege(privilege)); return null; } }); } public void revokePrivilege(final Subject subject, final String role, final MPrivilege privilege) throws SqoopException { execute(new Command<Void>() { @Override public Void run(SentryGenericServiceClient client) throws Exception { client.revokePrivilege(subject.getName(), role, COMPONENT_TYPE, toTSentryPrivilege(privilege)); return null; } }); } public void grantGroupToRole(final Subject subject, final String group, final MRole role) throws SqoopException { execute(new Command<Void>() { @Override public Void run(SentryGenericServiceClient client) throws Exception { client.grantRoleToGroups(subject.getName(), role.getName(), COMPONENT_TYPE, Sets.newHashSet(group)); return null; } }); } public void revokeGroupfromRole(final Subject subject, final String group, final MRole role) throws SqoopException { execute(new Command<Void>() { @Override public Void run(SentryGenericServiceClient client) throws Exception { client.revokeRoleFromGroups(subject.getName(), role.getName(), COMPONENT_TYPE, Sets.newHashSet(group)); return null; } }); } public void renamePrivilege(final Subject subject, final MResource srcResource, final MResource dstResource) throws SqoopException { execute(new Command<Void>() { @Override public Void run(SentryGenericServiceClient client) throws Exception { client.renamePrivilege(subject.getName(), COMPONENT_TYPE, sqoopServer.getName(), toAuthorizable(srcResource), toAuthorizable(dstResource)); return null; } }); } public void dropPrivilege(final MResource resource) throws SqoopException { execute(new Command<Void>() { @Override public Void run(SentryGenericServiceClient client) throws Exception { TSentryPrivilege privilege = new TSentryPrivilege(); privilege.setComponent(COMPONENT_TYPE); privilege.setServiceName(sqoopServer.getName()); privilege.setAuthorizables(toTSentryAuthorizable(resource)); privilege.setAction(SqoopActionConstant.ALL); client.dropPrivilege(bindingSubject.getName(), COMPONENT_TYPE, privilege); return null; } }); } private MPrivilege toSqoopPrivilege(TSentryPrivilege tPrivilege) { //construct a sqoop resource boolean grantOption = false; if (tPrivilege.getGrantOption() == TSentryGrantOption.TRUE) { grantOption = true; } //construct a sqoop privilege return new MPrivilege(toSqoopResource(tPrivilege.getAuthorizables()), tPrivilege.getAction().equalsIgnoreCase(SqoopActionConstant.ALL) ? SqoopActionConstant.ALL_NAME : tPrivilege.getAction(), grantOption); } private MResource toSqoopResource(List<TAuthorizable> authorizables) { if (authorizables == null || authorizables.isEmpty()) { //server resource return new MResource(sqoopServer.getName(), MResource.TYPE.SERVER); } else { //currently Sqoop only has one-level hierarchy authorizable resource return new MResource(authorizables.get(0).getName(), authorizables.get(0).getType()); } } /** * construct a Sentry privilege to call by the thrift API * @param privilege * @return {@link TSentryPrivilege} */ private TSentryPrivilege toTSentryPrivilege(MPrivilege privilege) { TSentryPrivilege tSentryPrivilege = new TSentryPrivilege(); tSentryPrivilege.setComponent(COMPONENT_TYPE); tSentryPrivilege.setServiceName(sqoopServer.getName()); tSentryPrivilege.setAction( privilege.getAction().equalsIgnoreCase(SqoopActionConstant.ALL_NAME) ? SqoopActionConstant.ALL : privilege.getAction()); if (privilege.isWith_grant_option()) { tSentryPrivilege.setGrantOption(TSentryGrantOption.TRUE); } else { tSentryPrivilege.setGrantOption(TSentryGrantOption.FALSE); } tSentryPrivilege.setAuthorizables(toTSentryAuthorizable(privilege.getResource())); return tSentryPrivilege; } private List<TAuthorizable> toTSentryAuthorizable(MResource resource) { List<TAuthorizable> tAuthorizables = Lists.newArrayList(); /** * Currently Sqoop supports grant privileges on server object, but the server name must be equaled the configuration * of org.apache.sqoop.security.authorization.server_name in the Sqoop.properties. */ if (resource.getType().equalsIgnoreCase(MResource.TYPE.SERVER.name())) { if (!resource.getName().equalsIgnoreCase(sqoopServer.getName())) { throw new IllegalArgumentException(resource.getName() + " must be equal to " + sqoopServer.getName() + "\n" + " Currently Sqoop supports grant/revoke privileges on server object, but the server name must be equal to the configuration " + "of org.apache.sqoop.security.authorization.server_name in the Sqoop.properties"); } } else { tAuthorizables.add(new TAuthorizable(resource.getType(), resource.getName())); } return tAuthorizables; } private List<Authorizable> toAuthorizable(final MResource resource) { List<Authorizable> authorizables = Lists.newArrayList(); if (resource == null) { return authorizables; } authorizables.add(new Authorizable() { @Override public String getTypeName() { return resource.getType(); } @Override public String getName() { return resource.getName(); } }); return authorizables; } /** * A Command is a closure used to pass a block of code from individual * functions to execute, which centralizes connection error * handling. Command is parameterized on the return type of the function. */ private interface Command<T> { T run(SentryGenericServiceClient client) throws Exception; } private <T> T execute(Command<T> cmd) throws SqoopException { try (SentryGenericServiceClient client = getClient()) { return cmd.run(client); } catch (SentryUserException ex) { String msg = "Unable to excute command on sentry server: " + ex.getMessage(); LOG.error(msg, ex); throw new SqoopException(SecurityError.AUTH_0014, msg, ex); } catch (Exception ex) { String msg = "Unable to obtain client:" + ex.getMessage(); LOG.error(msg, ex); throw new SqoopException(SecurityError.AUTH_0014, msg, ex); } } }