Repository: incubator-sentry Updated Branches: refs/heads/master 6203a7a65 -> c9e47c8ca
SENTRY-881: Allow some metadata operations with column-level privileges ( Lenni Kuff, Reviewed by: Sravya Tirukkovalur) Project: http://git-wip-us.apache.org/repos/asf/incubator-sentry/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-sentry/commit/c9e47c8c Tree: http://git-wip-us.apache.org/repos/asf/incubator-sentry/tree/c9e47c8c Diff: http://git-wip-us.apache.org/repos/asf/incubator-sentry/diff/c9e47c8c Branch: refs/heads/master Commit: c9e47c8cadce0cc6300a46d02fe2224eec6cc882 Parents: 6203a7a Author: Sravya Tirukkovalur <[email protected]> Authored: Mon Sep 14 17:15:56 2015 -0700 Committer: Sravya Tirukkovalur <[email protected]> Committed: Mon Sep 14 17:15:56 2015 -0700 ---------------------------------------------------------------------- .../binding/hive/HiveAuthzBindingHook.java | 28 +++++++- .../hive/authz/HiveAuthzPrivilegesMap.java | 5 +- .../e2e/dbprovider/TestColumnEndToEnd.java | 68 +++++++++++++++++++- .../e2e/hive/TestMetadataObjectRetrieval.java | 26 +++++++- 4 files changed, 118 insertions(+), 9 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/c9e47c8c/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 fd801a4..18b8a8f 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 @@ -70,6 +70,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; @@ -85,6 +86,13 @@ public class HiveAuthzBindingHook extends AbstractSemanticAnalyzerHook { private Table currOutTab = null; private Database currOutDB = null; + // True if this is a basic DESCRIBE <table> operation. False for other DESCRIBE variants + // like DESCRIBE [FORMATTED|EXTENDED]. Required because Hive treats these stmts as the same + // HiveOperationType, but we want to enforces different privileges on each statement. + // Basic DESCRIBE <table> is allowed with only column-level privs, while the variants + // require table-level privileges. + public boolean isDescTableBasic = false; + public HiveAuthzBindingHook() throws Exception { SessionState session = SessionState.get(); if(session == null) { @@ -247,6 +255,12 @@ public class HiveAuthzBindingHook extends AbstractSemanticAnalyzerHook { String dbName = BaseSemanticAnalyzer.unescapeIdentifier(ast.getChild(1).getChild(0).getChild(0).getText()); currDB = new Database(dbName); break; + case HiveParser.TOK_DESCTABLE: + currDB = getCanonicalDb(); + // For DESCRIBE FORMATTED/EXTENDED ast will have an additional child node with value + // "FORMATTED/EXTENDED". + isDescTableBasic = (ast.getChildCount() == 1); + break; default: currDB = getCanonicalDb(); break; @@ -434,6 +448,14 @@ public class HiveAuthzBindingHook extends AbstractSemanticAnalyzerHook { LOG.debug("context.getOutputs() = " + context.getOutputs()); } + // Workaround to allow DESCRIBE <table> to be executed with only column-level privileges, while + // still authorizing DESCRIBE [EXTENDED|FORMATTED] as table-level. + // This is done by treating DESCRIBE <table> the same as SHOW COLUMNS, which only requires column + // level privs. + if (isDescTableBasic) { + stmtAuthObject = HiveAuthzPrivilegesMap.getHiveAuthzPrivileges(HiveOperation.SHOWCOLUMNS); + } + switch (stmtAuthObject.getOperationScope()) { case SERVER : @@ -478,6 +500,8 @@ public class HiveAuthzBindingHook extends AbstractSemanticAnalyzerHook { inputHierarchy.add(externalAuthorizableHierarchy); } + + // workaround for DDL statements // Capture the table name in pre-analyze and include that in the output entity list if (currOutTab != null) { @@ -735,7 +759,7 @@ public class HiveAuthzBindingHook extends AbstractSemanticAnalyzerHook { throws SemanticException { List<FieldSchema> filteredResult = new ArrayList<FieldSchema>(); Subject subject = new Subject(userName); - HiveAuthzPrivileges ColumnMetaDataPrivilege = + HiveAuthzPrivileges columnMetaDataPrivilege = HiveAuthzPrivilegesMap.getHiveAuthzPrivileges(HiveOperation.SHOWCOLUMNS); Database database = new Database(dbName); @@ -752,7 +776,7 @@ public class HiveAuthzBindingHook extends AbstractSemanticAnalyzerHook { inputHierarchy.add(externalAuthorizableHierarchy); try { - hiveAuthzBinding.authorize(operation, ColumnMetaDataPrivilege, subject, + hiveAuthzBinding.authorize(operation, columnMetaDataPrivilege, subject, inputHierarchy, outputHierarchy); filteredResult.add(col); } catch (AuthorizationException e) { http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/c9e47c8c/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzPrivilegesMap.java ---------------------------------------------------------------------- diff --git a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzPrivilegesMap.java b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzPrivilegesMap.java index e721555..d35b09d 100644 --- a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzPrivilegesMap.java +++ b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzPrivilegesMap.java @@ -140,7 +140,8 @@ public class HiveAuthzPrivilegesMap { setOperationType(HiveOperationType.INFO). build(); - HiveAuthzPrivileges ColumnMetaDataPrivilege = new HiveAuthzPrivileges.AuthzPrivilegeBuilder(). + // Metadata statements which only require column-level privileges. + HiveAuthzPrivileges columnMetaDataPrivilege = new HiveAuthzPrivileges.AuthzPrivilegeBuilder(). addInputObjectPriviledge(AuthorizableType.Column, EnumSet.of(DBModelAction.SELECT, DBModelAction.INSERT)). setOperationScope(HiveOperationScope.COLUMN). setOperationType(HiveOperationType.INFO). @@ -262,7 +263,7 @@ public class HiveAuthzPrivilegesMap { hiveAuthzStmtPrivMap.put(HiveOperation.DROPFUNCTION, functionPrivilege); // SHOWCOLUMNS - hiveAuthzStmtPrivMap.put(HiveOperation.SHOWCOLUMNS, ColumnMetaDataPrivilege); + hiveAuthzStmtPrivMap.put(HiveOperation.SHOWCOLUMNS, columnMetaDataPrivilege); // SHOWDATABASES // SHOWTABLES http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/c9e47c8c/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/dbprovider/TestColumnEndToEnd.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/dbprovider/TestColumnEndToEnd.java b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/dbprovider/TestColumnEndToEnd.java index 718a736..343048d 100644 --- a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/dbprovider/TestColumnEndToEnd.java +++ b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/dbprovider/TestColumnEndToEnd.java @@ -17,8 +17,7 @@ package org.apache.sentry.tests.e2e.dbprovider; -import static junit.framework.Assert.fail; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.io.File; import java.io.FileOutputStream; @@ -32,12 +31,12 @@ import java.util.List; import org.apache.sentry.provider.db.SentryAccessDeniedException; import org.apache.sentry.provider.file.PolicyFile; import org.apache.sentry.tests.e2e.hive.AbstractTestWithStaticConfiguration; -import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import com.google.common.io.Resources; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -91,6 +90,68 @@ public class TestColumnEndToEnd extends AbstractTestWithStaticConfiguration { } @Test + public void testDescribeTbl() throws Exception { + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + statement.execute("CREATE TABLE IF NOT EXISTS t1 (c1 string, c2 string)"); + statement.execute("CREATE TABLE t2 (c1 string, c2 string)"); + statement.execute("CREATE ROLE user_role1"); + statement.execute("GRANT SELECT (c1) ON TABLE t1 TO ROLE user_role1"); + statement.execute("GRANT ROLE user_role1 TO GROUP " + USERGROUP1); + statement.close(); + connection.close(); + + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + + // Expect that DESCRIBE table works with only column-level privileges, but other + // DESCRIBE variants like DESCRIBE FORMATTED fail. Note that if a user has privileges + // on any column they can describe all columns. + ResultSet rs = statement.executeQuery("DESCRIBE t1"); + assertTrue(rs.next()); + assertEquals("c1", rs.getString(1)); + assertEquals("string", rs.getString(2)); + assertTrue(rs.next()); + assertEquals("c2", rs.getString(1)); + assertEquals("string", rs.getString(2)); + + statement.executeQuery("DESCRIBE t1 c1"); + statement.executeQuery("DESCRIBE t1 c2"); + + try { + statement.executeQuery("DESCRIBE t2"); + fail("Expected DESCRIBE to fail on t2"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + try { + statement.executeQuery("DESCRIBE FORMATTED t1"); + fail("Expected DESCRIBE FORMATTED to fail"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + try { + statement.executeQuery("DESCRIBE EXTENDED t1"); + fail("Expected DESCRIBE EXTENDED to fail"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + statement.close(); + connection.close(); + + // Cleanup + connection = context.createConnection(ADMIN1); + statement = context.createStatement(connection); + statement.execute("DROP TABLE t1"); + statement.execute("DROP TABLE t2"); + statement.execute("DROP ROLE user_role1"); + statement.close(); + connection.close(); + } + + @Test public void testNegative() throws Exception { Connection connection = context.createConnection(ADMIN1); Statement statement = context.createStatement(connection); @@ -205,6 +266,7 @@ public class TestColumnEndToEnd extends AbstractTestWithStaticConfiguration { statement = context.createStatement(connection); statement.execute("use " + DB1); statement.execute("SELECT c1 FROM t1"); + statement.execute("DESCRIBE t1"); // 2.1 user_role2 select c1,c2 on t1 connection = context.createConnection(USER2_1); http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/c9e47c8c/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestMetadataObjectRetrieval.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestMetadataObjectRetrieval.java b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestMetadataObjectRetrieval.java index 7dd0f01..f824cc5 100644 --- a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestMetadataObjectRetrieval.java +++ b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestMetadataObjectRetrieval.java @@ -66,7 +66,7 @@ public class TestMetadataObjectRetrieval extends AbstractTestWithStaticConfigura * show create table table * show tblproperties table * - * The table is assumed to have two colums under_col int and value string. + * The table is assumed to have two columns under_col int and value string. */ private void positiveDescribeShowTests(String user, String db, String table) throws Exception { Connection connection = context.createConnection(user); @@ -91,6 +91,27 @@ public class TestMetadataObjectRetrieval extends AbstractTestWithStaticConfigura assertTrue("describe table fail", rs.getString(1).trim().equals("value")); assertTrue("describe table fail", rs.getString(2).trim().equals("string")); + rs = statement.executeQuery("DESCRIBE EXTENDED " + table); + assertTrue(rs.next()); + assertTrue(rs.getString(1), rs.getString(1).contains("under_col")); + assertTrue(rs.getString(2), rs.getString(2).contains("int")); + assertTrue(rs.next()); + assertTrue(rs.getString(1), rs.getString(1).contains("value")); + assertTrue(rs.getString(2), rs.getString(2).contains("string")); + assertTrue(rs.next()); + + rs = statement.executeQuery("DESCRIBE FORMATTED " + table); + // Skip the header + assertTrue(rs.next()); + assertTrue(rs.next()); + assertTrue(rs.next()); + assertTrue(rs.getString(1), rs.getString(1).contains("under_col")); + assertTrue(rs.getString(2), rs.getString(2).contains("int")); + assertTrue(rs.next()); + assertTrue(rs.getString(1), rs.getString(1).contains("value")); + assertTrue(rs.getString(2), rs.getString(2).contains("string")); + assertTrue(rs.next()); + rs = statement.executeQuery("SHOW COLUMNS FROM " + table); assertTrue(rs.next()); assertTrue("show columns from fail", rs.getString(1).trim().equals("under_col")); @@ -120,9 +141,10 @@ public class TestMetadataObjectRetrieval extends AbstractTestWithStaticConfigura Connection connection = context.createConnection(user); Statement statement = context.createStatement(connection); statement.execute("USE " + db); - context.assertAuthzException(statement, "DESCRIBE " + table); context.assertAuthzException(statement, "DESCRIBE " + table + " under_col"); context.assertAuthzException(statement, "DESCRIBE " + table + " value"); + context.assertAuthzException(statement, "DESCRIBE FORMATTED " + table); + context.assertAuthzException(statement, "DESCRIBE EXTENDED " + table); context.assertAuthzException(statement, "SHOW COLUMNS FROM " + table); context.assertAuthzException(statement, "SHOW CREATE TABLE " + table); context.assertAuthzException(statement, "SHOW TBLPROPERTIES " + table);
