SENTRY-2540: Only use SELECT action for filter SHOW DATABASES and SHOW TABLES command based on configuration (Na Li, reviewed by Kalyan Kumar Kalvagadda)
diff --git a/sentry-binding/sentry-binding-hive-conf/src/main/java/org/apache/sentry/binding/hive/conf/HiveAuthzConf.java b/sentry-binding/sentry-binding-hive-conf/src/main/java/org/apache/sentry/binding/hive/conf/HiveAuthzConf.java
index d555027..95e6ef1 100644
--- a/sentry-binding/sentry-binding-hive-conf/src/main/java/org/apache/sentry/binding/hive/conf/HiveAuthzConf.java
+++ b/sentry-binding/sentry-binding-hive-conf/src/main/java/org/apache/sentry/binding/hive/conf/HiveAuthzConf.java
@@ -103,6 +103,8 @@
                 "org.apache.sentry.binding.hive.SentryIniPolicyFileFormatter"),
         AUTHZ_SERVER_NAME("sentry.hive.server", SENTRY_HIVE_SERVER_DEFAULT),
         AUTHZ_RESTRICT_DEFAULT_DB("sentry.hive.restrict.defaultDB", "false"),
+        SHOWDATABASES_ON_SELECT_ONLY("sentry.showdatabases.select.only", "false"),
+        SHOWTABLES_ON_SELECT_ONLY("sentry.showtables.select.only", "false"),
         SENTRY_TESTING_MODE("sentry.hive.testing.mode", "false"),
         AUTHZ_ALLOW_HIVE_IMPERSONATION("sentry.hive.allow.hive.impersonation", "false"),
         AUTHZ_ONFAILURE_HOOKS("sentry.hive.failure.hooks", ""),
diff --git a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/MetastoreAuthzObjectFilter.java b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/MetastoreAuthzObjectFilter.java
index e64d1a5..9f323f4 100644
--- a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/MetastoreAuthzObjectFilter.java
+++ b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/MetastoreAuthzObjectFilter.java
@@ -30,6 +30,7 @@
 import org.apache.sentry.binding.hive.authz.HiveAuthzPrivileges.HiveOperationScope;
 import org.apache.sentry.binding.hive.authz.HiveAuthzPrivileges.HiveOperationType;
 import org.apache.sentry.binding.hive.conf.HiveAuthzConf;
+import org.apache.sentry.binding.hive.conf.HiveAuthzConf.AuthzConfVars;
 import org.apache.sentry.core.common.Subject;
 import org.apache.sentry.core.model.db.Column;
 import org.apache.sentry.core.model.db.DBModelAction;
@@ -67,6 +68,15 @@
     .setOperationType(HiveOperationType.QUERY)
     .build();
 
+  private static final HiveAuthzPrivileges LIST_DATABASES_PRIVILEGES_ON_SELECT = new HiveAuthzPrivileges.AuthzPrivilegeBuilder()
+      .addInputObjectPriviledge(
+          AuthorizableType.Column,
+          EnumSet.of(DBModelAction.SELECT))
+      .addInputObjectPriviledge(AuthorizableType.URI, EnumSet.of(DBModelAction.SELECT))
+      .setOperationScope(HiveOperationScope.CONNECT)
+      .setOperationType(HiveOperationType.QUERY)
+      .build();
+
   private static final HiveAuthzPrivileges LIST_TABLES_PRIVILEGES = new HiveAuthzPrivileges.AuthzPrivilegeBuilder()
     .addInputObjectPriviledge(
       AuthorizableType.Column,
@@ -78,7 +88,18 @@
       HiveAuthzPrivileges.HiveOperationType.INFO)
     .build();
 
+  private static final HiveAuthzPrivileges LIST_TABLES_PRIVILEGES_ON_SELECT = new HiveAuthzPrivileges.AuthzPrivilegeBuilder()
+      .addInputObjectPriviledge(
+          AuthorizableType.Column,
+          EnumSet.of(DBModelAction.SELECT))
+      .setOperationScope(HiveOperationScope.TABLE)
+      .setOperationType(
+          HiveAuthzPrivileges.HiveOperationType.INFO)
+      .build();
+
   private final boolean DEFAULT_DATABASE_RESTRICTED;
+  private final boolean SHOWDATABASES_ON_SELECT_ONLY;
+  private final boolean SHOWTABLES_ON_SELECT_ONLY;
   private final DBModelAuthorizable AUTH_SERVER;
   private HiveAuthzBinding authzBinding;
   private ObjectExtractor extractor;
