Repository: sentry
Updated Branches:
  refs/heads/master f330be532 -> 4d9bc9cea


SENTRY-2274: Grant and revoke owner privileges based on HMS 
updates(server-side) (Kalyan Kumar Kalvagadda reviewed by Sergio Pena)


Project: http://git-wip-us.apache.org/repos/asf/sentry/repo
Commit: http://git-wip-us.apache.org/repos/asf/sentry/commit/4d9bc9ce
Tree: http://git-wip-us.apache.org/repos/asf/sentry/tree/4d9bc9ce
Diff: http://git-wip-us.apache.org/repos/asf/sentry/diff/4d9bc9ce

Branch: refs/heads/master
Commit: 4d9bc9cea94f13b7994817d33c016c776fa7dcda
Parents: f330be5
Author: Kalyan Kumar Kalvagadda <kkal...@cloudera.com>
Authored: Thu Jun 21 15:59:05 2018 -0500
Committer: Kalyan Kumar Kalvagadda <kkal...@cloudera.com>
Committed: Thu Jun 21 15:59:05 2018 -0500

----------------------------------------------------------------------
 .../java/org/apache/sentry/SentryOwnerInfo.java |  84 +++++++
 .../org/apache/sentry/hdfs/SentryPlugin.java    |  40 ++--
 .../api/service/thrift/SentryMetrics.java       |   2 +
 .../thrift/SentryPolicyStoreProcessor.java      | 227 ++++++++++++++++++-
 .../provider/db/SentryPolicyStorePlugin.java    |  22 +-
 .../db/service/persistent/CounterWait.java      |   4 +-
 .../db/service/persistent/SentryStore.java      | 208 ++++++++++++++++-
 .../thrift/TestSentryPolicyStoreProcessor.java  | 125 +++++++++-
 .../db/service/persistent/TestSentryStore.java  | 204 +++++++++++++++++
 9 files changed, 869 insertions(+), 47 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/sentry/blob/4d9bc9ce/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/SentryOwnerInfo.java
----------------------------------------------------------------------
diff --git 
a/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/SentryOwnerInfo.java
 
b/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/SentryOwnerInfo.java
new file mode 100644
index 0000000..ee4fce5
--- /dev/null
+++ 
b/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/SentryOwnerInfo.java
@@ -0,0 +1,84 @@
+/**
+ * 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;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.sentry.service.common.ServiceConstants.SentryEntityType;
+
+/**
+ * This class holds the owner name and the Type
+ */
+public class SentryOwnerInfo {
+  private String ownerName;
+  private SentryEntityType ownerType;
+
+  public SentryOwnerInfo (SentryEntityType type, String name) {
+    ownerType = type;
+    ownerName = name;
+  }
+
+  public void setOwnerName(String name) {
+    ownerName = name;
+  }
+
+  public String getOwnerName() {
+    return ownerName;
+  }
+
+  public SentryEntityType getOwnerType() {
+    return ownerType;
+  }
+
+  public void setOwnerType(SentryEntityType type) {
+    ownerType = type;
+  }
+
+  @Override
+  public String toString() {
+    return "Owner Type: " + ownerType.toString() + ", Owner Name: " + 
ownerName;
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + ((ownerName == null) ? 0 : ownerName.hashCode());
+    result = prime * result + ownerType.hashCode();
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null) {
+      return false;
+    }
+    if (getClass() != obj.getClass()) {
+      return false;
+    }
+    SentryOwnerInfo other = (SentryOwnerInfo) obj;
+
+    if(ownerType != other.getOwnerType()) {
+      return false;
+    }
+    return StringUtils.equals(ownerName, other.ownerName);
+  }
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/4d9bc9ce/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/SentryPlugin.java
----------------------------------------------------------------------
diff --git 
a/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/SentryPlugin.java
 
b/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/SentryPlugin.java
index b5e01e4..6ff3c82 100644
--- 
a/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/SentryPlugin.java
+++ 
b/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/SentryPlugin.java
@@ -38,8 +38,6 @@ import 
org.apache.sentry.provider.db.service.persistent.SentryStore;
 import org.apache.sentry.api.common.SentryServiceUtil;
 import org.apache.sentry.api.service.thrift.TAlterSentryRoleAddGroupsRequest;
 import 
org.apache.sentry.api.service.thrift.TAlterSentryRoleDeleteGroupsRequest;
-import 
org.apache.sentry.api.service.thrift.TAlterSentryRoleGrantPrivilegeRequest;
-import 
org.apache.sentry.api.service.thrift.TAlterSentryRoleRevokePrivilegeRequest;
 import org.apache.sentry.api.service.thrift.TDropPrivilegesRequest;
 import org.apache.sentry.api.service.thrift.TDropSentryRoleRequest;
 import org.apache.sentry.api.service.thrift.TRenamePrivilegesRequest;
@@ -251,17 +249,18 @@ public class SentryPlugin implements 
SentryPolicyStorePlugin, SigUtils.SigListen
   }
 
   @Override
