You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sentry.apache.org by sr...@apache.org on 2015/09/15 02:16:34 UTC

incubator-sentry git commit: SENTRY-881: Allow some metadata operations with column-level privileges ( Lenni Kuff, Reviewed by: Sravya Tirukkovalur)

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 <sr...@cloudera.com>
Authored: Mon Sep 14 17:15:56 2015 -0700
Committer: Sravya Tirukkovalur <sr...@cloudera.com>
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);