@@ -89,6 +110,12 @@
     this.AUTH_SERVER = authzBinding.getAuthServer();
     this.DEFAULT_DATABASE_RESTRICTED = authzBinding.getAuthzConf()
       .getBoolean(HiveAuthzConf.AuthzConfVars.AUTHZ_RESTRICT_DEFAULT_DB.getVar(), false);
+
+    this.SHOWDATABASES_ON_SELECT_ONLY = authzBinding.getAuthzConf()
+        .getBoolean(HiveAuthzConf.AuthzConfVars.SHOWDATABASES_ON_SELECT_ONLY.getVar(), false);
+
+    this.SHOWTABLES_ON_SELECT_ONLY = authzBinding.getAuthzConf()
+        .getBoolean(AuthzConfVars.SHOWTABLES_ON_SELECT_ONLY.getVar(), false);
   }
 
   /**
@@ -103,7 +130,11 @@
    * Return the required privileges for listing tables in a database
    * @return the required privileges for authorizing listing tables in a database
    */
-  public static HiveAuthzPrivileges getListTablePrivileges() {
+  public HiveAuthzPrivileges getListTablePrivileges() {
+    if (SHOWTABLES_ON_SELECT_ONLY) {
+      return LIST_TABLES_PRIVILEGES_ON_SELECT;
+    }
+
     return LIST_TABLES_PRIVILEGES;
   }
 
@@ -170,7 +201,14 @@
       AUTH_SERVER, database, Table.ALL, Column.ALL
     );
 