-  public void 
onAlterSentryRoleGrantPrivilege(TAlterSentryRoleGrantPrivilegeRequest request,
-          Map<TSentryPrivilege, Update> privilegesUpdateMap) throws 
SentryPluginException {
-    Preconditions.checkNotNull(request, "request");
+  public void onAlterSentryRoleGrantPrivilege(String roleName, 
Set<TSentryPrivilege> privileges,
+                                              Map<TSentryPrivilege, Update> 
privilegesUpdateMap) throws SentryPluginException {
+    Preconditions.checkNotNull(roleName, "Role name is NULL");
+    Preconditions.checkNotNull(privilegesUpdateMap, "Privilege MAP NULL");
+    Preconditions.checkNotNull(privileges, "Privilege Set provided is NULL");
+
     if (LOGGER.isTraceEnabled()) {
-      LOGGER.trace("onAlterSentryRoleGrantPrivilege: {}", request); // 
request.toString() provides all details
+      LOGGER.trace("onAlterSentryRoleGrantPrivilege: {}", roleName, 
privileges);
     }
 
-    if (request.isSetPrivileges()) {
-      String roleName = request.getRoleName();
-
-      for (TSentryPrivilege privilege : request.getPrivileges()) {
+    if (privileges.size() > 0) {
+      for (TSentryPrivilege privilege : privileges) {
         
if(!(PrivilegeScope.COLUMN.name().equalsIgnoreCase(privilege.getPrivilegeScope())))
 {
           PermissionsUpdate update = onAlterSentryGrantPrivilegeCore(new 
TPrivilegeEntity(TPrivilegeEntityType.ROLE,
                   roleName), privilege);
@@ -285,7 +284,7 @@ public class SentryPlugin implements 
SentryPolicyStorePlugin, SigUtils.SigListen
     Preconditions.checkNotNull(privileges, "Privilege Set provided is NULL");
 
     if (LOGGER.isTraceEnabled()) {
-      LOGGER.trace("onAlterSentryUserGrantPrivilege: {}", userName);
+      LOGGER.trace("onAlterSentryUserGrantPrivilege: {}", userName, 
privileges);
     }
 
     if (privileges.size() > 0) {
@@ -349,18 +348,19 @@ public class SentryPlugin implements 
SentryPolicyStorePlugin, SigUtils.SigListen
   }
 
   @Override
-  public void 
onAlterSentryRoleRevokePrivilege(TAlterSentryRoleRevokePrivilegeRequest request,
-      Map<TSentryPrivilege, Update> privilegesUpdateMap)
+  public void onAlterSentryRoleRevokePrivilege(String roleName, 
Set<TSentryPrivilege> privileges,
+                                               Map<TSentryPrivilege, Update> 
privilegesUpdateMap)
           throws SentryPluginException {
-    Preconditions.checkNotNull(request, "request");
+    Preconditions.checkNotNull(roleName, "Role name is NULL");
+    Preconditions.checkNotNull(privilegesUpdateMap, "Privilege MAP NULL");
+    Preconditions.checkNotNull(privileges, "Privilege Set provided is NULL");
+
     if (LOGGER.isTraceEnabled()) {
-      LOGGER.trace("onAlterSentryRoleRevokePrivilege: {}", request); // 
request.toString() provides all details
+      LOGGER.trace("onAlterSentryRoleRevokePrivilege: {}", roleName, 
privileges);
     }
 
-    if (request.isSetPrivileges()) {
-      String roleName = request.getRoleName();
-
-      for (TSentryPrivilege privilege : request.getPrivileges()) {
+    if (privileges.size() > 0) {
+      for (TSentryPrivilege privilege : privileges) {
         if(!("COLUMN".equalsIgnoreCase(privilege.getPrivilegeScope()))) {
           PermissionsUpdate update = onAlterSentryRevokePrivilegeCore(new 
TPrivilegeEntity(TPrivilegeEntityType.ROLE,
                   roleName), privilege);
@@ -385,7 +385,7 @@ public class SentryPlugin implements 
SentryPolicyStorePlugin, SigUtils.SigListen
     Preconditions.checkNotNull(privileges, "Privilege Set provided is NULL");
 
     if (LOGGER.isTraceEnabled()) {
-      LOGGER.trace("onAlterSentryUserRevokePrivilege: {}", userName); // 
request.toString() provides all details
+      LOGGER.trace("onAlterSentryUserRevokePrivilege: {}", userName, 
privileges);
     }
 
     if (privileges.size() > 0) {

http://git-wip-us.apache.org/repos/asf/sentry/blob/4d9bc9ce/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/api/service/thrift/SentryMetrics.java
----------------------------------------------------------------------
diff --git 
a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/api/service/thrift/SentryMetrics.java
 
b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/api/service/thrift/SentryMetrics.java
index 5424bff..232a979 100644
--- 
a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/api/service/thrift/SentryMetrics.java
+++ 
b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/api/service/thrift/SentryMetrics.java
@@ -112,6 +112,8 @@ public final class SentryMetrics {
       name(SentryPolicyStoreProcessor.class, "list-privileges-for-provider"));
   final Timer listPrivilegesByAuthorizableTimer = METRIC_REGISTRY.timer(
       name(SentryPolicyStoreProcessor.class, 
"list-privileges-by-authorizable"));
+  final Timer notificationProcessTimer = METRIC_REGISTRY.timer(
+          name(SentryPolicyStoreProcessor.class, "process-hsm-notification"));
 
   /**
    * Return a Timer with name.

http://git-wip-us.apache.org/repos/asf/sentry/blob/4d9bc9ce/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/api/service/thrift/SentryPolicyStoreProcessor.java
----------------------------------------------------------------------
diff --git 
a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/api/service/thrift/SentryPolicyStoreProcessor.java
 
b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/api/service/thrift/SentryPolicyStoreProcessor.java
index 7f97ff7..5aef620 100644
--- 
a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/api/service/thrift/SentryPolicyStoreProcessor.java
+++ 
b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/api/service/thrift/SentryPolicyStoreProcessor.java
@@ -22,6 +22,8 @@ package org.apache.sentry.api.service.thrift;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -32,6 +34,8 @@ import java.util.regex.Pattern;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hive.metastore.messaging.EventMessage.EventType;
+import org.apache.sentry.SentryOwnerInfo;
 import org.apache.sentry.api.common.ThriftConstants;
 import org.apache.sentry.core.common.exception.SentryUserException;
 import 
org.apache.sentry.core.common.exception.SentrySiteConfigurationException;
@@ -68,6 +72,7 @@ import static com.codahale.metrics.MetricRegistry.name;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
@@ -81,6 +86,10 @@ import static org.apache.sentry.hdfs.Updateable.Update;
 public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
   private static final Logger LOGGER = 
Logger.getLogger(SentryPolicyStoreProcessor.class);
   private static final Logger AUDIT_LOGGER = 
Logger.getLogger(Constants.AUDIT_LOGGER_NAME);
+  private static final Map<TSentryObjectOwnerType, SentryEntityType> 
mapOwnerType = ImmutableMap.of(
+          TSentryObjectOwnerType.ROLE, SentryEntityType.ROLE,
+          TSentryObjectOwnerType.USER, SentryEntityType.USER
+  );
 
   private final String name;
   private final Configuration conf;
@@ -257,7 +266,7 @@ public class SentryPolicyStoreProcessor implements 
SentryPolicyService.Iface {
       Preconditions.checkState(sentryPlugins.size() <= 1);
       Map<TSentryPrivilege, Update> privilegesUpdateMap = new HashMap<>();
       for (SentryPolicyStorePlugin plugin : sentryPlugins) {
-        plugin.onAlterSentryRoleGrantPrivilege(request, privilegesUpdateMap);
+        plugin.onAlterSentryRoleGrantPrivilege(request.getRoleName(), 
request.getPrivileges(), privilegesUpdateMap);
       }
 
       if (!privilegesUpdateMap.isEmpty()) {
@@ -335,7 +344,7 @@ public class SentryPolicyStoreProcessor implements 
SentryPolicyService.Iface {
       Preconditions.checkState(sentryPlugins.size() <= 1);
       Map<TSentryPrivilege, Update> privilegesUpdateMap = new HashMap<>();
       for (SentryPolicyStorePlugin plugin : sentryPlugins) {
-        plugin.onAlterSentryRoleRevokePrivilege(request, privilegesUpdateMap);
+        plugin.onAlterSentryRoleRevokePrivilege(request.getRoleName(), 
request.getPrivileges(), privilegesUpdateMap);
       }
 
       if (!privilegesUpdateMap.isEmpty()) {
@@ -1342,6 +1351,179 @@ public class SentryPolicyStoreProcessor implements 
SentryPolicyService.Iface {
     return response;
   }
 
+  @Override
+  public TSentryHmsEventNotificationResponse sentry_notify_hms_event
+          (TSentryHmsEventNotification request) throws TException{
+    TSentryHmsEventNotificationResponse response = new 
TSentryHmsEventNotificationResponse();
+    final Timer.Context timerContext = 
sentryMetrics.notificationProcessTimer.time();
+    EventType eventType = EventType.valueOf(request.getEventType());
+    try {
+      switch (eventType) {
+        case CREATE_DATABASE:
+        case CREATE_TABLE:
+          // Wait till Sentry server processes HMS Notification Event.
+          if(request.getId() > 0) {
+            response.setId(syncEventId(request.getId()));
+          }
+          //Grant privilege to the owner.
+          grantOwnerPrivilege(request);
+          break;
+        case DROP_DATABASE:
+        case DROP_TABLE:
+          // Wait till Sentry server processes HMS Notification Event.
+          if(request.getId() > 0) {
+            response.setId(syncEventId(request.getId()));
+          }
+          // Owner privileges for the database and tables that are dropped are 
cleaned-up when
+          // sentry fetches and process the DROP_DATABASE and DROP_TABLE 
notifications.
+          break;
+        case ALTER_TABLE:
+          /* Alter table event is notified to sentry when either of below is 
observed.
+             together.
+             1. Owner Update
+             2. Table Rename
+          */
+          // Wait till Sentry server processes HMS Notification Event.
+          if(request.getId() > 0) {
+            response.setId(syncEventId(request.getId()));
+          }
+          // Owner is updated. There is no need to wait till Sentry processes 
HMS Notification Event.
+          // Revoke owner privilege from old owners and grant one to the new 
owner.
+          updateOwnerPrivilege(request);
+          break;
+        default:
+         LOGGER.info("Processing HMS Event of Type: " + eventType.toString() + 
" skipped");
+      }
+      response.setStatus(Status.OK());
+    } catch (SentryNoSuchObjectException e) {
+      String msg = request.getOwnerType().toString() + ": " + 
request.getOwnerName() + " doesn't exist";
+      LOGGER.error(msg, e);
+      response.setStatus(Status.NoSuchObject(msg, e));
+    } catch (SentryInvalidInputException e) {
+      LOGGER.error(e.getMessage(), e);
+      response.setStatus(Status.InvalidInput(e.getMessage(), e));
+    } catch (SentryThriftAPIMismatchException e) {
+      LOGGER.error(e.getMessage(), e);
+      response.setStatus(Status.THRIFT_VERSION_MISMATCH(e.getMessage(), e));
+    } catch (Exception e) {
+      String msg = "Unknown error for request: " + request + ", message: " + 
e.getMessage();
+      LOGGER.error(msg, e);
+        response.setStatus(Status.RuntimeError(msg, e));
+    } finally {
+      timerContext.stop();
+    }
+    return response;
+  }
+
+  /**
+   * Grants owner privilege  to an authorizable.
+   *
+   * Privilege is granted based on the information in 
TSentryHmsEventNotification
+   * @param request TSentryHmsEventNotification
+   * @throws Exception when there an exception while sending/processing the 
request.
+   */
+  private void grantOwnerPrivilege(TSentryHmsEventNotification request) throws 
Exception {
+    if (Strings.isNullOrEmpty(request.getOwnerName()) || 
(request.getOwnerType().getValue() == 0)) {
+      LOGGER.debug(String.format("Owner Information not provided for 
Operation: [%s], Not adding owner privilege for" +
+              " object: [%s].[%s]", request.getEventType(), 
request.getAuthorizable().getDb(),
+              request.getAuthorizable().getTable()));
+      return;
+    }
+
+    TSentryPrivilege ownerPrivilege = 
constructOwnerPrivilege(request.getAuthorizable());
+    if (ownerPrivilege == null) {
+      LOGGER.debug("Owner privilege is not added");
+      return;
+    }
+
+    SentryEntityType entityType = getSentryEntityType(request.getOwnerType());
+    if (entityType == null) {
+      String error = "Invalid owner type : " + request.getEventType();
+      LOGGER.error(error);
+      throw new SentryInvalidInputException(error);
+    }
+
+    Preconditions.checkState(sentryPlugins.size() <= 1);
+    Set<TSentryPrivilege> privSet = Collections.singleton(ownerPrivilege);
+    Map<TSentryPrivilege, Update> privilegesUpdateMap = new HashMap<>();
+    switch (request.getOwnerType()) {
+      case ROLE:
+        for (SentryPolicyStorePlugin plugin : sentryPlugins) {
+          plugin.onAlterSentryRoleGrantPrivilege(request.getOwnerName(), 
privSet, privilegesUpdateMap);
+        }
+        break;
+      case USER:
+        for (SentryPolicyStorePlugin plugin : sentryPlugins) {
+          plugin.onAlterSentryUserGrantPrivilege(request.getOwnerName(), 
privSet, privilegesUpdateMap);
+        }
+        break;
+      default:
+        LOGGER.error("Invalid owner Type");
+    }
+    // Grants owner privilege to the entity
+    sentryStore.alterSentryGrantOwnerPrivilege(request.getOwnerName(), 
entityType,
+            ownerPrivilege, privilegesUpdateMap.get(ownerPrivilege));
+    //TODO Implement notificationHandlerInvoker API for granting user priv and 
invoke it.
+    //TODO Implement Audit Log API's and invoke them here.
+  }
+
+  /**
+   * Alters owner privilege of an authorizable.
+   *
+   * Revoke all the owner privileges on the authorizable and grants new owner 
privilege.
+   * @param request Sentry HMS Event Notification
+   * @throws Exception when there an exception while sending/processing the 
request.
+   */
+  private void updateOwnerPrivilege(TSentryHmsEventNotification request) 
throws Exception {
+    if (Strings.isNullOrEmpty(request.getOwnerName()) || 
(request.getOwnerType().getValue() == 0)) {
+      LOGGER.debug(String.format("Owner Information not provided for 
Operation: [%s], Not revoking owner privilege for" +
+                      " object: [%s].[%s]", request.getEventType(), 
request.getAuthorizable().getDb(),
+              request.getAuthorizable().getTable()));
+      return;
+    }
+
+    TSentryPrivilege ownerPrivilege = 
constructOwnerPrivilege(request.getAuthorizable());
+    if (ownerPrivilege == null) {
+      LOGGER.debug("Owner privilege is not added");
+      return;
+    }
+
+    SentryEntityType entityType = getSentryEntityType(request.getOwnerType());
+    if(entityType == null ) {
+      String error = "Invalid owner type : " + request.getEventType();
+      LOGGER.error(error);
+      throw new SentryInvalidInputException(error);
+    }
+
+    Set<TSentryPrivilege> privSet = Collections.singleton(ownerPrivilege);
+    Preconditions.checkState(sentryPlugins.size() <= 1);
+    Map<TSentryPrivilege, Update> privilegesUpdateMap = new HashMap<>();
+    List<Update> updateList = new ArrayList<>();
+    List<SentryOwnerInfo> ownerInfoList = 
sentryStore.listOwnersByAuthorizable(request.getAuthorizable());
+    // Creating updates for deleting all the old owner privileges
+    // There should only one owner privilege for an authorizable but the 
current schema
+    // doesn't have constraints to limit it. It is possible to have multiple 
owners for an authorizable (which is unlikely)
+    // This logic makes sure of revoking all the owner privilege.
+    for (SentryOwnerInfo ownerInfo : ownerInfoList) {
+      if (ownerInfo.getOwnerType() == SentryEntityType.USER) {
+        for (SentryPolicyStorePlugin plugin : sentryPlugins) {
+          plugin.onAlterSentryUserRevokePrivilege(ownerInfo.getOwnerName(), 
privSet, privilegesUpdateMap);
+          updateList.add(privilegesUpdateMap.get(ownerPrivilege));
+        }
+      } else if (ownerInfo.getOwnerType() == SentryEntityType.ROLE) {
+        for (SentryPolicyStorePlugin plugin : sentryPlugins) {
+          plugin.onAlterSentryRoleRevokePrivilege(request.getOwnerName(), 
privSet, privilegesUpdateMap);
+          updateList.add(privilegesUpdateMap.get(ownerPrivilege));
+        }
+      }
+    }
+    // Revokes old owner privileges and grants owner privilege for new owner.
+    sentryStore.updateOwnerPrivilege(request.getAuthorizable(), 
request.getOwnerName(),
+        entityType, updateList);
+    //TODO Implement notificationHandlerInvoker API for granting user priv and 
invoke it.
+    //TODO Implement Audit Log API's and invoke them here.
+  }
+
   /**
    * This API constructs (@Link TSentryPrivilege} for authorizable provided
    * based on the configurations.
@@ -1352,7 +1534,7 @@ public class SentryPolicyStoreProcessor implements 
SentryPolicyService.Iface {
   TSentryPrivilege constructOwnerPrivilege(TSentryAuthorizable authorizable) {
     Boolean isOwnerPrivEnabled = 
conf.getBoolean(ServerConfig.SENTRY_ENABLE_OWNER_PRIVILEGES,
       ServerConfig.SENTRY_ENABLE_OWNER_PRIVILEGES_DEFAULT);
-    if(isOwnerPrivEnabled == false) {
+    if(!isOwnerPrivEnabled) {
       return null;
     }
     if(Strings.isNullOrEmpty(authorizable.getDb())) {
@@ -1366,6 +1548,9 @@ public class SentryPolicyStoreProcessor implements 
SentryPolicyService.Iface {
     ownerPrivilege.setDbName(authorizable.getDb());
     if(!Strings.isNullOrEmpty(authorizable.getTable())) {
       ownerPrivilege.setTableName(authorizable.getTable());
+      ownerPrivilege.setPrivilegeScope("TABLE");
+    } else {
+      ownerPrivilege.setPrivilegeScope("DATABASE");
     }
     if(privilegeWithGrantOption) {
       ownerPrivilege.setGrantOption(TSentryGrantOption.TRUE);
@@ -1374,12 +1559,34 @@ public class SentryPolicyStoreProcessor implements 
SentryPolicyService.Iface {
     return ownerPrivilege;
   }
 
-  @Override
-  public TSentryHmsEventNotificationResponse sentry_notify_hms_event
-          (TSentryHmsEventNotification request) throws TException{
-    TSentryHmsEventNotificationResponse response = new 
TSentryHmsEventNotificationResponse();
-    //TODO This API has to be implemented.
-    response.setStatus(Status.OK());
-    return response;
+  /**
+   *
+   * @param ownerType
+   * @return SentryEntityType if input was valid, otherwise returns null
+   * @throws Exception
+   */
+  private SentryEntityType getSentryEntityType(TSentryObjectOwnerType 
ownerType) throws Exception {
+    return mapOwnerType.get(ownerType);
+  }
+
+  /**
+   * Syncronizes with the eventId processed by sentry
+   * @param eventId
+   * @return current counter value that should be no smaller then the requested
+   * value, returns 0 if there were an exception.
+   */
+  long syncEventId(long eventId) {
+    try {
+      return sentryStore.getCounterWait().waitFor(eventId);
+    } catch (InterruptedException e) {
+      String msg = String.format("wait request for id %d is interrupted",
+              eventId);
+      LOGGER.error(msg, e);
+      Thread.currentThread().interrupt();
+    } catch (TimeoutException e) {
+      String msg = String.format("timed out wait request for id %d", eventId);
+      LOGGER.warn(msg, e);
+    }
+    return 0;
   }
 }

http://git-wip-us.apache.org/repos/asf/sentry/blob/4d9bc9ce/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/SentryPolicyStorePlugin.java
----------------------------------------------------------------------
diff --git 
a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/SentryPolicyStorePlugin.java
 
b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/SentryPolicyStorePlugin.java
index 52f25dc..e27e1db 100644
--- 
a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/SentryPolicyStorePlugin.java
+++ 
b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/SentryPolicyStorePlugin.java
@@ -24,8 +24,6 @@ import 
org.apache.sentry.core.common.exception.SentryUserException;
 import org.apache.sentry.provider.db.service.persistent.SentryStore;
 import org.apache.sentry.api.service.thrift.TAlterSentryRoleAddGroupsRequest;
 import 
org.apache.sentry.api.service.thrift.TAlterSentryRoleDeleteGroupsRequest;
-import 
org.apache.sentry.api.service.thrift.TAlterSentryRoleGrantPrivilegeRequest;
-import 
org.apache.sentry.api.service.thrift.TAlterSentryRoleRevokePrivilegeRequest;
 import org.apache.sentry.api.service.thrift.TDropPrivilegesRequest;
 import org.apache.sentry.api.service.thrift.TDropSentryRoleRequest;
 import org.apache.sentry.api.service.thrift.TRenamePrivilegesRequest;
@@ -62,10 +60,24 @@ public interface SentryPolicyStorePlugin {
 
   Update onAlterSentryRoleDeleteGroups(TAlterSentryRoleDeleteGroupsRequest 
tRequest) throws SentryPluginException;
 
-  void onAlterSentryRoleGrantPrivilege(TAlterSentryRoleGrantPrivilegeRequest 
tRequest,
-        Map<TSentryPrivilege, Update> privilegesUpdateMap) throws 
SentryPluginException;
+  /**
+   * Used to create an update when privileges are granted to owner who is a 
Role
+   * @param roleName
+   * @param privileges
+   * @param privilegesUpdateMap
+   * @throws SentryPluginException
+   */
+  void onAlterSentryRoleGrantPrivilege(String roleName, Set<TSentryPrivilege> 
privileges,
+       Map<TSentryPrivilege, Update> privilegesUpdateMap) throws 
SentryPluginException;
 
-  void onAlterSentryRoleRevokePrivilege(TAlterSentryRoleRevokePrivilegeRequest 
tRequest,
+  /**
+   * Used to create an update when privileges are revoked from owner who is a 
role
+   * @param roleName
+   * @param privileges
+   * @param privilegesUpdateMap
+   * @throws SentryPluginException
+   */
+  void onAlterSentryRoleRevokePrivilege(String roleName, Set<TSentryPrivilege> 
privileges,
         Map<TSentryPrivilege, Update> privilegesUpdateMap) throws 
SentryPluginException;
 
   /**

http://git-wip-us.apache.org/repos/asf/sentry/blob/4d9bc9ce/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/persistent/CounterWait.java
----------------------------------------------------------------------
diff --git 
a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/persistent/CounterWait.java
 
b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/persistent/CounterWait.java
index d8c8297..ea2f77d 100644
--- 
a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/persistent/CounterWait.java
+++ 
b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/persistent/CounterWait.java
@@ -54,7 +54,7 @@ import java.util.concurrent.atomic.AtomicLong;
  * updater threads.
  */
 @ThreadSafe
-public final class CounterWait {
+public class CounterWait {
   // Implementation notes.
   //
   // The implementation is based on:
@@ -102,7 +102,7 @@ public final class CounterWait {
 
   /**
    * Create an instance of CounterWait object that will timeout during wait
-   * @param waitTimeout maximum time in seconds to wait for counter
+   * @param waitTimeoutSec maximum time in seconds to wait for counter
    */
   public CounterWait(long waitTimeoutSec) {
     this(waitTimeoutSec, TimeUnit.SECONDS);

http://git-wip-us.apache.org/repos/asf/sentry/blob/4d9bc9ce/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java
----------------------------------------------------------------------
diff --git 
a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java
 
b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java
index d673239..27b8876 100644
--- 
a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java
+++ 
b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java
@@ -42,6 +42,7 @@ import javax.jdo.Query;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.hadoop.conf.Configuration;
+import org.apache.sentry.SentryOwnerInfo;
 import org.apache.sentry.core.common.exception.SentryAccessDeniedException;
 import org.apache.sentry.core.common.exception.SentryAlreadyExistsException;
 import org.apache.sentry.core.common.exception.SentryGrantDeniedException;
@@ -199,6 +200,7 @@ public class SentryStore {
    */
   private final CounterWait counterWait;
 
+  private final boolean ownerPrivilegeWithGrant;
   public static Properties getDataNucleusProperties(Configuration conf)
           throws SentrySiteConfigurationException, IOException {
     Properties prop = new Properties();
@@ -283,6 +285,8 @@ public class SentryStore {
     long notificationTimeout = 
conf.getInt(ServerConfig.SENTRY_NOTIFICATION_SYNC_TIMEOUT_MS,
             ServerConfig.SENTRY_NOTIFICATION_SYNC_TIMEOUT_DEFAULT);
     counterWait = new CounterWait(notificationTimeout, TimeUnit.MILLISECONDS);
+    ownerPrivilegeWithGrant = 
conf.getBoolean(ServerConfig.SENTRY_OWNER_PRIVILEGE_WITH_GRANT,
+            ServerConfig.SENTRY_OWNER_PRIVILEGE_WITH_GRANT_DEFAULT);
   }
 
   public void setPersistUpdateDeltas(boolean persistUpdateDeltas) {
@@ -793,7 +797,8 @@ public class SentryStore {
       if(type == SentryEntityType.ROLE) {
         throw noSuchRole(entityName);
       } else if(type == SentryEntityType.USER) {
-        throw noSuchUser(entityName);
+        // User might not exist. Creating one.
+        mEntity = new MSentryUser(entityName, System.currentTimeMillis(), 
Sets.newHashSet());
       }
     }
 
@@ -802,8 +807,9 @@ public class SentryStore {
       throw new SentryInvalidInputException("cannot grant URI privileges to 
Null or EMPTY location");
     }
 
-    if (!isNULL(privilege.getColumnName()) || !isNULL(privilege.getTableName())
-        || !isNULL(privilege.getDbName())) {
+    if ((!isNULL(privilege.getColumnName()) || 
!isNULL(privilege.getTableName())
+        || !isNULL(privilege.getDbName()))
+        && !AccessConstants.OWNER.equalsIgnoreCase(privilege.getAction())) {
       // If Grant is for ALL and Either INSERT/SELECT already exists..
       // need to remove it and GRANT ALL..
       if (AccessConstants.ALL.equalsIgnoreCase(privilege.getAction())
@@ -878,6 +884,36 @@ public class SentryStore {
   }
 
   /**
+   * Alter a give sentry user/role to set owner privilege, as well as persist 
the corresponding
+   * permission change to MSentryPermChange table in a single transaction.
+   * Creates User, if it is not already there.
+   * Internally calls alterSentryGrantPrivilege.
+   * @param entityName Entity name to which permissions should be granted.
+   * @param entityType Entity Type
+   * @param privilege Privilege to be granted
+   * @param update DeltaTransactionBlock
+   * @throws Exception
+   */
+  public void alterSentryGrantOwnerPrivilege(final String entityName, 
SentryEntityType entityType,
+                                              final TSentryPrivilege privilege,
+                                              final Update update) throws 
Exception {
+    execute(update, pm -> {
+      pm.setDetachAllOnCommit(false); // No need to detach objects
+      String trimmedEntityName = trimAndLower(entityName);
+
+      // Alter sentry Role and grant Privilege.
+      MSentryPrivilege mPrivilege = alterSentryGrantPrivilegeCore(pm, 
entityType,
+              trimmedEntityName, privilege);
+
+      if (mPrivilege != null) {
+        // update the privilege to be the one actually updated.
+        convertToTSentryPrivilege(mPrivilege, privilege);
+      }
+      return null;
+    });
+  }
+
+  /**
    * Alter a given sentry user to grant a set of privileges, as well as 
persist the
    * corresponding permission change to MSentryPermChange table in a single 
transaction.
    * Internally calls alterSentryGrantPrivilege.
@@ -1092,7 +1128,7 @@ public class SentryStore {
       privilegeGraph.add(mFalse);
     }
     // Get the privilege graph
-    populateChildren(pm, SentryEntityType.ROLE, Sets.newHashSet(entityName), 
mPrivilege, privilegeGraph);
+    populateChildren(pm, type, Sets.newHashSet(entityName), mPrivilege, 
privilegeGraph);
     for (MSentryPrivilege childPriv : privilegeGraph) {
       revokePrivilege(pm, tPrivilege, mEntity, childPriv);
     }
@@ -1933,6 +1969,46 @@ public class SentryStore {
                 return result;
               });
   }
+  /**
+   * List the Owner privileges for an authorizable
+   * @param pm persistance manager
+   * @param authHierarchy Authorizable
+   * @return privilege list
+   * @throws Exception
+   */
+  private List<MSentryPrivilege> 
getMSentryOwnerPrivilegesByAuth(PersistenceManager pm,
+      final TSentryAuthorizable
+      authHierarchy) throws Exception {
+    Query query = pm.newQuery(MSentryPrivilege.class);
+    QueryParamBuilder paramBuilder = QueryParamBuilder.newQueryParamBuilder();
+    if (authHierarchy.getServer() != null) {
+      paramBuilder.add(SERVER_NAME, authHierarchy.getServer());
+      if (authHierarchy.getDb() != null) {
+        paramBuilder.add(DB_NAME, authHierarchy.getDb()).addNull(URI);
+        if (authHierarchy.getTable() != null) {
+          paramBuilder.add(TABLE_NAME, authHierarchy.getTable());
+        } else {
+          paramBuilder.addNull(TABLE_NAME);
+        }
+      } else if (authHierarchy.getUri() != null) {
+        paramBuilder.addNotNull(URI)
+                .addNull(DB_NAME)
+                .addCustomParam("(:authURI.startsWith(URI))", "authURI", 
authHierarchy.getUri());
+      } else {
+        paramBuilder.addNull(DB_NAME)
+                .addNull(URI);
+      }
+      paramBuilder.add(ACTION, AccessConstants.OWNER);
+    } else {
+      // if no server, then return empty result
+      return Collections.emptyList();
+    }
+    query.setFilter(paramBuilder.toString());
+    @SuppressWarnings("unchecked")
+    List<MSentryPrivilege> result = (List<MSentryPrivilege>) query.
+            executeWithMap(paramBuilder.getArguments());
+    return result;
+  }
 
   private Set<MSentryPrivilege> getMSentryPrivilegesByUserName(String userName)
       throws Exception {
@@ -1997,6 +2073,31 @@ public class SentryStore {
   }
 
   /**
+   * List the Owners for an authorizable
+   * @param authorizable Authorizable
+   * @return List of owner for an authorizable
+   * @throws Exception
+   */
+  public List<SentryOwnerInfo> listOwnersByAuthorizable(TSentryAuthorizable 
authorizable)
+          throws Exception {
+    List<SentryOwnerInfo> ownerInfolist = new ArrayList<>();
+    return tm.executeTransaction(
+            pm -> {
+              List<MSentryPrivilege> mSentryPrivileges =
+                      getMSentryOwnerPrivilegesByAuth(pm, authorizable);
+              for (MSentryPrivilege priv : mSentryPrivileges) {
+                for (PrivilegeEntity user : priv.getUsers()) {
+                  ownerInfolist.add(new SentryOwnerInfo(user.getType(), 
user.getEntityName()));
+                }
+                for (PrivilegeEntity role : priv.getRoles()) {
+                  ownerInfolist.add(new SentryOwnerInfo(role.getType(), 
role.getEntityName()));
+                }
+              }
+              return ownerInfolist;
+            });
+  }
+
+  /**
    * Get all privileges associated with the authorizable and input users
    * @param userNames the users to get their privileges
    * @param authHierarchy the authorizables
@@ -2577,6 +2678,78 @@ public class SentryStore {
   }
 
   /**
+   * Updates the owner privileges by revoking owner privileges to an 
authorizable and adding new
+   * privilege based on the arguments provided.
+   * @param tAuthorizable Authorizable to which owner privilege should be 
granted.
+   * @param ownerName
+   * @param entityType
+   * @param updates Delta Updates.
+   * @throws Exception
+   */
+  public synchronized void updateOwnerPrivilege(final TSentryAuthorizable 
tAuthorizable,
+      String ownerName,  SentryEntityType entityType,
+      final List<Update> updates) throws Exception {
+    execute(updates, pm -> {
+      if(entityType == null) {
+        LOGGER.info("Invalid Entity Type");
+      }
+      pm.setDetachAllOnCommit(false); // No need to detach objects
+      TSentryPrivilege tOwnerPrivilege = toSentryPrivilege(tAuthorizable);
+      tOwnerPrivilege.setAction(AccessConstants.OWNER);
+
+      revokeOwnerPrivilegesCore(pm, tAuthorizable);
+
+      try {
+        if(ownerPrivilegeWithGrant) {
+          tOwnerPrivilege.setGrantOption(TSentryGrantOption.TRUE);
+        }
+        //Granting the privilege.
+        alterSentryGrantPrivilegeCore(pm, entityType, ownerName, 
tOwnerPrivilege);
+        return null;
+      } catch (JDODataStoreException e) {
+        throw new SentryInvalidInputException("Failed to grant owner privilege 
on Authorizable : " +
+                tAuthorizable.toString() + " to " + entityType.toString() + ": 
" + ownerName + " "
+                + e.getMessage());
+      }
+    });
+  }
+
+  /**
+   * Revokes all the owner privileges granted to an authorizable
+   * @param tAuthorizable authorizable for which owner privilege should be 
revoked.
+   * @param updates
+   * @throws Exception
+   */
+  @VisibleForTesting
+  void revokeOwnerPrivileges(final TSentryAuthorizable tAuthorizable, final 
List<Update> updates)
+     throws Exception{
+    execute(updates, pm -> {
+      pm.setDetachAllOnCommit(false);
+      revokeOwnerPrivilegesCore(pm, tAuthorizable);
+      return null;
+    });
+  }
+
+  public void revokeOwnerPrivilegesCore(PersistenceManager pm, final 
TSentryAuthorizable tAuthorizable)
+      throws Exception{
+    TSentryPrivilege tOwnerPrivilege = toSentryPrivilege(tAuthorizable);
+    tOwnerPrivilege.setAction(AccessConstants.OWNER);
+
+    // Finding owner privileges and removing them.
+    List<MSentryPrivilege> mOwnerPrivileges = 
getMSentryPrivileges(tOwnerPrivilege, pm);
+    for(MSentryPrivilege mOwnerPriv : mOwnerPrivileges) {
+      Set<MSentryUser> users;
+      users = mOwnerPriv.getUsers();
+      // Making sure of removing stale users.
+      for (MSentryUser user : users) {
+        user.removePrivilege(mOwnerPriv);
+        persistEntity(pm, SentryEntityType.USER, user);
+      }
+    }
+    pm.deletePersistentAll(mOwnerPrivileges);
+  }
+
+  /**
    * Rename the privilege for all roles. Drop the old privilege name and 
create the new one.
    *
    * @param oldTAuthorizable the old authorizable name needs to be renamed.
@@ -4469,12 +4642,31 @@ public class SentryStore {
    */
   private void execute(Update update,
         TransactionBlock<Object> transactionBlock) throws Exception {
-    List<TransactionBlock<Object>> tbs = new ArrayList<>(2);
+    execute(update != null ? Collections.singletonList(update) : 
Collections.emptyList(), transactionBlock);
+  }
 
-    if (persistUpdateDeltas && update != null) {
-      tbs.add(new DeltaTransactionBlock(update));
-    }
 
+  /**
+   * Execute multiple delta updates in a single transaction.
+   * Note that this method only applies to TransactionBlock that
+   * does not have any return value.
+   * <p>
+   * Failure in any TransactionBlock would cause the whole transaction
+   * to fail.
+   *
+   * @param updates list of delta updates
+   * @throws Exception
+   */
+  private void execute(List<Update> updates, TransactionBlock<Object> 
transactionBlock) throws Exception {
+    // Currently this API is used to update the owner privilege. This needs 
two DeltaTransactionBlock's to record
+    // revoking/granting owner privilege and one TransactionBlock to perform 
actual permission change.
+    // Default size of tbs is picked accordingly.
+    List<TransactionBlock<Object>> tbs = new ArrayList<>(3);
+    if (persistUpdateDeltas && updates != null && updates.size() > 0) {
+      for (Update update : updates) {
+        tbs.add(new DeltaTransactionBlock(update));
+      }
+    }
     tbs.add(transactionBlock);
     tm.executeTransactionBlocksWithRetry(tbs);
   }

http://git-wip-us.apache.org/repos/asf/sentry/blob/4d9bc9ce/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/api/service/thrift/TestSentryPolicyStoreProcessor.java
----------------------------------------------------------------------
diff --git 
a/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/api/service/thrift/TestSentryPolicyStoreProcessor.java
 
b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/api/service/thrift/TestSentryPolicyStoreProcessor.java
index 6bfe872..de4e001 100644
--- 
a/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/api/service/thrift/TestSentryPolicyStoreProcessor.java
+++ 
b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/api/service/thrift/TestSentryPolicyStoreProcessor.java
@@ -23,11 +23,14 @@ import static org.junit.Assert.assertTrue;
 import com.codahale.metrics.Gauge;
 import com.google.common.collect.Sets;
 import java.util.Set;
+import org.apache.hadoop.hive.metastore.messaging.EventMessage;
+import org.apache.hadoop.hive.metastore.messaging.EventMessage.EventType;
 import org.apache.sentry.api.common.ApiConstants;
 import org.apache.sentry.api.common.Status;
 import org.apache.sentry.api.common.ThriftConstants;
 import org.apache.sentry.core.common.exception.SentryInvalidInputException;
 import org.apache.sentry.core.model.db.AccessConstants;
+import org.apache.sentry.provider.db.service.persistent.CounterWait;
 import org.apache.sentry.service.common.ServiceConstants;
 import 
org.apache.sentry.core.common.exception.SentrySiteConfigurationException;
 import org.apache.sentry.provider.db.service.persistent.SentryStore;
@@ -44,11 +47,17 @@ import org.mockito.Mockito;
 
 public class TestSentryPolicyStoreProcessor {
 
+  private static final String DBNAME = "db1";
+  private static final String TABLENAME = "table1";
+  private static final String OWNER = "owner1";
   private Configuration conf;
   private static final SentryStore sentryStore = 
Mockito.mock(SentryStore.class);
+  private static final CounterWait counterWait = 
Mockito.mock(CounterWait.class);
   @Before
-  public void setup() {
+  public void setup() throws Exception{
     conf = new Configuration(false);
+    //Check behaviour when DB name is not set
+    
conf.setBoolean(ServiceConstants.ServerConfig.SENTRY_ENABLE_OWNER_PRIVILEGES, 
true);
 
     Mockito.when(sentryStore.getRoleCountGauge()).thenReturn(new Gauge< Long 
>() {
       @Override
@@ -98,8 +107,14 @@ public class TestSentryPolicyStoreProcessor {
       }
     });
 
+    Mockito.doAnswer((invocation) -> {
+      long id = (long) invocation.getArguments()[0];
+      return id;
+    }).when(counterWait).waitFor(Mockito.anyLong());
 
-
+    Mockito.doAnswer((invocation) -> {
+      return counterWait;
+    }).when(sentryStore).getCounterWait();
   }
   @Test(expected=SentrySiteConfigurationException.class)
   public void testConfigNotNotificationHandler() throws Exception {
@@ -147,6 +162,7 @@ public class TestSentryPolicyStoreProcessor {
 
   @Test
   public void testConstructOwnerPrivilege() throws Exception {
+    
conf.setBoolean(ServiceConstants.ServerConfig.SENTRY_ENABLE_OWNER_PRIVILEGES, 
false);
     SentryPolicyStoreProcessor sentryServiceHandler =
             new 
SentryPolicyStoreProcessor(ApiConstants.SentryPolicyServiceConstants.SENTRY_POLICY_SERVICE_NAME,
                     conf, sentryStore);
@@ -173,6 +189,7 @@ public class TestSentryPolicyStoreProcessor {
     authorizable.setDb("db1");
     privilege.setDbName("db1");
     privilege.setAction(AccessConstants.OWNER);
+    privilege.setPrivilegeScope("DATABASE");
     
Assert.assertNotNull(sentryServiceHandler.constructOwnerPrivilege(authorizable));
     Assert.assertEquals(privilege, 
sentryServiceHandler.constructOwnerPrivilege(authorizable));
 
@@ -181,6 +198,7 @@ public class TestSentryPolicyStoreProcessor {
     authorizable.setDb("db1");
     authorizable.setTable("tb1");
     privilege.setTableName("tb1");
+    privilege.setPrivilegeScope("TABLE");
     
Assert.assertNotNull(sentryServiceHandler.constructOwnerPrivilege(authorizable));
     Assert.assertEquals(privilege, 
sentryServiceHandler.constructOwnerPrivilege(authorizable));
 
@@ -193,6 +211,7 @@ public class TestSentryPolicyStoreProcessor {
     authorizable = new TSentryAuthorizable("");
     authorizable.setDb("db1");
     authorizable.setTable("tb1");
+    privilege.setPrivilegeScope("TABLE");
     privilege.setGrantOption(TSentryGrantOption.TRUE);
     
Assert.assertNotNull(sentryServiceHandler.constructOwnerPrivilege(authorizable));
     Assert.assertEquals(privilege, 
sentryServiceHandler.constructOwnerPrivilege(authorizable));
@@ -281,4 +300,106 @@ public class TestSentryPolicyStoreProcessor {
     privilege.setAction(action);
     return privilege;
   }
+
+@Test
+  public void testCreateTableEventProcessing() throws Exception {
+    SentryPolicyStoreProcessor sentryServiceHandler =
+            new 
SentryPolicyStoreProcessor(ApiConstants.SentryPolicyServiceConstants.SENTRY_POLICY_SERVICE_NAME,
+                    conf, sentryStore);
+    TSentryAuthorizable authorizable = new TSentryAuthorizable();
+    authorizable.setDb(DBNAME);
+    authorizable.setTable(TABLENAME);
+
+    TSentryHmsEventNotification notification = new 
TSentryHmsEventNotification();
+    notification.setId(1L);
+    notification.setOwnerType(TSentryObjectOwnerType.ROLE);
+    notification.setOwnerName(OWNER);
+    notification.setAuthorizable(authorizable);
+    notification.setEventType(EventMessage.EventType.CREATE_TABLE.toString());
+
+    sentryServiceHandler.sentry_notify_hms_event(notification);
+
+     TSentryPrivilege ownerPrivilege = 
sentryServiceHandler.constructOwnerPrivilege(authorizable);
+    Mockito.verify(
+            sentryStore, Mockito.times(1)
+    ).alterSentryGrantOwnerPrivilege(OWNER, SentryEntityType.ROLE, 
ownerPrivilege, null);
+
+    notification.setOwnerType(TSentryObjectOwnerType.USER);
+    sentryServiceHandler.sentry_notify_hms_event(notification);
+
+    //Verify Sentry Store is invoked to grant privilege.
+  Mockito.verify(
+          sentryStore, Mockito.times(1)
+  ).alterSentryGrantOwnerPrivilege(OWNER, SentryEntityType.USER, 
ownerPrivilege, null);
+  }
+
+
+  @Test
+  public void testCreateDatabaseEventProcessing() throws Exception {
+
+    SentryPolicyStoreProcessor sentryServiceHandler =
+            new 
SentryPolicyStoreProcessor(ApiConstants.SentryPolicyServiceConstants.SENTRY_POLICY_SERVICE_NAME,
+                    conf, sentryStore);
+    TSentryAuthorizable authorizable = new TSentryAuthorizable();
+    authorizable.setDb(DBNAME);
+
+    TSentryHmsEventNotification notification = new 
TSentryHmsEventNotification();
+    notification.setId(1L);
+    notification.setOwnerType(TSentryObjectOwnerType.ROLE);
+    notification.setOwnerName(OWNER);
+    notification.setAuthorizable(authorizable);
+    notification.setEventType(EventType.CREATE_DATABASE.toString());
+
+    sentryServiceHandler.sentry_notify_hms_event(notification);
+
+    //Verify Sentry Store is invoked to grant privilege.
+    TSentryPrivilege ownerPrivilege = 
sentryServiceHandler.constructOwnerPrivilege(authorizable);
+    Mockito.verify(
+            sentryStore, Mockito.times(1)
+    ).alterSentryGrantOwnerPrivilege(OWNER, SentryEntityType.ROLE, 
ownerPrivilege, null);
+
+    notification.setOwnerType(TSentryObjectOwnerType.USER);
+    sentryServiceHandler.sentry_notify_hms_event(notification);
+
+    //Verify Sentry Store is invoked to grant privilege.
+    Mockito.verify(
+            sentryStore, Mockito.times(1)
+    ).alterSentryGrantOwnerPrivilege(OWNER, SentryEntityType.USER, 
ownerPrivilege, null);
+  }
+
+  @Test
+  public void testAlterTableEventProcessing() throws Exception {
+
+    SentryPolicyStoreProcessor sentryServiceHandler =
+            new 
SentryPolicyStoreProcessor(ApiConstants.SentryPolicyServiceConstants.SENTRY_POLICY_SERVICE_NAME,
+                    conf, sentryStore);
+    TSentryAuthorizable authorizable = new TSentryAuthorizable();
+    authorizable.setDb(DBNAME);
+    authorizable.setTable(TABLENAME);
+
+    TSentryHmsEventNotification notification = new 
TSentryHmsEventNotification();
+    notification.setId(1L);
+    notification.setOwnerType(TSentryObjectOwnerType.ROLE);
+    notification.setOwnerName(OWNER);
+    notification.setAuthorizable(authorizable);
+    notification.setEventType(EventType.ALTER_TABLE.toString());
+
+    sentryServiceHandler.sentry_notify_hms_event(notification);
+
+    //Verify Sentry Store is invoked to grant privilege.
+    Mockito.verify(
+            sentryStore, Mockito.times(1)
+    ).updateOwnerPrivilege(Mockito.eq(authorizable), Mockito.eq(OWNER), 
Mockito.eq(SentryEntityType.ROLE),
+    Mockito.anyList());
+
+
+    notification.setOwnerType(TSentryObjectOwnerType.USER);
+    sentryServiceHandler.sentry_notify_hms_event(notification);
+
+    //Verify Sentry Store is invoked to grant privilege.
+    Mockito.verify(
+            sentryStore, Mockito.times(1)
+    ).updateOwnerPrivilege(Mockito.eq(authorizable), Mockito.eq(OWNER), 
Mockito.eq(SentryEntityType.ROLE),
+    Mockito.anyList());
+  }
 }

http://git-wip-us.apache.org/repos/asf/sentry/blob/4d9bc9ce/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryStore.java
----------------------------------------------------------------------
diff --git 
a/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryStore.java
 
b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryStore.java
index c056446..51048bc 100644
--- 
a/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryStore.java
+++ 
b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryStore.java
@@ -43,6 +43,7 @@ import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.security.alias.CredentialProvider;
 import org.apache.hadoop.security.alias.CredentialProviderFactory;
 import org.apache.hadoop.security.alias.UserProvider;
+import org.apache.sentry.SentryOwnerInfo;
 import org.apache.sentry.core.common.exception.SentryAccessDeniedException;
 import org.apache.sentry.core.common.exception.SentryInvalidInputException;
 import org.apache.sentry.core.model.db.AccessConstants;
@@ -1976,6 +1977,209 @@ public class TestSentryStore extends org.junit.Assert {
   }
 
   /**
+   * Grants owner privileges to role/user and updates the owner
+   * and makes sure that the owner privilege is updated.
+   * @throws Exception
+   */
+  @Test
+  public void testUpdateOwnerPrivilege() throws Exception {
+    String roleName1 = "list-privs-r1", roleName2 = "list-privs-r2", roleName3 
= "list-privs-r3";
+    String userName1 = "user1", userName2 = "user2";
+    String grantor = "g1";
+    List<SentryOwnerInfo> ownerInfoList  = null;
+    sentryStore.createSentryRole(roleName1);
+    sentryStore.createSentryRole(roleName2);
+    sentryStore.createSentryRole(roleName3);
+    sentryStore.createSentryUser(userName1);
+    sentryStore.createSentryUser(userName2);
+
+
+    TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
+    privilege_tbl1.setPrivilegeScope("TABLE");
+    privilege_tbl1.setServerName("server1");
+    privilege_tbl1.setDbName("db1");
+    privilege_tbl1.setTableName("tbl1");
+    privilege_tbl1.setCreateTime(System.currentTimeMillis());
+    privilege_tbl1.setAction("OWNER");
+
+    TSentryAuthorizable tSentryAuthorizable = new TSentryAuthorizable();
+    tSentryAuthorizable.setServer("server1");
+    tSentryAuthorizable.setDb("db1");
+    tSentryAuthorizable.setTable("tbl1");
+
+    sentryStore.alterSentryGrantPrivilege(grantor, SentryEntityType.ROLE, 
roleName1, privilege_tbl1, null);
+
+    assertEquals(1, sentryStore.getAllTSentryPrivilegesByRoleName(roleName1)
+            .size());
+    assertEquals(0, sentryStore.getAllTSentryPrivilegesByRoleName(roleName2)
+            .size());
+
+    ownerInfoList  = sentryStore.listOwnersByAuthorizable(tSentryAuthorizable);
+    assertEquals(1, ownerInfoList.size());
+    assertEquals(SentryEntityType.ROLE, ownerInfoList.get(0).getOwnerType());
+    assertEquals(roleName1, ownerInfoList.get(0).getOwnerName());
+
+
+    // Change owner from a one role to another role
+    sentryStore.updateOwnerPrivilege(tSentryAuthorizable, roleName2, 
SentryEntityType.ROLE, null);
+    ownerInfoList  = sentryStore.listOwnersByAuthorizable(tSentryAuthorizable);
+    assertEquals(1, ownerInfoList.size());
+    assertEquals(SentryEntityType.ROLE, ownerInfoList.get(0).getOwnerType());
+    assertEquals(roleName2, ownerInfoList.get(0).getOwnerName());
+
+    assertEquals(0, sentryStore.getAllTSentryPrivilegesByRoleName(roleName1)
+            .size());
+    assertEquals(1, sentryStore.getAllTSentryPrivilegesByRoleName(roleName2)
+            .size());
+
+    tSentryAuthorizable.setTable("tbl2");
+    TSentryPrivilege privilege_tbl2 = new TSentryPrivilege(privilege_tbl1);
+    privilege_tbl2.setTableName("tbl2");
+
+    // Change owner from a one user to another user
+    sentryStore.alterSentryGrantPrivilege(grantor, 
SentryEntityType.USER,userName1, privilege_tbl2, null);
+    assertEquals(1, sentryStore.getAllTSentryPrivilegesByUserName(userName1)
+            .size());
+
+
+
+    sentryStore.updateOwnerPrivilege(tSentryAuthorizable, userName2, 
SentryEntityType.USER, null);
+    assertEquals(1, sentryStore.getAllTSentryPrivilegesByUserName(userName2)
+            .size());
+    ownerInfoList  = sentryStore.listOwnersByAuthorizable(tSentryAuthorizable);
+    assertEquals(1, ownerInfoList.size());
+    assertEquals(SentryEntityType.USER, ownerInfoList.get(0).getOwnerType());
+    assertEquals(userName2, ownerInfoList.get(0).getOwnerName());
+
+  // Change owner from a user to role
+    sentryStore.updateOwnerPrivilege(tSentryAuthorizable, roleName1, 
SentryEntityType.ROLE, null);
+    assertEquals(1, sentryStore.getAllTSentryPrivilegesByRoleName(roleName1)
+            .size());
+
+    // At this point  roleName1 has owner privilege on db1.tb2
+    //Add all privilege to roleName1 and make sure that owner privilege is not 
effected.
+    TSentryPrivilege privilege_tbl2_all = new TSentryPrivilege(privilege_tbl2);
+    privilege_tbl2_all.setAction(AccessConstants.ALL);
+    sentryStore.alterSentryGrantPrivilege(grantor, SentryEntityType.ROLE, 
roleName1, privilege_tbl2_all, null);
+    // Verify that there are two privileges.
+    assertEquals(2, sentryStore.getAllTSentryPrivilegesByRoleName(roleName1)
+            .size());
+
+
+    tSentryAuthorizable.setTable("tbl3");
+    TSentryPrivilege privilege_tbl3_all = new TSentryPrivilege(privilege_tbl2);
+    privilege_tbl3_all.setAction(AccessConstants.ALL);
+    privilege_tbl3_all.setTableName("tbl3");
+    sentryStore.alterSentryGrantPrivilege(grantor, SentryEntityType.ROLE, 
roleName3, privilege_tbl3_all, null);
+    assertEquals(1, sentryStore.getAllTSentryPrivilegesByRoleName(roleName3)
+            .size());
+    TSentryPrivilege privilege_tbl3_owner = new 
TSentryPrivilege(privilege_tbl3_all);
+    privilege_tbl3_owner.setAction(AccessConstants.OWNER);
+    sentryStore.alterSentryGrantPrivilege(grantor, SentryEntityType.ROLE, 
roleName3, privilege_tbl3_owner, null);
+
+    assertEquals(2, sentryStore.getAllTSentryPrivilegesByRoleName(roleName3)
+            .size());
+  }
+
+  @Test
+  public void testListSentryOwnerPrivilegesByAuthorizable() throws Exception {
+    String roleName1 = "list-privs-r1";
+    String userName1 = "user1";
+    String grantor = "g1";
+    sentryStore.createSentryRole(roleName1);
+    sentryStore.createSentryUser(userName1);
+
+    TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
+    privilege_tbl1.setPrivilegeScope("TABLE");
+    privilege_tbl1.setServerName("server1");
+    privilege_tbl1.setDbName("db1");
+    privilege_tbl1.setTableName("tbl1");
+    privilege_tbl1.setCreateTime(System.currentTimeMillis());
+    privilege_tbl1.setAction("OWNER");
+    privilege_tbl1.setGrantOption(TSentryGrantOption.TRUE);
+
+    TSentryAuthorizable tSentryAuthorizable = new TSentryAuthorizable();
+    tSentryAuthorizable.setServer("server1");
+    tSentryAuthorizable.setDb("db1");
+    tSentryAuthorizable.setTable("tbl1");
+
+    sentryStore.alterSentryGrantPrivilege(grantor, SentryEntityType.ROLE, 
roleName1, privilege_tbl1, null);
+
+    sentryStore.updateOwnerPrivilege(tSentryAuthorizable, userName1, 
SentryEntityType.USER, null);
+    assertEquals(1, sentryStore.getAllTSentryPrivilegesByUserName(userName1)
+            .size());
+  }
+
+  @Test
+  public void testRevokeOwnerPrivilege() throws Exception {
+
+    String roleName1 = "list-privs-r1";
+    String userName1 = "user1";
+    String grantor = "g1";
+    sentryStore.createSentryRole(roleName1);
+    sentryStore.createSentryUser(userName1);
+
+    TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
+    privilege_tbl1.setPrivilegeScope("TABLE");
+    privilege_tbl1.setServerName("server1");
+    privilege_tbl1.setDbName("db1");
+    privilege_tbl1.setTableName("tbl1");
+    privilege_tbl1.setCreateTime(System.currentTimeMillis());
+    privilege_tbl1.setAction("OWNER");
+    privilege_tbl1.setGrantOption(TSentryGrantOption.TRUE);
+
+    TSentryAuthorizable tSentryAuthorizable = new TSentryAuthorizable();
+    tSentryAuthorizable.setServer("server1");
+    tSentryAuthorizable.setDb("db1");
+    tSentryAuthorizable.setTable("tbl1");
+
+    sentryStore.alterSentryGrantPrivilege(grantor, SentryEntityType.ROLE, 
roleName1, privilege_tbl1, null);
+
+    sentryStore.revokeOwnerPrivileges(tSentryAuthorizable, null);
+    assertEquals(0, sentryStore.getAllTSentryPrivilegesByUserName(userName1)
+            .size());
+  }
+
+  @Test
+  public void testDropUserOnUpdateOwnerPrivilege() throws Exception {
+    String userName1 = "user1", userName2 = "user2";
+    String grantor = "g1";
+    sentryStore.createSentryUser(userName1);
+    sentryStore.createSentryUser(userName2);
+
+
+    TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
+    privilege_tbl1.setPrivilegeScope("TABLE");
+    privilege_tbl1.setServerName("server1");
+    privilege_tbl1.setDbName("db1");
+    privilege_tbl1.setTableName("tbl1");
+    privilege_tbl1.setCreateTime(System.currentTimeMillis());
+    privilege_tbl1.setAction("OWNER");
+
+    TSentryAuthorizable tSentryAuthorizable = new TSentryAuthorizable();
+    tSentryAuthorizable.setServer("server1");
+    tSentryAuthorizable.setDb("db1");
+    tSentryAuthorizable.setTable("tbl1");
+
+
+    // Change owner from a one user to another user
+    sentryStore.alterSentryGrantPrivilege(grantor, SentryEntityType.USER, 
userName1, privilege_tbl1, null);
+    assertEquals(1, sentryStore.getAllTSentryPrivilegesByUserName(userName1)
+            .size());
+
+
+    sentryStore.updateOwnerPrivilege(tSentryAuthorizable, userName2, 
SentryEntityType.USER, null);
+    assertEquals(1, sentryStore.getAllTSentryPrivilegesByUserName(userName2)
+            .size());
+
+    try {
+      sentryStore.createSentryUser(userName1);
+    } catch (Exception e) {
+      fail("Exception should not be seen asthe user: " + userName1 + " should 
have been deleted.");
+    }
+
+  }
+  /**
    * Regression test for SENTRY-547 and SENTRY-548
    * Use case:
    * GRANT INSERT on TABLE tbl1 to ROLE role1

Reply via email to