diff --git a/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java b/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java index e2bd38b4e1..13c203ce0a 100644 --- a/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java +++ b/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java @@ -794,6 +794,9 @@ private static void populateLlapDaemonVarsSet(Set llapDaemonVarsSetLocal METASTORE_EVENT_DB_LISTENER_TTL("hive.metastore.event.db.listener.timetolive", "86400s", new TimeValidator(TimeUnit.SECONDS), "time after which events will be removed from the database listener queue"), + METASTORE_EVENT_DB_NOTIFICATION_API_AUTH("hive.metastore.event.db.notification.api.auth", false, + "Should metastore do authroization against db notification related APIs such as get_next_notification.\n" + + "If set to true, then only the superusers in proxy settings of core-site.xml have the permission"), METASTORE_AUTHORIZATION_STORAGE_AUTH_CHECKS("hive.metastore.authorization.storage.checks", false, "Should the metastore do authorization checks against the underlying storage (usually hdfs) \n" + "for operations like drop-partition (disallow the drop-partition if the user in\n" + diff --git a/itests/hcatalog-unit/src/test/java/org/apache/hive/hcatalog/listener/TestDbNotificationListener.java b/itests/hcatalog-unit/src/test/java/org/apache/hive/hcatalog/listener/TestDbNotificationListener.java index c36b632fd1..1c40674bc4 100644 --- a/itests/hcatalog-unit/src/test/java/org/apache/hive/hcatalog/listener/TestDbNotificationListener.java +++ b/itests/hcatalog-unit/src/test/java/org/apache/hive/hcatalog/listener/TestDbNotificationListener.java @@ -98,6 +98,7 @@ import org.apache.hadoop.hive.ql.session.SessionState; import org.apache.hive.hcatalog.api.repl.ReplicationV1CompatRule; import org.apache.hive.hcatalog.data.Pair; +import org.apache.thrift.TException; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; @@ -117,6 +118,7 @@ private static final int EVENTS_TTL = 30; private static final int CLEANUP_SLEEP_TIME = 10; private static Map emptyParameters = new HashMap(); + private static HiveConf conf; private static IMetaStoreClient msClient; private static Driver driver; private static MessageDeserializer md = null; @@ -232,7 +234,7 @@ public void onInsert(InsertEvent insertEvent) throws MetaException { @SuppressWarnings("rawtypes") @BeforeClass public static void connectToMetastore() throws Exception { - HiveConf conf = new HiveConf(); + conf = new HiveConf(); conf.setVar(HiveConf.ConfVars.METASTORE_TRANSACTIONAL_EVENT_LISTENERS, DbNotificationListener.class.getName()); conf.setVar(HiveConf.ConfVars.METASTORE_EVENT_LISTENERS, MockMetaStoreEventListener.class.getName()); @@ -1575,4 +1577,23 @@ public void cleanupNotifs() throws Exception { LOG.info("second trigger done"); assertEquals(0, rsp2.getEventsSize()); } + + @Test(expected = TException.class) + public void testAuthForNotificationAPIs() throws Exception { + String dbName = "sqldb"; + driver.run("create database " + dbName); + msClient.close(); + conf.setBoolVar(HiveConf.ConfVars.METASTORE_EVENT_DB_NOTIFICATION_API_AUTH, true); + msClient = new HiveMetaStoreClient(conf); + try { + msClient.getNextNotification(firstEventId, 0, null); + } catch (Exception ex) + { + throw ex; + } finally { + msClient.close(); + conf.setBoolVar(HiveConf.ConfVars.METASTORE_EVENT_DB_NOTIFICATION_API_AUTH, false); + msClient = new HiveMetaStoreClient(conf); + } + } } diff --git a/metastore/src/java/org/apache/hadoop/hive/metastore/HiveMetaStore.java b/metastore/src/java/org/apache/hadoop/hive/metastore/HiveMetaStore.java index d5de4f2c7b..877d5fcb2d 100644 --- a/metastore/src/java/org/apache/hadoop/hive/metastore/HiveMetaStore.java +++ b/metastore/src/java/org/apache/hadoop/hive/metastore/HiveMetaStore.java @@ -7055,12 +7055,26 @@ private Table getTable(String dbName, String tableName) @Override public NotificationEventResponse get_next_notification(NotificationEventRequest rqst) throws TException { + try { + authorize(); + } catch (Exception ex) { + LOG.error("User is not authorized to make the get_next_notification call", ex); + throw new TException(ex); + } + RawStore ms = getMS(); return ms.getNextNotification(rqst); } @Override public CurrentNotificationEventId get_current_notificationEventId() throws TException { + try { + authorize(); + } catch (Exception ex) { + LOG.error("User is not authorized to make the get_current_notificationEventId call", ex); + throw new TException(ex); + } + RawStore ms = getMS(); return ms.getCurrentNotificationEventId(); } @@ -7068,10 +7082,35 @@ public CurrentNotificationEventId get_current_notificationEventId() throws TExce @Override public NotificationEventsCountResponse get_notification_events_count(NotificationEventsCountRequest rqst) throws TException { + try { + authorize(); + } catch (Exception ex) { + LOG.error("User is not authorized to make the get_notification_events_count call", ex); + throw new TException(ex); + } + RawStore ms = getMS(); return ms.getNotificationEventsCount(rqst); } + private void authorize() throws Exception + { + if (!hiveConf.getBoolVar(HiveConf.ConfVars.METASTORE_EVENT_DB_NOTIFICATION_API_AUTH)) + { + return; + } + try { + String user = Utils.getUGI().getShortUserName(); + if (!MetaStoreUtils.hasPermissionForDbNotificationCalls(user, hiveConf)) + { + throw new MetaException("User " + user + "is not allowed to perform this API call"); + } + } catch (Exception ex) { + LOG.error("Cannot obtain username", ex); + throw ex; + } + } + @Override public FireEventResponse fire_listener_event(FireEventRequest rqst) throws TException { switch (rqst.getData().getSetField()) { diff --git a/metastore/src/java/org/apache/hadoop/hive/metastore/MetaStoreUtils.java b/metastore/src/java/org/apache/hadoop/hive/metastore/MetaStoreUtils.java index b51446d5c6..108ec8eaac 100644 --- a/metastore/src/java/org/apache/hadoop/hive/metastore/MetaStoreUtils.java +++ b/metastore/src/java/org/apache/hadoop/hive/metastore/MetaStoreUtils.java @@ -63,6 +63,7 @@ import org.apache.hadoop.hive.metastore.api.Order; import org.apache.hadoop.hive.metastore.api.SkewedInfo; import org.apache.hadoop.hive.shims.ShimLoader; +import org.apache.hadoop.hive.shims.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.hadoop.conf.Configuration; @@ -1974,4 +1975,19 @@ public ColumnStatisticsObj call() throws Exception { public static double decimalToDouble(Decimal decimal) { return new BigDecimal(new BigInteger(decimal.getUnscaled()), decimal.getScale()).doubleValue(); } + + private static final String PROXYUSER_SETTING_PREFIX = "hadoop.proxyuser."; + private static final String PROXYUSER_SETTING_SUFFIX = ".hosts"; + + /** + * Verify if the user is allowed to make DB notification related calls. + * Only the superusers defined in the Hadoop proxy user settings have the permission. + * + * @param user the short user name + * @param config that contains the proxy user settings + * @return if the user has the permission + */ + public static boolean hasPermissionForDbNotificationCalls(String user, Configuration conf) { + return conf.get(PROXYUSER_SETTING_PREFIX + user + PROXYUSER_SETTING_SUFFIX) != null; + } }