-    return authorize(HiveOperation.SHOWDATABASES, LIST_DATABASES_PRIVILEGES, username, authorizable);
+    if (SHOWDATABASES_ON_SELECT_ONLY) {
+      return authorize(HiveOperation.SHOWDATABASES, LIST_DATABASES_PRIVILEGES_ON_SELECT, username,
+          authorizable);
+
+    } else {
+      return authorize(HiveOperation.SHOWDATABASES, LIST_DATABASES_PRIVILEGES, username,
+          authorizable);
+    }
   }
 
   /**
@@ -185,7 +223,11 @@
       AUTH_SERVER, database, table, Column.ALL
     );
 
-    return authorize(HiveOperation.SHOWTABLES, LIST_TABLES_PRIVILEGES, username, authorizable);
+    if (SHOWTABLES_ON_SELECT_ONLY) {
+      return authorize(HiveOperation.SHOWTABLES, LIST_TABLES_PRIVILEGES_ON_SELECT, username, authorizable);
+    } else {
+      return authorize(HiveOperation.SHOWTABLES, LIST_TABLES_PRIVILEGES, username, authorizable);
+    }
   }
 
   /**
diff --git a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/AbstractTestWithStaticConfiguration.java b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/AbstractTestWithStaticConfiguration.java
index 8690a35..86e7d14 100644
--- a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/AbstractTestWithStaticConfiguration.java
+++ b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/AbstractTestWithStaticConfiguration.java
@@ -35,6 +35,7 @@
 
 import com.google.common.collect.Sets;
 import org.apache.hive.hcatalog.listener.DbNotificationListener;
+import org.apache.sentry.binding.hive.conf.HiveAuthzConf.AuthzConfVars;
 import org.apache.sentry.binding.metastore.messaging.json.SentryJSONMessageFactory;
 import org.apache.sentry.api.common.ApiConstants.ClientConfig;
 import org.apache.sentry.tests.e2e.hive.fs.TestFSContants;
@@ -143,6 +144,9 @@
   protected static int hmsFollowerIntervalInMilliseconds = 10000;
   // indicate if the database need to be clear for every test case in one test class
   protected static boolean clearDbPerTest = true;
+  protected static boolean showDbOnSelectOnly = false;
+  protected static boolean showTableOnSelectOnly = false;
+  protected static boolean restrictDefaultDatabase = false;
 
   protected static File baseDir;
   protected static File logDir;
@@ -312,6 +316,11 @@
       properties.put(HiveConf.ConfVars.HIVE_LOCK_MANAGER.varname,
           "org.apache.hadoop.hive.ql.lockmgr.EmbeddedLockManager");
     }
+
+    properties.put(AuthzConfVars.SHOWDATABASES_ON_SELECT_ONLY.getVar(), String.valueOf(showDbOnSelectOnly));
+    properties.put(AuthzConfVars.SHOWTABLES_ON_SELECT_ONLY.getVar(), String.valueOf(showTableOnSelectOnly));
+    properties.put(AuthzConfVars.AUTHZ_RESTRICT_DEFAULT_DB.getVar(), String.valueOf(restrictDefaultDatabase));
+
     if (useSentryService && (!startSentry)) {
       configureHiveAndMetastoreForSentry();
       setupSentryService();
diff --git a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestShowMetadataPrivileges.java b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestShowMetadataPrivileges.java
index 6a88d0b..0837c7f 100644
--- a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestShowMetadataPrivileges.java
+++ b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestShowMetadataPrivileges.java
@@ -34,15 +34,15 @@
 
 @RunWith(Parameterized.class)
 public class TestShowMetadataPrivileges extends AbstractTestWithStaticConfiguration {
-  private static final boolean ALLOWED = true;
-  private static final boolean NOT_ALLOWED = false;
-  private static final String SERVER1 = "server1";
+  protected static final boolean ALLOWED = true;
+  protected static final boolean NOT_ALLOWED = false;
+  protected static final String SERVER1 = "server1";
 
-  private static Connection adminCon, user1Con;
-  private static Statement adminStmt, user1Stmt;
+  protected static Connection adminCon, user1Con;
+  protected static Statement adminStmt, user1Stmt;
 
-  private DBModelAction action;
-  private boolean allowed;
+  protected DBModelAction action;
+  protected boolean allowed;
 
   @Parameterized.Parameters
   public static Collection describePrivileges() {
@@ -62,6 +62,7 @@
   @BeforeClass
   public static void setupTestStaticConfiguration() throws Exception{
     useSentryService = true;
+    restrictDefaultDatabase = true;
     AbstractTestWithStaticConfiguration.setupTestStaticConfiguration();
     setupAdmin();
 
@@ -151,4 +152,40 @@
         user1Stmt.getResultSet().next());
     }
   }
+
+  @Test
+  public void testShowDatabasesWithGrantOnDatabase() throws Exception {
+    if (action != null) {
+      adminStmt.execute("GRANT " + action + " ON DATABASE " + DB1 + " TO ROLE role1");
+    }
+
+    user1Stmt.execute("SHOW DATABASES");
+    if (!allowed) {
+      assertFalse(
+          "SHOW DATABASES should NOT display databases with " + action + " privileges on the database.",
+          user1Stmt.getResultSet().next());
+    } else {
+      assertTrue(
+          "SHOW DATABASES should display databases with " + action + " privileges on the database.",
+          user1Stmt.getResultSet().next());
+    }
+  }
+
+  @Test
+  public void testShowDatabasesWithGrantOnServer() throws Exception {
+    if (action != null) {
+      adminStmt.execute("GRANT " + action + " ON SERVER " + SERVER1 + " TO ROLE role1");
+    }
+
+    user1Stmt.execute("SHOW DATABASES");
+    if (!allowed) {
+      assertFalse(
+          "SHOW DATABASES should NOT display databases with " + action + " privileges on the server.",
+          user1Stmt.getResultSet().next());
+    } else {
+      assertTrue(
+          "SHOW DATABASES should display databases with " + action + " privileges on the server.",
+          user1Stmt.getResultSet().next());
+    }
+  }
 }
diff --git a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestShowMetadataPrivilegesOnSelectOnly.java b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestShowMetadataPrivilegesOnSelectOnly.java
new file mode 100644
index 0000000..b89e941
--- /dev/null
+++ b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/hive/TestShowMetadataPrivilegesOnSelectOnly.java
@@ -0,0 +1,54 @@
+/*
+ * 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.tests.e2e.hive;
+
+
+import java.util.Arrays;
+import java.util.Collection;
+import org.apache.sentry.core.model.db.DBModelAction;
+import org.junit.BeforeClass;
+import org.junit.runners.Parameterized;
+
+public class TestShowMetadataPrivilegesOnSelectOnly extends TestShowMetadataPrivileges {
+
+    @Parameterized.Parameters
+    public static Collection describePrivilegesOnSelect() {
+        return Arrays.asList(new Object[][] {
+            { null,                  NOT_ALLOWED }, // Means no privileges
+            { DBModelAction.ALL,     ALLOWED },
+            { DBModelAction.CREATE,  NOT_ALLOWED },
+            { DBModelAction.SELECT,  ALLOWED },
+            { DBModelAction.INSERT,  NOT_ALLOWED },
+            { DBModelAction.ALTER,   NOT_ALLOWED },
+            { DBModelAction.DROP,    NOT_ALLOWED },
+            { DBModelAction.INDEX,   NOT_ALLOWED },
+            { DBModelAction.LOCK,    NOT_ALLOWED },
+        });
+    }
+
+    @BeforeClass
+    public static void setupSelectOnlyTestStaticConfiguration() throws Exception {
+        showDbOnSelectOnly = true;
+        showTableOnSelectOnly = true;
+        setupTestStaticConfiguration();
+    }
+
+    public TestShowMetadataPrivilegesOnSelectOnly(DBModelAction action, boolean allowed) {
+        super(action, allowed);
+    }
+
+}