SENTRY-1975 - Add sqoop support to SentryShellGeneric. Signed off by Sergio Pena.
Project: http://git-wip-us.apache.org/repos/asf/sentry/repo Commit: http://git-wip-us.apache.org/repos/asf/sentry/commit/9927bc4a Tree: http://git-wip-us.apache.org/repos/asf/sentry/tree/9927bc4a Diff: http://git-wip-us.apache.org/repos/asf/sentry/diff/9927bc4a Branch: refs/heads/akolb-cli Commit: 9927bc4a3a5d993b4e20d9855f8f5b16d1dbdfbb Parents: c3810a6 Author: Colm O hEigeartaigh <[email protected]> Authored: Mon Oct 16 10:31:00 2017 +0100 Committer: Colm O hEigeartaigh <[email protected]> Committed: Mon Oct 16 10:35:17 2017 +0100 ---------------------------------------------------------------------- bin/sentryShell | 1 + .../sentry/sqoop/binding/SqoopAuthBinding.java | 7 + .../tools/GenericPrivilegeConverter.java | 8 +- .../db/generic/tools/SentryShellGeneric.java | 5 + .../provider/db/tools/SentryShellCommon.java | 2 +- .../db/generic/tools/TestSentryShellSqoop.java | 523 +++++++++++++++++++ 6 files changed, 544 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/sentry/blob/9927bc4a/bin/sentryShell ---------------------------------------------------------------------- diff --git a/bin/sentryShell b/bin/sentryShell index 6fd2848..17b1429 100755 --- a/bin/sentryShell +++ b/bin/sentryShell @@ -62,6 +62,7 @@ while [ $# -gt 0 ]; do # Until you run out of parameters . . . "hive") shell=org.apache.sentry.provider.db.tools.SentryShellHive ;; "kafka") shell=org.apache.sentry.provider.db.generic.tools.SentryShellGeneric ;; "solr") shell=org.apache.sentry.provider.db.generic.tools.SentryShellGeneric ;; + "sqoop") shell=org.apache.sentry.provider.db.generic.tools.SentryShellGeneric ;; *) echo "Doesn't support the type $2!"; exit 1 ;; esac fi http://git-wip-us.apache.org/repos/asf/sentry/blob/9927bc4a/sentry-binding/sentry-binding-sqoop/src/main/java/org/apache/sentry/sqoop/binding/SqoopAuthBinding.java ---------------------------------------------------------------------- diff --git a/sentry-binding/sentry-binding-sqoop/src/main/java/org/apache/sentry/sqoop/binding/SqoopAuthBinding.java b/sentry-binding/sentry-binding-sqoop/src/main/java/org/apache/sentry/sqoop/binding/SqoopAuthBinding.java index 5d0831e..79ec477 100644 --- a/sentry-binding/sentry-binding-sqoop/src/main/java/org/apache/sentry/sqoop/binding/SqoopAuthBinding.java +++ b/sentry-binding/sentry-binding-sqoop/src/main/java/org/apache/sentry/sqoop/binding/SqoopAuthBinding.java @@ -43,6 +43,8 @@ import org.apache.sentry.provider.db.generic.service.thrift.TAuthorizable; import org.apache.sentry.provider.db.generic.service.thrift.TSentryGrantOption; import org.apache.sentry.provider.db.generic.service.thrift.TSentryPrivilege; import org.apache.sentry.provider.db.generic.service.thrift.TSentryRole; +import org.apache.sentry.provider.db.generic.tools.GenericPrivilegeConverter; +import org.apache.sentry.service.thrift.ServiceConstants; import org.apache.sentry.sqoop.conf.SqoopAuthConf.AuthzConfVars; import org.apache.sqoop.common.SqoopException; import org.apache.sqoop.model.MPrivilege; @@ -109,6 +111,11 @@ public class SqoopAuthBinding { providerBackendName = SentryGenericProviderBackend.class.getName(); } + // for convenience, set the PrivilegeConverter. + if (authConf.get(ServiceConstants.ClientConfig.PRIVILEGE_CONVERTER) == null) { + authConf.set(ServiceConstants.ClientConfig.PRIVILEGE_CONVERTER, GenericPrivilegeConverter.class.getName()); + } + //Instantiate the configured providerBackend Constructor<?> providerBackendConstructor = Class.forName(providerBackendName) .getDeclaredConstructor(Configuration.class, String.class); http://git-wip-us.apache.org/repos/asf/sentry/blob/9927bc4a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/tools/GenericPrivilegeConverter.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/tools/GenericPrivilegeConverter.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/tools/GenericPrivilegeConverter.java index ea8cf07..526a521 100644 --- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/tools/GenericPrivilegeConverter.java +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/tools/GenericPrivilegeConverter.java @@ -39,6 +39,8 @@ import org.apache.sentry.core.model.kafka.KafkaModelAuthorizables; import org.apache.sentry.core.model.kafka.KafkaPrivilegeModel; import org.apache.sentry.core.model.search.SearchModelAuthorizables; import org.apache.sentry.core.model.search.SearchPrivilegeModel; +import org.apache.sentry.core.model.sqoop.SqoopModelAuthorizables; +import org.apache.sentry.core.model.sqoop.SqoopPrivilegeModel; import org.apache.sentry.provider.common.AuthorizationComponent; import org.apache.sentry.provider.db.generic.service.thrift.TAuthorizable; import org.apache.sentry.provider.db.generic.service.thrift.TSentryGrantOption; @@ -47,7 +49,7 @@ import org.apache.sentry.provider.db.generic.tools.command.TSentryPrivilegeConve import org.apache.shiro.config.ConfigurationException; /** - * A TSentryPrivilegeConverter implementation for "Generic" privileges, covering Apache Kafka and Apache Solr. + * A TSentryPrivilegeConverter implementation for "Generic" privileges, covering Apache Kafka, Apache Solr and Apache Sqoop. * It converts privilege Strings to TSentryPrivilege Objects, and vice versa, for Generic clients. * * When a privilege String is converted to a TSentryPrivilege in "fromString", the validators associated with the @@ -160,6 +162,8 @@ public class GenericPrivilegeConverter implements TSentryPrivilegeConverter { return KafkaPrivilegeModel.getInstance().getPrivilegeValidators(); } else if ("SOLR".equals(component)) { return SearchPrivilegeModel.getInstance().getPrivilegeValidators(); + } else if (AuthorizationComponent.SQOOP.equals(component)) { + return SqoopPrivilegeModel.getInstance().getPrivilegeValidators(service); } throw new Exception("Invalid component specified for GenericPrivilegeCoverter: " + component); @@ -170,6 +174,8 @@ public class GenericPrivilegeConverter implements TSentryPrivilegeConverter { return KafkaModelAuthorizables.from(keyValue); } else if ("SOLR".equals(component)) { return SearchModelAuthorizables.from(keyValue); + } else if (AuthorizationComponent.SQOOP.equals(component)) { + return SqoopModelAuthorizables.from(keyValue); } throw new Exception("Invalid component specified for GenericPrivilegeCoverter: " + component); http://git-wip-us.apache.org/repos/asf/sentry/blob/9927bc4a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/tools/SentryShellGeneric.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/tools/SentryShellGeneric.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/tools/SentryShellGeneric.java index ab9d969..25c8003 100644 --- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/tools/SentryShellGeneric.java +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/tools/SentryShellGeneric.java @@ -49,6 +49,7 @@ public class SentryShellGeneric extends SentryShellCommon { private static final Logger LOGGER = LoggerFactory.getLogger(SentryShellGeneric.class); private static final String KAFKA_SERVICE_NAME = "sentry.service.client.kafka.service.name"; private static final String SOLR_SERVICE_NAME = "sentry.service.client.solr.service.name"; + private static final String SQOOP_SERVICE_NAME = "sentry.service.client.sqoop.service.name"; @Override public void run() throws Exception { @@ -100,6 +101,8 @@ public class SentryShellGeneric extends SentryShellCommon { return AuthorizationComponent.KAFKA; } else if (type == TYPE.solr) { return "SOLR"; + } else if (type == TYPE.sqoop) { + return AuthorizationComponent.SQOOP; } throw new Exception("Invalid type specified for SentryShellGeneric: " + type); @@ -110,6 +113,8 @@ public class SentryShellGeneric extends SentryShellCommon { return conf.get(KAFKA_SERVICE_NAME, AuthorizationComponent.KAFKA); } else if (type == TYPE.solr) { return conf.get(SOLR_SERVICE_NAME, "service1"); + } else if (type == TYPE.sqoop) { + return conf.get(SQOOP_SERVICE_NAME, "sqoopServer1"); } throw new Exception("Invalid type specified for SentryShellGeneric: " + type); http://git-wip-us.apache.org/repos/asf/sentry/blob/9927bc4a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/tools/SentryShellCommon.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/tools/SentryShellCommon.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/tools/SentryShellCommon.java index b2b6f4f..dd245ea 100644 --- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/tools/SentryShellCommon.java +++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/tools/SentryShellCommon.java @@ -36,7 +36,7 @@ import org.apache.commons.lang.StringUtils; */ abstract public class SentryShellCommon { - public enum TYPE { kafka, hive, solr }; + public enum TYPE { kafka, hive, solr, sqoop }; public static final String OPTION_DESC_HELP = "Shell usage"; public static final String OPTION_DESC_CONF = "sentry-site file path"; http://git-wip-us.apache.org/repos/asf/sentry/blob/9927bc4a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/generic/tools/TestSentryShellSqoop.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/generic/tools/TestSentryShellSqoop.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/generic/tools/TestSentryShellSqoop.java new file mode 100644 index 0000000..1f49cce --- /dev/null +++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/generic/tools/TestSentryShellSqoop.java @@ -0,0 +1,523 @@ +/** + * 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.provider.db.generic.tools; + +import com.google.common.collect.Sets; +import com.google.common.io.Files; +import org.apache.commons.io.FileUtils; +import org.apache.sentry.core.common.exception.SentryUserException; +import org.apache.sentry.provider.common.AuthorizationComponent; +import org.apache.sentry.provider.db.generic.service.thrift.SentryGenericServiceIntegrationBase; +import org.apache.sentry.provider.db.generic.service.thrift.TSentryPrivilege; +import org.apache.sentry.provider.db.generic.service.thrift.TSentryRole; +import org.apache.sentry.provider.db.tools.SentryShellCommon; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import static org.junit.Assert.*; + +public class TestSentryShellSqoop extends SentryGenericServiceIntegrationBase { + private File confDir; + private File confPath; + private static String TEST_ROLE_NAME_1 = "testRole1"; + private static String TEST_ROLE_NAME_2 = "testRole2"; + private String requestorName = ""; + private String service = "sqoopServer1"; + + @Before + public void prepareForTest() throws Exception { + confDir = Files.createTempDir(); + confPath = new File(confDir, "sentry-site.xml"); + if (confPath.createNewFile()) { + FileOutputStream to = new FileOutputStream(confPath); + conf.writeXml(to); + to.close(); + } + requestorName = clientUgi.getShortUserName();//.getProperty("user.name", ""); + Set<String> requestorUserGroupNames = Sets.newHashSet(ADMIN_GROUP); + setLocalGroupMapping(requestorName, requestorUserGroupNames); + // add ADMIN_USER for the after() in SentryServiceIntegrationBase + setLocalGroupMapping(ADMIN_USER, requestorUserGroupNames); + writePolicyFile(); + } + + @After + public void clearTestData() throws Exception { + FileUtils.deleteQuietly(confDir); + } + + @Test + public void testCreateDropRole() throws Exception { + runTestAsSubject(new TestOperation() { + @Override + public void runTestAsSubject() throws Exception { + // test: create role with -cr + String[] args = { "-cr", "-r", TEST_ROLE_NAME_1, "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + SentryShellGeneric.main(args); + // test: create role with --create_role + args = new String[] { "--create_role", "-r", TEST_ROLE_NAME_2, "-conf", + confPath.getAbsolutePath(), "-t", "sqoop" }; + SentryShellGeneric.main(args); + + // validate the result, list roles with -lr + args = new String[] { "-lr", "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + SentryShellGeneric sentryShell = new SentryShellGeneric(); + Set<String> roleNames = getShellResultWithOSRedirect(sentryShell, args, true); + validateRoleNames(roleNames, TEST_ROLE_NAME_1, TEST_ROLE_NAME_2); + + // validate the result, list roles with --list_role + args = new String[] { "--list_role", "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + sentryShell = new SentryShellGeneric(); + roleNames = getShellResultWithOSRedirect(sentryShell, args, true); + validateRoleNames(roleNames, TEST_ROLE_NAME_1, TEST_ROLE_NAME_2); + + // test: drop role with -dr + args = new String[] { "-dr", "-r", TEST_ROLE_NAME_1, "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + SentryShellGeneric.main(args); + // test: drop role with --drop_role + args = new String[] { "--drop_role", "-r", TEST_ROLE_NAME_2, "-conf", + confPath.getAbsolutePath(), "-t", "sqoop" }; + SentryShellGeneric.main(args); + + // validate the result + Set<TSentryRole> roles = client.listAllRoles(requestorName, AuthorizationComponent.SQOOP); + assertEquals("Incorrect number of roles", 0, roles.size()); + } + }); + } + + @Test + public void testAddDeleteRoleForGroup() throws Exception { + runTestAsSubject(new TestOperation() { + @Override + public void runTestAsSubject() throws Exception { + // Group names are case sensitive - mixed case names should work + String TEST_GROUP_1 = "testGroup1"; + String TEST_GROUP_2 = "testGroup2"; + String TEST_GROUP_3 = "testGroup3"; + + // create the role for test + client.createRole(requestorName, TEST_ROLE_NAME_1, AuthorizationComponent.SQOOP); + client.createRole(requestorName, TEST_ROLE_NAME_2, AuthorizationComponent.SQOOP); + // test: add role to group with -arg + String[] args = { "-arg", "-r", TEST_ROLE_NAME_1, "-g", TEST_GROUP_1, "-conf", + confPath.getAbsolutePath(), "-t", "sqoop" }; + SentryShellGeneric.main(args); + // test: add role to multiple groups + args = new String[] { "-arg", "-r", TEST_ROLE_NAME_1, "-g", TEST_GROUP_2 + "," + TEST_GROUP_3, + "-conf", + confPath.getAbsolutePath(), "-t", "sqoop" }; + SentryShellGeneric.main(args); + // test: add role to group with --add_role_group + args = new String[] { "--add_role_group", "-r", TEST_ROLE_NAME_2, "-g", TEST_GROUP_1, + "-conf", + confPath.getAbsolutePath(), "-t", "sqoop" }; + SentryShellGeneric.main(args); + + // validate the result list roles with -lr and -g + args = new String[] { "-lr", "-g", TEST_GROUP_1, "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + SentryShellGeneric sentryShell = new SentryShellGeneric(); + Set<String> roleNames = getShellResultWithOSRedirect(sentryShell, args, true); + validateRoleNames(roleNames, TEST_ROLE_NAME_1, TEST_ROLE_NAME_2); + + // list roles with --list_role and -g + args = new String[] { "--list_role", "-g", TEST_GROUP_2, "-conf", + confPath.getAbsolutePath(), "-t", "sqoop" }; + sentryShell = new SentryShellGeneric(); + roleNames = getShellResultWithOSRedirect(sentryShell, args, true); + validateRoleNames(roleNames, TEST_ROLE_NAME_1); + + args = new String[] { "--list_role", "-g", TEST_GROUP_3, "-conf", + confPath.getAbsolutePath(), "-t", "sqoop" }; + sentryShell = new SentryShellGeneric(); + roleNames = getShellResultWithOSRedirect(sentryShell, args, true); + validateRoleNames(roleNames, TEST_ROLE_NAME_1); + + // test: delete role from group with -drg + args = new String[] { "-drg", "-r", TEST_ROLE_NAME_1, "-g", TEST_GROUP_1, "-conf", + confPath.getAbsolutePath(), "-t", "sqoop" }; + SentryShellGeneric.main(args); + // test: delete role to multiple groups + args = new String[] { "-drg", "-r", TEST_ROLE_NAME_1, "-g", TEST_GROUP_2 + "," + TEST_GROUP_3, + "-conf", + confPath.getAbsolutePath(), "-t", "sqoop" }; + SentryShellGeneric.main(args); + // test: delete role from group with --delete_role_group + args = new String[] { "--delete_role_group", "-r", TEST_ROLE_NAME_2, "-g", TEST_GROUP_1, + "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + SentryShellGeneric.main(args); + + // validate the result + Set<TSentryRole> roles = client.listRolesByGroupName(requestorName, TEST_GROUP_1, AuthorizationComponent.SQOOP); + assertEquals("Incorrect number of roles", 0, roles.size()); + roles = client.listRolesByGroupName(requestorName, TEST_GROUP_2, AuthorizationComponent.SQOOP); + assertEquals("Incorrect number of roles", 0, roles.size()); + roles = client.listRolesByGroupName(requestorName, TEST_GROUP_3, AuthorizationComponent.SQOOP); + assertEquals("Incorrect number of roles", 0, roles.size()); + // clear the test data + client.dropRole(requestorName, TEST_ROLE_NAME_1, AuthorizationComponent.SQOOP); + client.dropRole(requestorName, TEST_ROLE_NAME_2, AuthorizationComponent.SQOOP); + } + }); + } + + @Test + public void testCaseSensitiveGroupName() throws Exception { + runTestAsSubject(new TestOperation() { + @Override + public void runTestAsSubject() throws Exception { + + // create the role for test + client.createRole(requestorName, TEST_ROLE_NAME_1, AuthorizationComponent.SQOOP); + // add role to a group (lower case) + String[] args = {"-arg", "-r", TEST_ROLE_NAME_1, "-g", "group1", "-conf", + confPath.getAbsolutePath(), "-t", "sqoop"}; + SentryShellGeneric.main(args); + + // validate the roles when group name is same case as above + args = new String[]{"-lr", "-g", "group1", "-conf", confPath.getAbsolutePath(), "-t", "sqoop"}; + SentryShellGeneric sentryShell = new SentryShellGeneric(); + Set<String> roleNames = getShellResultWithOSRedirect(sentryShell, args, true); + validateRoleNames(roleNames, TEST_ROLE_NAME_1); + + // roles should be empty when group name is different case than above + args = new String[]{"-lr", "-g", "GROUP1", "-conf", confPath.getAbsolutePath(), "-t", "sqoop"}; + roleNames = getShellResultWithOSRedirect(sentryShell, args, true); + validateRoleNames(roleNames); + } + }); + } + + public static String grant(boolean shortOption) { + return shortOption ? "-gpr" : "--grant_privilege_role"; + } + + public static String revoke(boolean shortOption) { + return shortOption ? "-rpr" : "--revoke_privilege_role"; + } + + public static String list(boolean shortOption) { + return shortOption ? "-lp" : "--list_privilege"; + } + + private void assertGrantRevokePrivilege(final boolean shortOption) throws Exception { + runTestAsSubject(new TestOperation() { + @Override + public void runTestAsSubject() throws Exception { + // create the role for test + client.createRole(requestorName, TEST_ROLE_NAME_1, AuthorizationComponent.SQOOP); + client.createRole(requestorName, TEST_ROLE_NAME_2, AuthorizationComponent.SQOOP); + + String [] privs = { + "SERVER=sqoopserver1->CONNECTOR=c1->action=read", + "SERVER=sqoopserver1->JOB=j1->action=write", + "SERVER=sqoopserver1->LINK=l1->action=read", + }; + for (int i = 0; i < privs.length; ++i) { + // test: grant privilege to role + String [] args = new String [] { grant(shortOption), "-r", TEST_ROLE_NAME_1, "-p", + privs[ i ], + "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + SentryShellGeneric.main(args); + } + + // test the list privilege + String [] args = new String[] { list(shortOption), "-r", TEST_ROLE_NAME_1, "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + SentryShellGeneric sentryShell = new SentryShellGeneric(); + Set<String> privilegeStrs = getShellResultWithOSRedirect(sentryShell, args, true); + + assertEquals("Incorrect number of privileges", privs.length, privilegeStrs.size()); + for (int i = 0; i < privs.length; ++i) { + assertTrue("Expected privilege: " + privs[i] + " in " + Arrays.toString(privilegeStrs.toArray()), privilegeStrs.contains(privs[i])); + } + + for (int i = 0; i < privs.length; ++i) { + args = new String[] { revoke(shortOption), "-r", TEST_ROLE_NAME_1, "-p", + privs[ i ], "-conf", + confPath.getAbsolutePath(), "-t", "sqoop" }; + SentryShellGeneric.main(args); + Set<TSentryPrivilege> privileges = client.listPrivilegesByRoleName(requestorName, + TEST_ROLE_NAME_1, AuthorizationComponent.SQOOP, service); + assertEquals("Incorrect number of privileges. Received privileges: " + Arrays.toString(privileges.toArray()), privs.length - (i + 1), privileges.size()); + } + + // clear the test data + client.dropRole(requestorName, TEST_ROLE_NAME_1, AuthorizationComponent.SQOOP); + client.dropRole(requestorName, TEST_ROLE_NAME_2, AuthorizationComponent.SQOOP); + } + }); + } + + + @Test + public void testGrantRevokePrivilegeWithShortOption() throws Exception { + assertGrantRevokePrivilege(true); + } + + @Test + public void testGrantRevokePrivilegeWithLongOption() throws Exception { + assertGrantRevokePrivilege(false); + } + + @Test + public void testNegativeCaseWithInvalidArgument() throws Exception { + runTestAsSubject(new TestOperation() { + @Override + public void runTestAsSubject() throws Exception { + client.createRole(requestorName, TEST_ROLE_NAME_1, AuthorizationComponent.SQOOP); + // test: create duplicate role with -cr + String[] args = { "-cr", "-r", TEST_ROLE_NAME_1, "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + SentryShellGeneric sentryShell = new SentryShellGeneric(); + try { + sentryShell.executeShell(args); + fail("Exception should be thrown for creating duplicate role"); + } catch (SentryUserException e) { + // expected exception + } catch (Exception e) { + fail ("Unexpected exception received. " + e); + } + + // test: drop non-exist role with -dr + args = new String[] { "-dr", "-r", TEST_ROLE_NAME_2, "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + sentryShell = new SentryShellGeneric(); + try { + sentryShell.executeShell(args); + fail("Exception should be thrown for dropping non-exist role"); + } catch (SentryUserException e) { + // excepted exception + } catch (Exception e) { + fail ("Unexpected exception received. " + e); + } + + // test: add non-exist role to group with -arg + args = new String[] { "-arg", "-r", TEST_ROLE_NAME_2, "-g", "testGroup1", "-conf", + confPath.getAbsolutePath(), "-t", "sqoop" }; + sentryShell = new SentryShellGeneric(); + try { + sentryShell.executeShell(args); + fail("Exception should be thrown for granting non-exist role to group"); + } catch (SentryUserException e) { + // excepted exception + } catch (Exception e) { + fail ("Unexpected exception received. " + e); + } + + // test: drop group from non-exist role with -drg + args = new String[] { "-drg", "-r", TEST_ROLE_NAME_2, "-g", "testGroup1", "-conf", + confPath.getAbsolutePath(), "-t", "sqoop" }; + sentryShell = new SentryShellGeneric(); + try { + sentryShell.executeShell(args); + fail("Exception should be thrown for drop group from non-exist role"); + } catch (SentryUserException e) { + // excepted exception + } catch (Exception e) { + fail ("Unexpected exception received. " + e); + } + + // test: grant privilege to role with the error privilege format + args = new String[] { "-gpr", "-r", TEST_ROLE_NAME_1, "-p", "serverserver1->action=all", + "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + sentryShell = new SentryShellGeneric(); + try { + sentryShell.executeShell(args); + fail("Exception should be thrown for the error privilege format, invalid key value."); + } catch (IllegalArgumentException e) { + // excepted exception + } catch (Exception e) { + fail ("Unexpected exception received. " + e); + } + + // clear the test data + client.dropRole(requestorName, TEST_ROLE_NAME_1, AuthorizationComponent.SQOOP); + } + }); + } + + @Test + public void testNegativeCaseWithoutRequiredArgument() throws Exception { + runTestAsSubject(new TestOperation() { + @Override + public void runTestAsSubject() throws Exception { + String strOptionConf = "conf"; + client.createRole(requestorName, TEST_ROLE_NAME_1, AuthorizationComponent.SQOOP); + // test: the conf is required argument + String[] args = { "-cr", "-r", TEST_ROLE_NAME_1, "-t", "sqoop" }; + SentryShellGeneric sentryShell = new SentryShellGeneric(); + validateMissingParameterMsg(sentryShell, args, + SentryShellCommon.PREFIX_MESSAGE_MISSING_OPTION + strOptionConf); + + // test: -r is required when create role + args = new String[] { "-cr", "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + sentryShell = new SentryShellGeneric(); + validateMissingParameterMsg(sentryShell, args, + SentryShellCommon.PREFIX_MESSAGE_MISSING_OPTION + SentryShellCommon.OPTION_DESC_ROLE_NAME); + + // test: -r is required when drop role + args = new String[] { "-dr", "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + sentryShell = new SentryShellGeneric(); + validateMissingParameterMsg(sentryShell, args, + SentryShellCommon.PREFIX_MESSAGE_MISSING_OPTION + SentryShellCommon.OPTION_DESC_ROLE_NAME); + + // test: -r is required when add role to group + args = new String[] { "-arg", "-g", "testGroup1", "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + sentryShell = new SentryShellGeneric(); + validateMissingParameterMsg(sentryShell, args, + SentryShellCommon.PREFIX_MESSAGE_MISSING_OPTION + SentryShellCommon.OPTION_DESC_ROLE_NAME); + + // test: -g is required when add role to group + args = new String[] { "-arg", "-r", TEST_ROLE_NAME_2, "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + sentryShell = new SentryShellGeneric(); + validateMissingParameterMsg(sentryShell, args, + SentryShellCommon.PREFIX_MESSAGE_MISSING_OPTION + SentryShellCommon.OPTION_DESC_GROUP_NAME); + + // test: -r is required when delete role from group + args = new String[] { "-drg", "-g", "testGroup1", "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + sentryShell = new SentryShellGeneric(); + validateMissingParameterMsg(sentryShell, args, + SentryShellCommon.PREFIX_MESSAGE_MISSING_OPTION + SentryShellCommon.OPTION_DESC_ROLE_NAME); + + // test: -g is required when delete role from group + args = new String[] { "-drg", "-r", TEST_ROLE_NAME_2, "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + sentryShell = new SentryShellGeneric(); + validateMissingParameterMsg(sentryShell, args, + SentryShellCommon.PREFIX_MESSAGE_MISSING_OPTION + SentryShellCommon.OPTION_DESC_GROUP_NAME); + + // test: -r is required when grant privilege to role + args = new String[] { "-gpr", "-p", "server=server1", "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + sentryShell = new SentryShellGeneric(); + validateMissingParameterMsg(sentryShell, args, + SentryShellCommon.PREFIX_MESSAGE_MISSING_OPTION + SentryShellCommon.OPTION_DESC_ROLE_NAME); + + // test: -p is required when grant privilege to role + args = new String[] { "-gpr", "-r", TEST_ROLE_NAME_1, "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + sentryShell = new SentryShellGeneric(); + validateMissingParameterMsg(sentryShell, args, + SentryShellCommon.PREFIX_MESSAGE_MISSING_OPTION + SentryShellCommon.OPTION_DESC_PRIVILEGE); + + // test: action is required in privilege + args = new String[] { "-gpr", "-r", TEST_ROLE_NAME_1, "-conf", confPath.getAbsolutePath(), "-p", "Server=sqoopServer1->Connector", "-t", "sqoop" }; + sentryShell = new SentryShellGeneric(); + try { + getShellResultWithOSRedirect(sentryShell, args, false); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } catch (Exception e) { + fail ("Unexpected exception received. " + e); + } + + // test: -r is required when revoke privilege from role + args = new String[] { "-rpr", "-p", "Server=sqoopServer1->Connector->action=*", "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + sentryShell = new SentryShellGeneric(); + validateMissingParameterMsg(sentryShell, args, + SentryShellCommon.PREFIX_MESSAGE_MISSING_OPTION + SentryShellCommon.OPTION_DESC_ROLE_NAME); + + // test: -p is required when revoke privilege from role + args = new String[] { "-rpr", "-r", TEST_ROLE_NAME_1, "-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + sentryShell = new SentryShellGeneric(); + validateMissingParameterMsg(sentryShell, args, + SentryShellCommon.PREFIX_MESSAGE_MISSING_OPTION + SentryShellCommon.OPTION_DESC_PRIVILEGE); + + // test: command option is required for shell + args = new String[] {"-conf", confPath.getAbsolutePath(), "-t", "sqoop" }; + sentryShell = new SentryShellGeneric(); + validateMissingParameterMsgsContains(sentryShell, args, + SentryShellCommon.PREFIX_MESSAGE_MISSING_OPTION + "[", + "-arg Add role to group", + "-cr Create role", + "-rpr Revoke privilege from role", + "-drg Delete role from group", + "-lr List role", + "-lp List privilege", + "-gpr Grant privilege to role", + "-dr Drop role"); + + // clear the test data + client.dropRole(requestorName, TEST_ROLE_NAME_1, AuthorizationComponent.SQOOP); + } + }); + } + + // redirect the System.out to ByteArrayOutputStream, then execute the command and parse the result. + private Set<String> getShellResultWithOSRedirect(SentryShellGeneric sentryShell, + String[] args, boolean expectedExecuteResult) throws Exception { + PrintStream oldOut = System.out; + ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outContent)); + assertEquals(expectedExecuteResult, sentryShell.executeShell(args)); + Set<String> resultSet = Sets.newHashSet(outContent.toString().split("\n")); + System.setOut(oldOut); + return resultSet; + } + + private void validateRoleNames(Set<String> roleNames, String ... expectedRoleNames) { + if (expectedRoleNames != null && expectedRoleNames.length > 0) { + assertEquals("Found: " + roleNames.size() + " roles, expected: " + expectedRoleNames.length, + expectedRoleNames.length, roleNames.size()); + Set<String> lowerCaseRoles = new HashSet<String>(); + for (String role : roleNames) { + lowerCaseRoles.add(role.toLowerCase()); + } + + for (String expectedRole : expectedRoleNames) { + assertTrue("Expected role: " + expectedRole, + lowerCaseRoles.contains(expectedRole.toLowerCase())); + } + } + } + + private void validateMissingParameterMsg(SentryShellGeneric sentryShell, String[] args, + String expectedErrorMsg) throws Exception { + Set<String> errorMsgs = getShellResultWithOSRedirect(sentryShell, args, false); + assertTrue("Expected error message: " + expectedErrorMsg, errorMsgs.contains(expectedErrorMsg)); + } + + private void validateMissingParameterMsgsContains(SentryShellGeneric sentryShell, String[] args, + String ... expectedErrorMsgsContains) throws Exception { + Set<String> errorMsgs = getShellResultWithOSRedirect(sentryShell, args, false); + boolean foundAllMessages = false; + Iterator<String> it = errorMsgs.iterator(); + while (it.hasNext()) { + String errorMessage = it.next(); + boolean missingExpected = false; + for (String expectedContains : expectedErrorMsgsContains) { + if (!errorMessage.contains(expectedContains)) { + missingExpected = true; + break; + } + } + if (!missingExpected) { + foundAllMessages = true; + break; + } + } + assertTrue(foundAllMessages); + } +}
