Repository: incubator-sentry Updated Branches: refs/heads/master 3ee3e9320 -> 67031139f
SENTRY-826: TRUNCATE on empty partitioned table in Hive fails (Li Li via Lenni Kuff) Change-Id: I1a1969e169ad014915473435ae62b4c1e054bac7 Project: http://git-wip-us.apache.org/repos/asf/incubator-sentry/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-sentry/commit/67031139 Tree: http://git-wip-us.apache.org/repos/asf/incubator-sentry/tree/67031139 Diff: http://git-wip-us.apache.org/repos/asf/incubator-sentry/diff/67031139 Branch: refs/heads/master Commit: 67031139f0db369ed4969a23e97308c941a8c953 Parents: 3ee3e93 Author: Lenni Kuff <lsk...@cloudera.com> Authored: Sun Jan 10 00:04:40 2016 -0800 Committer: Lenni Kuff <lsk...@cloudera.com> Committed: Sun Jan 10 00:04:40 2016 -0800 ---------------------------------------------------------------------- .../binding/hive/HiveAuthzBindingHook.java | 14 ++ .../e2e/hive/TestPrivilegesAtTableScope.java | 149 +++++++++++++++---- 2 files changed, 136 insertions(+), 27 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/67031139/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/HiveAuthzBindingHook.java ---------------------------------------------------------------------- diff --git a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/HiveAuthzBindingHook.java b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/HiveAuthzBindingHook.java index 994af8a..57e4689 100644 --- a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/HiveAuthzBindingHook.java +++ b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/HiveAuthzBindingHook.java @@ -28,6 +28,7 @@ import java.util.EnumSet; import java.util.List; import java.util.Set; +import com.google.common.base.Preconditions; import org.apache.hadoop.hive.common.JavaUtils; import org.apache.hadoop.hive.conf.HiveConf; import org.apache.hadoop.hive.conf.HiveConf.ConfVars; @@ -266,6 +267,19 @@ public class HiveAuthzBindingHook extends AbstractSemanticAnalyzerHook { // "FORMATTED/EXTENDED". isDescTableBasic = (ast.getChildCount() == 1); break; + case HiveParser.TOK_TRUNCATETABLE: + // SENTRY-826: + // Truncate empty partitioned table should throw SemanticException only if the + // user does not have permission. + // In postAnalyze, currOutDB and currOutTbl will be added into outputHierarchy + // which will be validated in the hiveAuthzBinding.authorize method. + Preconditions.checkArgument(ast.getChildCount() == 1); + // childcount is 1 for table without partition, 2 for table with partitions + Preconditions.checkArgument(ast.getChild(0).getChildCount() >= 1); + Preconditions.checkArgument(ast.getChild(0).getChild(0).getChildCount() == 1); + currOutDB = extractDatabase((ASTNode) ast.getChild(0)); + currOutTab = extractTable((ASTNode) ast.getChild(0).getChild(0).getChild(0)); + break; default: currDB = getCanonicalDb(); break; http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/67031139/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegesAtTableScope.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegesAtTableScope.java b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegesAtTableScope.java index 56776db..b5240ea 100644 --- a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegesAtTableScope.java +++ b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegesAtTableScope.java @@ -450,44 +450,70 @@ public class TestPrivilegesAtTableScope extends AbstractTestWithStaticConfigurat statement.close(); connection.close(); - policyFile - .addRolesToGroup(USERGROUP1, "all_tab1") - .addPermissionsToRole("all_tab1", - "server=server1->db=" + DB1 + "->table=" + TBL2) - .addRolesToGroup(USERGROUP2, "drop_tab1") - .addPermissionsToRole("drop_tab1", - "server=server1->db=" + DB1 + "->table=" + TBL3 + "->action=drop", - "server=server1->db=" + DB1 + "->table=" + TBL3 + "->action=select") - .addRolesToGroup(USERGROUP3, "select_tab1") - .addPermissionsToRole("select_tab1", - "server=server1->db=" + DB1 + "->table=" + TBL1 + "->action=select"); + // add roles and grant permissions + updatePolicyFile(); + + // test truncate table without partitions + truncateTableTests(false); + } + + /*** + * Verify truncate partitioned permissions for different users with different + * privileges + * @throws Exception + */ + @Test + public void testTruncatePartitionedTable() throws Exception { + File dataDir = context.getDataDir(); + // copy data file to test dir + File dataFile = new File(dataDir, MULTI_TYPE_DATA_FILE_NAME); + FileOutputStream to = new FileOutputStream(dataFile); + Resources.copy(Resources.getResource(MULTI_TYPE_DATA_FILE_NAME), to); + to.close(); + + policyFile.setUserGroupMapping(StaticUserGroup.getStaticMapping()); writePolicyFile(policyFile); - connection = context.createConnection(USER1_1); - statement = context.createStatement(connection); + // create partitioned tables + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); statement.execute("USE " + DB1); - // verify all on tab can truncate table - statement.execute("TRUNCATE TABLE " + TBL2); - assertFalse(hasData(statement, TBL2)); - statement.close(); - connection.close(); + statement.execute("DROP TABLE if exists " + TBL1); + statement.execute("CREATE TABLE " + TBL1 + " (i int) PARTITIONED BY (j int)"); + statement.execute("DROP TABLE if exists " + TBL2); + statement.execute("CREATE TABLE " + TBL2 + " (i int) PARTITIONED BY (j int)"); + statement.execute("DROP TABLE if exists " + TBL3); + statement.execute("CREATE TABLE " + TBL3 + " (i int) PARTITIONED BY (j int)"); - connection = context.createConnection(USER2_1); - statement = context.createStatement(connection); - statement.execute("USE " + DB1); - // verify drop on tab can truncate table - statement.execute("TRUNCATE TABLE " + TBL3); - assertFalse(hasData(statement, TBL3)); + // verify admin can execute truncate empty partitioned table + statement.execute("TRUNCATE TABLE " + TBL1); + assertFalse(hasData(statement, TBL1)); statement.close(); connection.close(); - connection = context.createConnection(USER3_1); + // add roles and grant permissions + updatePolicyFile(); + + // test truncate empty partitioned tables + truncateTableTests(false); + + // add partitions to tables + connection = context.createConnection(ADMIN1); statement = context.createStatement(connection); statement.execute("USE " + DB1); - // verify select on tab can NOT truncate table - context.assertAuthzException(statement, "TRUNCATE TABLE " + TBL3); + statement.execute("ALTER TABLE " + TBL1 + " ADD PARTITION (j=1) PARTITION (j=2)"); + statement.execute("ALTER TABLE " + TBL2 + " ADD PARTITION (j=1) PARTITION (j=2)"); + statement.execute("ALTER TABLE " + TBL3 + " ADD PARTITION (j=1) PARTITION (j=2)"); + + // verify admin can execute truncate NOT empty partitioned table + statement.execute("TRUNCATE TABLE " + TBL1 + " partition (j=1)"); + statement.execute("TRUNCATE TABLE " + TBL1); + assertFalse(hasData(statement, TBL1)); statement.close(); connection.close(); + + // test truncate NOT empty partitioned tables + truncateTableTests(true); } /** @@ -564,4 +590,73 @@ public class TestPrivilegesAtTableScope extends AbstractTestWithStaticConfigurat connection.close(); } + + /** + * update policy file for truncate table tests + */ + private void updatePolicyFile() throws Exception{ + policyFile + .addRolesToGroup(USERGROUP1, "all_tab1") + .addPermissionsToRole("all_tab1", + "server=server1->db=" + DB1 + "->table=" + TBL2) + .addRolesToGroup(USERGROUP2, "drop_tab1") + .addPermissionsToRole("drop_tab1", + "server=server1->db=" + DB1 + "->table=" + TBL3 + "->action=drop", + "server=server1->db=" + DB1 + "->table=" + TBL3 + "->action=select") + .addRolesToGroup(USERGROUP3, "select_tab1") + .addPermissionsToRole("select_tab1", + "server=server1->db=" + DB1 + "->table=" + TBL1 + "->action=select"); + writePolicyFile(policyFile); + } + + /** + * Test truncate table with or without partitions for users with different privileges. + * Only test truncate table partition if truncPartition is true. + */ + private void truncateTableTests(boolean truncPartition) throws Exception{ + Connection connection = null; + Statement statement = null; + try { + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + statement.execute("USE " + DB1); + // verify all privileges on table can truncate table + if (truncPartition) { + statement.execute("TRUNCATE TABLE " + TBL2 + " PARTITION (j=1)"); + } + statement.execute("TRUNCATE TABLE " + TBL2); + assertFalse(hasData(statement, TBL2)); + statement.close(); + connection.close(); + + connection = context.createConnection(USER2_1); + statement = context.createStatement(connection); + statement.execute("USE " + DB1); + // verify drop privilege on table can truncate table + if (truncPartition) { + statement.execute("TRUNCATE TABLE " + TBL3 + " partition (j=1)"); + } + statement.execute("TRUNCATE TABLE " + TBL3); + assertFalse(hasData(statement, TBL3)); + statement.close(); + connection.close(); + + connection = context.createConnection(USER3_1); + statement = context.createStatement(connection); + statement.execute("USE " + DB1); + // verify select privilege on table can NOT truncate table + if (truncPartition) { + context.assertAuthzException( + statement, "TRUNCATE TABLE " + TBL1 + " PARTITION (j=1)"); + } + context.assertAuthzException(statement, "TRUNCATE TABLE " + TBL1); + } finally { + if (statement != null) { + statement.close(); + } + if (connection != null) { + connection.close(); + } + } + } }