You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sentry.apache.org by sp...@apache.org on 2018/10/27 15:01:27 UTC

[2/2] sentry git commit: SENTRY-2372: SentryStore should not implement grantOptionCheck (Sergio Pena, reviewed by Na Li)

SENTRY-2372: SentryStore should not implement grantOptionCheck (Sergio Pena, reviewed by Na Li)


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

Branch: refs/heads/master
Commit: 8e0505703eaf7c75c268593f5f31a3e66bb04a56
Parents: 6734686
Author: Sergio Pena <se...@cloudera.com>
Authored: Sat Oct 27 10:00:59 2018 -0500
Committer: Sergio Pena <se...@cloudera.com>
Committed: Sat Oct 27 10:00:59 2018 -0500

----------------------------------------------------------------------
 .../sentry/core/common/utils/SentryUtils.java   |  12 +
 .../org/apache/sentry/hdfs/SentryPlugin.java    |   6 +-
 .../thrift/SentryPolicyStoreProcessor.java      |  77 +-
 .../service/thrift/SentryPolicyStoreUtils.java  |  93 +++
 .../db/service/model/MSentryPrivilege.java      |   3 +-
 .../db/service/persistent/SentryStore.java      | 288 +------
 .../persistent/SentryStoreInterface.java        |  18 +-
 .../SentryPolicyStoreProcessorTestUtils.java    | 238 ++++++
 .../thrift/TestSentryPolicyStoreProcessor.java  | 159 ++++
 .../TestHMSFollowerSentryStoreIntegration.java  |  18 +-
 .../db/service/persistent/TestSentryStore.java  | 741 ++++---------------
 11 files changed, 763 insertions(+), 890 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/sentry/blob/8e050570/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/SentryUtils.java
----------------------------------------------------------------------
diff --git a/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/SentryUtils.java b/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/SentryUtils.java
index b225af0..f3394b8 100644
--- a/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/SentryUtils.java
+++ b/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/core/common/utils/SentryUtils.java
@@ -18,6 +18,9 @@
 
 package org.apache.sentry.core.common.utils;
 
+import static org.apache.sentry.core.common.utils.SentryConstants.NULL_COL;
+
+import com.google.common.base.Strings;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -74,4 +77,13 @@ public final class SentryUtils {
 
     return collapsedStrings.toString();
   }
+
+  /**
+   * Function to check if a string is null, empty or @NULL_COLL specifier
+   * @param s string input, and can be null.
+   * @return True if the input string represents a NULL string - when it is null, empty or equals @NULL_COL
+   */
+  public static boolean isNULL(String s) {
+    return Strings.isNullOrEmpty(s) || s.equals(NULL_COL);
+  }
 }

http://git-wip-us.apache.org/repos/asf/sentry/blob/8e050570/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 b8f5ce7..0f3c162 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
@@ -27,6 +27,7 @@ import org.apache.hadoop.conf.Configuration;
 import org.apache.sentry.api.common.ApiConstants.PrivilegeScope;
 import org.apache.sentry.core.common.exception.SentryInvalidInputException;
 import org.apache.sentry.core.common.utils.PubSub;
+import org.apache.sentry.core.common.utils.SentryUtils;
 import org.apache.sentry.core.common.utils.SigUtils;
 import org.apache.sentry.hdfs.ServiceConstants.ServerConfig;
 import org.apache.sentry.hdfs.service.thrift.TPrivilegeChanges;
@@ -34,7 +35,6 @@ import org.apache.sentry.hdfs.service.thrift.TPrivilegePrincipal;
 import org.apache.sentry.hdfs.service.thrift.TPrivilegePrincipalType;
 import org.apache.sentry.hdfs.service.thrift.TRoleChanges;
 import org.apache.sentry.provider.db.SentryPolicyStorePlugin;
-import org.apache.sentry.provider.db.service.persistent.SentryStore;
 import org.apache.sentry.provider.db.service.persistent.SentryStoreInterface;
 import org.apache.sentry.api.common.SentryServiceUtil;
 import org.apache.sentry.api.service.thrift.TAlterSentryRoleAddGroupsRequest;
@@ -489,10 +489,10 @@ public class SentryPlugin implements SentryPolicyStorePlugin, SigUtils.SigListen
 
   private String getAuthzObj(TSentryPrivilege privilege) {
     String authzObj = null;
-    if (!SentryStore.isNULL(privilege.getDbName())) {
+    if (!SentryUtils.isNULL(privilege.getDbName())) {
       String dbName = privilege.getDbName();
       String tblName = privilege.getTableName();
-      if (SentryStore.isNULL(tblName)) {
+      if (SentryUtils.isNULL(tblName)) {
         authzObj = dbName;
       } else {
         authzObj = dbName + "." + tblName;

http://git-wip-us.apache.org/repos/asf/sentry/blob/8e050570/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 709434c..b9e3bf2 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
@@ -37,6 +37,7 @@ 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.SentryGrantDeniedException;
 import org.apache.sentry.core.common.exception.SentryUserException;
 import org.apache.sentry.core.common.exception.SentrySiteConfigurationException;
 import org.apache.sentry.core.common.utils.SentryConstants;
@@ -245,6 +246,60 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
     return response;
   }
 
+  /**
+   * Throws an exception if one of the set of privileges passed as a parameter cannot be granted by
+   * the grantor user.
+   *
+   * <p/> The check is done by looking at the grant option flag each user or user/group role have
+   * stored on the DB, and compare it with set of privileges that the user is attempting to grant.
+   * If one of the privileges has the grant option disabled, then this method throws an exception
+   * to let the caller know it cannot continue with the grant of the privilege.
+   *
+   * @param grantorUser The user who is attempting to grant the set or privileges.
+   * @param checkPrivileges The set of privileges to check.
+   * @throws Exception If the user does not have grant privileges.
+   */
+  private void checkGrantOptionPrivileges(String grantorUser, Set<TSentryPrivilege> checkPrivileges)
+    throws Exception {
+    Preconditions.checkNotNull(checkPrivileges, "Privileges to check for grant option must not be null.");
+
+    Set<String> groups = getGroupsFromUserName(conf, grantorUser);
+    if (groups != null && inAdminGroups(groups)) {
+      // grantorUser is part of one of the admin groups, so we permit the grant action
+      return;
+    }
+
+    // Get all the privileges a user has (either directly granted to the user or through a role
+    // which the user belongs too)
+    Set<TSentryPrivilege> userPrivileges = sentryStore.listSentryPrivilegesByUsersAndGroups(
+      groups, Collections.singleton(grantorUser), new TSentryActiveRoleSet(true, null), null
+    );
+
+    if (userPrivileges == null || userPrivileges.isEmpty()) {
+      throw new SentryGrantDeniedException(
+        String.format("User %s does not have privileges to grant.", grantorUser));
+    }
+
+    // Check if each privilege grant will be permitted. Throws an exception in the first privilege
+    // that is not permitted.
+    for (TSentryPrivilege checkPrivilege : checkPrivileges) {
+      boolean hasGrant = false;
+      for (TSentryPrivilege p : userPrivileges) {
+        if (p.getGrantOption() == TSentryGrantOption.TRUE
+            && SentryPolicyStoreUtils.privilegeImplies(p, checkPrivilege)) {
+          hasGrant = true;
+          break;
+        }
+      }
+
+      if (!hasGrant) {
+        throw new SentryGrantDeniedException(
+          String.format("User %s does not have privileges to grant %s.", grantorUser,
+            checkPrivilege.getAction().toUpperCase()));
+      }
+    }
+  }
+
   @Override
   public TAlterSentryRoleGrantPrivilegeResponse alter_sentry_role_grant_privilege
   (TAlterSentryRoleGrantPrivilegeRequest request) throws TException {
@@ -264,6 +319,9 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
       // Throw an exception if one of the grants is not permitted.
       SentryServiceUtil.checkDbExplicitGrantsPermitted(conf, request.getPrivileges());
 
+      // Throw an exception if the user has not rights to grant one of the grants requested
+      checkGrantOptionPrivileges(request.getRequestorUserName(), request.getPrivileges());
+
       // TODO: now only has SentryPlugin. Once add more SentryPolicyStorePlugins,
       // TODO: need to differentiate the updates for different Plugins.
       Preconditions.checkState(sentryPlugins.size() <= 1);
@@ -273,11 +331,11 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
       }
 
       if (!privilegesUpdateMap.isEmpty()) {
-        sentryStore.alterSentryRoleGrantPrivileges(request.getRequestorUserName(),
-            request.getRoleName(), request.getPrivileges(), privilegesUpdateMap);
+        sentryStore.alterSentryRoleGrantPrivileges(request.getRoleName(),
+          request.getPrivileges(), privilegesUpdateMap);
       } else {
-        sentryStore.alterSentryRoleGrantPrivileges(request.getRequestorUserName(),
-            request.getRoleName(), request.getPrivileges());
+        sentryStore.alterSentryRoleGrantPrivileges(request.getRoleName(),
+          request.getPrivileges());
       }
       GrantPrivilegeRequestValidator.validate(request);
       response.setStatus(Status.OK());
@@ -332,6 +390,9 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
         request.setPrivileges(Sets.newHashSet(request.getPrivilege()));
       }
 
+      // Throw an exception if the user has not rights to revoke one of the revokes requested
+      checkGrantOptionPrivileges(request.getRequestorUserName(), request.getPrivileges());
+
       // TODO: now only has SentryPlugin. Once add more SentryPolicyStorePlugins,
       // TODO: need to differentiate the updates for different Plugins.
       Preconditions.checkState(sentryPlugins.size() <= 1);
@@ -341,11 +402,11 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
       }
 
       if (!privilegesUpdateMap.isEmpty()) {
-        sentryStore.alterSentryRoleRevokePrivileges(request.getRequestorUserName(),
-            request.getRoleName(), request.getPrivileges(), privilegesUpdateMap);
+        sentryStore.alterSentryRoleRevokePrivileges(request.getRoleName(),
+          request.getPrivileges(), privilegesUpdateMap);
       } else {
-        sentryStore.alterSentryRoleRevokePrivileges(request.getRequestorUserName(),
-            request.getRoleName(), request.getPrivileges());
+        sentryStore.alterSentryRoleRevokePrivileges(request.getRoleName(),
+          request.getPrivileges());
       }
       RevokePrivilegeRequestValidator.validate(request);
       response.setStatus(Status.OK());

http://git-wip-us.apache.org/repos/asf/sentry/blob/8e050570/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/api/service/thrift/SentryPolicyStoreUtils.java
----------------------------------------------------------------------
diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/api/service/thrift/SentryPolicyStoreUtils.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/api/service/thrift/SentryPolicyStoreUtils.java
new file mode 100644
index 0000000..8d8b407
--- /dev/null
+++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/api/service/thrift/SentryPolicyStoreUtils.java
@@ -0,0 +1,93 @@
+/*
+  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.api.service.thrift;
+
+import static org.apache.sentry.core.common.utils.SentryUtils.isNULL;
+
+import org.apache.sentry.core.common.utils.PathUtils;
+import org.apache.sentry.core.model.db.AccessConstants;
+
+/**
+ * Utilities for Sentry policy store functions.
+ */
+public class SentryPolicyStoreUtils {
+  // Private to avoid instantiate this class
+  private SentryPolicyStoreUtils() {
+  }
+
+  /**
+   * Return true if this privilege implies other privilege (copied from MSentryPrivilege.implies).
+   * Otherwise, return false
+   *
+   * @param privilege, privilege to compare against the other privilege
+   * @param other, the other privilege
+   * @return True if both privilege are the same; False otherwise.
+   */
+  static boolean privilegeImplies(TSentryPrivilege privilege, TSentryPrivilege other) {
+    // serverName never be null
+    if (isNULL(privilege.getServerName()) || isNULL(other.getServerName())) {
+      return false;
+    } else if (!privilege.getServerName().equals(other.getServerName())) {
+      return false;
+    }
+
+    // check URI implies
+    if (!isNULL(privilege.getURI()) && !isNULL(other.getURI())) {
+      if (!PathUtils.impliesURI(privilege.getURI(), other.getURI())) {
+        return false;
+      }
+      // if URI is NULL, check dbName and tableName
+    } else if (isNULL(privilege.getURI()) && isNULL(other.getURI())) {
+      if (!isNULL(privilege.getDbName())) {
+        if (isNULL(other.getDbName())) {
+          return false;
+        } else if (!privilege.getDbName().equals(other.getDbName())) {
+          return false;
+        }
+      }
+      if (!isNULL(privilege.getTableName())) {
+        if (isNULL(other.getTableName())) {
+          return false;
+        } else if (!privilege.getTableName().equals(other.getTableName())) {
+          return false;
+        }
+      }
+      if (!isNULL(privilege.getColumnName())) {
+        if (isNULL(other.getColumnName())) {
+          return false;
+        } else if (!privilege.getColumnName().equals(other.getColumnName())) {
+          return false;
+        }
+      }
+      // if URI is not NULL, but other's URI is NULL, return false
+    } else if (!isNULL(privilege.getURI()) && isNULL(other.getURI())){
+      return false;
+    }
+
+    // check action implies
+    String action = privilege.getAction();
+    if (!action.equalsIgnoreCase(AccessConstants.ALL)
+      && !action.equalsIgnoreCase(AccessConstants.OWNER)
+      && !action.equalsIgnoreCase(other.getAction())
+      && !action.equalsIgnoreCase(AccessConstants.ACTION_ALL)) {
+      return false;
+    }
+
+    return true;
+  }
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/8e050570/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPrivilege.java
----------------------------------------------------------------------
diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPrivilege.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPrivilege.java
index eba40d2..009b326 100644
--- a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPrivilege.java
+++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPrivilege.java
@@ -24,6 +24,7 @@ import java.util.Set;
 import javax.jdo.annotations.PersistenceCapable;
 
 import org.apache.sentry.core.common.utils.PathUtils;
+import org.apache.sentry.core.common.utils.SentryUtils;
 import org.apache.sentry.core.model.db.AccessConstants;
 import org.apache.sentry.provider.db.service.persistent.SentryStore;
 import org.apache.sentry.provider.db.service.persistent.PrivilegePrincipal;
@@ -351,7 +352,7 @@ public class MSentryPrivilege {
   }
 
   private boolean isNULL(String s) {
-    return SentryStore.isNULL(s);
+    return SentryUtils.isNULL(s);
   }
 
   public boolean isActionALL() {

http://git-wip-us.apache.org/repos/asf/sentry/blob/8e050570/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 869605e..01b3634 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
@@ -54,11 +54,9 @@ 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;
 import org.apache.sentry.core.common.exception.SentryInvalidInputException;
 import org.apache.sentry.core.common.exception.SentryNoSuchObjectException;
 import org.apache.sentry.core.common.exception.SentrySiteConfigurationException;
-import org.apache.sentry.core.common.exception.SentryUserException;
 import org.apache.sentry.core.common.utils.PathUtils;
 import org.apache.sentry.core.common.utils.SentryConstants;
 import org.apache.sentry.core.model.db.AccessConstants;
@@ -83,7 +81,6 @@ import org.apache.sentry.provider.db.service.model.MSentryUtil;
 import org.apache.sentry.provider.db.service.model.MPath;
 import org.apache.sentry.hdfs.service.thrift.TPrivilegePrincipal;
 import org.apache.sentry.api.common.ApiConstants.PrivilegeScope;
-import org.apache.sentry.api.service.thrift.SentryPolicyStoreProcessor;
 import org.apache.sentry.api.service.thrift.TSentryActiveRoleSet;
 import org.apache.sentry.api.service.thrift.TSentryAuthorizable;
 import org.apache.sentry.api.service.thrift.TSentryGrantOption;
@@ -114,6 +111,7 @@ import static org.apache.sentry.core.common.utils.SentryConstants.NULL_COL;
 import static org.apache.sentry.core.common.utils.SentryConstants.SERVER_NAME;
 import static org.apache.sentry.core.common.utils.SentryConstants.TABLE_NAME;
 import static org.apache.sentry.core.common.utils.SentryConstants.URI;
+import static org.apache.sentry.core.common.utils.SentryUtils.isNULL;
 import static org.apache.sentry.hdfs.Updateable.Update;
 
 /**
@@ -725,47 +723,25 @@ public class SentryStore implements SentryStoreInterface {
     }
   }
 
-  /**
-   * Alter a given sentry role to grant a set of privileges.
-   * Internally calls alterSentryGrantPrivilege.
-   *
-   * @param grantorPrincipal User name
-   * @param roleName Role name
-   * @param privileges Set of privileges
-   * @throws Exception
-   */
-  public void alterSentryRoleGrantPrivileges(final String grantorPrincipal,
-      final String roleName, final Set<TSentryPrivilege> privileges) throws Exception {
+  @Override
+  public void alterSentryRoleGrantPrivileges(final String roleName,
+    final Set<TSentryPrivilege> privileges) throws Exception {
     for (TSentryPrivilege privilege : privileges) {
-      alterSentryGrantPrivilege(grantorPrincipal, SentryPrincipalType.ROLE, roleName, privilege, null);
+      alterSentryGrantPrivilege(SentryPrincipalType.ROLE, roleName, privilege, null);
     }
   }
 
-  /**
-   * Alter a given sentry role/user to grant a privilege, as well as persist the corresponding
-   * permission change to MSentryPermChange table in a single transaction.
-   *
-   * @param grantorPrincipal User name
-   * @param type Type of principal to which privilege is granted.
-   * @param name the name of the principal to which privilege is granted.
-   * @param privilege the given privilege
-   * @param update the corresponding permission delta update if any.
-   * @throws Exception
-   *
-   */
-  synchronized void alterSentryGrantPrivilege(final String grantorPrincipal, SentryPrincipalType type,
-     final String name, final TSentryPrivilege privilege,
-     final Update update) throws Exception {
+  synchronized void alterSentryGrantPrivilege(SentryPrincipalType type, final String name,
+    final TSentryPrivilege privilege,
+    final Update update) throws Exception {
 
     execute(update, pm -> {
       pm.setDetachAllOnCommit(false); // No need to detach objects
-
-      // first do grant check
-      grantOptionCheck(pm, grantorPrincipal, privilege);
+      String trimmedEntityName = trimAndLower(name);
 
       // Alter sentry Role and grant Privilege.
       MSentryPrivilege mPrivilege = alterSentryGrantPrivilegeCore(pm, type,
-              name, privilege);
+              trimmedEntityName, privilege);
 
       if (mPrivilege != null) {
         // update the privilege to be the one actually updated.
@@ -775,27 +751,15 @@ public class SentryStore implements SentryStoreInterface {
     });
   }
 
-  /**
-   * Alter a given sentry role to grant a set of privileges, as well as persist the
-   * corresponding permission change to MSentryPermChange table in a single transaction.
-   * Internally calls alterSentryGrantPrivilege.
-   *
-   * @param grantorPrincipal User name
-   * @param roleName the given role name
-   * @param privileges a Set of privileges
-   * @param privilegesUpdateMap the corresponding <privilege, DeltaTransactionBlock> map
-   * @throws Exception
-   *
-   */
-  public void alterSentryRoleGrantPrivileges(final String grantorPrincipal,
-      final String roleName, final Set<TSentryPrivilege> privileges,
-      final Map<TSentryPrivilege, Update> privilegesUpdateMap) throws Exception {
+  @Override
+  public void alterSentryRoleGrantPrivileges(final String roleName,
+    final Set<TSentryPrivilege> privileges,
+    final Map<TSentryPrivilege, Update> privilegesUpdateMap) throws Exception {
 
     Preconditions.checkNotNull(privilegesUpdateMap);
     for (TSentryPrivilege privilege : privileges) {
       Update update = privilegesUpdateMap.get(privilege);
-      alterSentryGrantPrivilege(grantorPrincipal, SentryPrincipalType.ROLE, roleName, privilege,
-          update);
+      alterSentryGrantPrivilege(SentryPrincipalType.ROLE, roleName, privilege, update);
     }
   }
 
@@ -923,13 +887,12 @@ public class SentryStore implements SentryStoreInterface {
    * Alter a given sentry user to grant a set of privileges.
    * Internally calls alterSentryGrantPrivilege.
    *
-   * @param grantorPrincipal User name
    * @param userName User name
    * @param privileges Set of privileges
    * @throws Exception
    */
-  public void alterSentryUserGrantPrivileges(final String grantorPrincipal,
-      final String userName, final Set<TSentryPrivilege> privileges) throws Exception {
+  public void alterSentryUserGrantPrivileges(final String userName,
+    final Set<TSentryPrivilege> privileges) throws Exception {
 
     try {
       MSentryUser userEntry = getMSentryUserByName(userName, false);
@@ -941,7 +904,7 @@ public class SentryStore implements SentryStoreInterface {
     }
 
     for (TSentryPrivilege privilege : privileges) {
-      alterSentryGrantPrivilege(grantorPrincipal, SentryPrincipalType.USER, userName, privilege, null);
+      alterSentryGrantPrivilege(SentryPrincipalType.USER, userName, privilege, null);
     }
   }
 
@@ -975,39 +938,6 @@ public class SentryStore implements SentryStoreInterface {
   }
 
   /**
-   * 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.
-   *
-   * @param grantorPrincipal User name
-   * @param userName the given user name
-   * @param privileges a Set of privileges
-   * @param privilegesUpdateMap the corresponding <privilege, DeltaTransactionBlock> map
-   * @throws Exception
-   *
-   */
-  public void alterSentryUserGrantPrivileges(final String grantorPrincipal,
-      final String userName, final Set<TSentryPrivilege> privileges,
-      final Map<TSentryPrivilege, Update> privilegesUpdateMap) throws Exception {
-
-    try {
-      MSentryUser userEntry = getMSentryUserByName(userName, false);
-      if (userEntry == null) {
-        createSentryUser(userName);
-      }
-    } catch (SentryAlreadyExistsException e) {
-      // the user may be created by other thread, so swallow the exception and proeed
-    }
-
-    Preconditions.checkNotNull(privilegesUpdateMap);
-    for (TSentryPrivilege privilege : privileges) {
-      Update update = privilegesUpdateMap.get(privilege);
-      alterSentryGrantPrivilege(grantorPrincipal, SentryPrincipalType.USER, userName, privilege,
-              update);
-    }
-  }
-
-  /**
    * Get the user entry by user name
    * @param userName the name of the user
    * @return the user entry
@@ -1048,109 +978,48 @@ public class SentryStore implements SentryStoreInterface {
    * Alter a given sentry user to revoke a set of privileges.
    * Internally calls alterSentryRevokePrivilege.
    *
-   * @param grantorPrincipal User name
-   * @param userName the given user name
-   * @param tPrivileges a Set of privileges
-   * @throws Exception
-   *
-   */
-  public void alterSentryUserRevokePrivileges(final String grantorPrincipal,
-      final String userName, final Set<TSentryPrivilege> tPrivileges) throws Exception {
-    for (TSentryPrivilege tPrivilege : tPrivileges) {
-      alterSentryRevokePrivilege(grantorPrincipal, SentryPrincipalType.USER, userName, tPrivilege, null);
-    }
-  }
-
-  /**
-   * Alter a given sentry user to revoke a set of privileges, as well as persist the
-   * corresponding permission change to MSentryPermChange table in a single transaction.
-   * Internally calls alterSentryRevokePrivilege.
-   *
-   * @param grantorPrincipal User name
    * @param userName the given user name
    * @param tPrivileges a Set of privileges
-   * @param privilegesUpdateMap the corresponding <privilege, Update> map
    * @throws Exception
    *
    */
-  public void alterSentryUserRevokePrivileges(final String grantorPrincipal,
-      final String userName, final Set<TSentryPrivilege> tPrivileges,
-      final Map<TSentryPrivilege, Update> privilegesUpdateMap)
-      throws Exception {
-
-    Preconditions.checkNotNull(privilegesUpdateMap);
+  public void alterSentryUserRevokePrivileges(final String userName,
+    final Set<TSentryPrivilege> tPrivileges) throws Exception {
     for (TSentryPrivilege tPrivilege : tPrivileges) {
-      Update update = privilegesUpdateMap.get(tPrivilege);
-      alterSentryRevokePrivilege(grantorPrincipal, SentryPrincipalType.USER, userName,
-              tPrivilege, update);
+      alterSentryRevokePrivilege(SentryPrincipalType.USER, userName, tPrivilege, null);
     }
   }
 
-  /**
-   * Alter a given sentry role to revoke a set of privileges.
-   * Internally calls alterSentryRevokePrivilege.
-   *
-   * @param grantorPrincipal User name
-   * @param roleName the given role name
-   * @param tPrivileges a Set of privileges
-   * @throws Exception
-   *
-   */
-  public void alterSentryRoleRevokePrivileges(final String grantorPrincipal,
-      final String roleName, final Set<TSentryPrivilege> tPrivileges) throws Exception {
+  @Override
+  public void alterSentryRoleRevokePrivileges(final String roleName,
+    final Set<TSentryPrivilege> tPrivileges)
+    throws Exception {
     for (TSentryPrivilege tPrivilege : tPrivileges) {
-      alterSentryRevokePrivilege(grantorPrincipal, SentryPrincipalType.ROLE, roleName, tPrivilege, null);
+      alterSentryRevokePrivilege(SentryPrincipalType.ROLE, roleName, tPrivilege, null);
     }
   }
 
-  /**
-   * Alter a given sentry role to revoke a privilege, as well as persist the corresponding
-   * permission change to MSentryPermChange table in a single transaction.
-   *
-   * @param grantorPrincipal User name
-   * @param type Type of principal to which privilege is granted.
-   * @param principalName the name of the principal from which privilege is revoked.
-   * @param tPrivilege the given privilege
-   * @param update the corresponding permission delta update transaction block
-   * @throws Exception
-   *
-   */
-  synchronized void alterSentryRevokePrivilege(final String grantorPrincipal, SentryPrincipalType type,
-                                              final String principalName, final TSentryPrivilege tPrivilege,
-                                              final Update update) throws Exception {
+  synchronized void alterSentryRevokePrivilege(SentryPrincipalType type, final String principalName,
+    final TSentryPrivilege tPrivilege,
+    final Update update) throws Exception {
     execute(update, pm -> {
       pm.setDetachAllOnCommit(false); // No need to detach objects
+      String trimmedEntityName = safeTrimLower(principalName);
 
-      // first do revoke check
-      grantOptionCheck(pm, grantorPrincipal, tPrivilege);
-
-      alterSentryRevokePrivilegeCore(pm, type, principalName, tPrivilege);
+      alterSentryRevokePrivilegeCore(pm, type, trimmedEntityName, tPrivilege);
       return null;
     });
   }
 
-  /**
-   * Alter a given sentry role to revoke a set of privileges, as well as persist the
-   * corresponding permission change to MSentryPermChange table in a single transaction.
-   * Internally calls alterSentryRevokePrivilege.
-   *
-   * @param grantorPrincipal User name
-   * @param roleName the given role name
-   * @param tPrivileges a Set of privileges
-   * @param privilegesUpdateMap the corresponding <privilege, Update> map
-   * @throws Exception
-   *
-   */
-  public void alterSentryRoleRevokePrivileges(final String grantorPrincipal,
-      final String roleName, final Set<TSentryPrivilege> tPrivileges,
-      final Map<TSentryPrivilege, Update> privilegesUpdateMap)
-          throws Exception {
+  @Override
+  public void alterSentryRoleRevokePrivileges(final String roleName, final Set<TSentryPrivilege> tPrivileges,
+    final Map<TSentryPrivilege, Update> privilegesUpdateMap)
+    throws Exception {
 
     Preconditions.checkNotNull(privilegesUpdateMap);
     for (TSentryPrivilege tPrivilege : tPrivileges) {
       Update update = privilegesUpdateMap.get(tPrivilege);
-      alterSentryRevokePrivilege(grantorPrincipal, SentryPrincipalType.ROLE, roleName,
-              tPrivilege, update);
+      alterSentryRevokePrivilege(SentryPrincipalType.ROLE, roleName, tPrivilege, update);
     }
   }
 
@@ -2144,12 +2013,6 @@ public class SentryStore implements SentryStoreInterface {
     return mSentryUser.getPrivileges();
   }
 
-  private Set<MSentryPrivilege> getMSentryPrivilegesByUserNameIfExists(String userName)
-          throws Exception {
-    MSentryUser mSentryUser = getMSentryUserByName(userName, false);
-    return mSentryUser != null ? mSentryUser.getPrivileges() : Collections.emptySet();
-  }
-
   /**
    * Gets sentry privilege objects for a given userName from the persistence layer
    * @param userName : userName to look up
@@ -2478,6 +2341,7 @@ public class SentryStore implements SentryStoreInterface {
     return result;
   }
 
+  @Override
   public Set<TSentryPrivilege> listSentryPrivilegesByUsersAndGroups(
       Set<String> groups, Set<String> users, TSentryActiveRoleSet roleSet,
       TSentryAuthorizable authHierarchy) throws Exception {
@@ -3147,84 +3011,6 @@ public class SentryStore implements SentryStoreInterface {
   }
 
   /**
-   * Function to check if a string is null, empty or @NULL_COLL specifier
-   * @param s string input, and can be null.
-   * @return True if the input string represents a NULL string - when it is null, empty or equals @NULL_COL
-   */
-  public static boolean isNULL(String s) {
-    return Strings.isNullOrEmpty(s) || s.equals(NULL_COL);
-  }
-
-  /**
-   * Grant option check
-   * @param pm Persistence manager instance
-   * @param grantorPrincipal User name
-   * @param privilege Privilege to check
-   * @throws SentryUserException
-   */
-  private void grantOptionCheck(PersistenceManager pm, String grantorPrincipal,
-                                TSentryPrivilege privilege)
-      throws Exception {
-    MSentryPrivilege mPrivilege = convertToMSentryPrivilege(privilege);
-    if (grantorPrincipal == null) {
-      throw new SentryInvalidInputException("grantorPrincipal should not be null");
-    }
-
-    Set<String> groups = SentryPolicyStoreProcessor.getGroupsFromUserName(conf, grantorPrincipal);
-
-    // if grantor is in adminGroup, don't need to do check
-    Set<String> admins = getAdminGroups();
-    boolean isAdminGroup = false;
-    if (groups != null && !admins.isEmpty()) {
-      for (String g : groups) {
-        if (admins.contains(g)) {
-          isAdminGroup = true;
-          break;
-        }
-      }
-    }
-
-    if (!isAdminGroup) {
-      boolean hasGrant = false;
-      Set<MSentryPrivilege> privilegeSet = new HashSet<>();
-      Set<MSentryPrivilege> enityPrivilegeSet = null;
-      // Collect the privileges granted to all roles to that user.
-      Set<MSentryRole> roles = getRolesForGroups(pm, groups);
-      roles.addAll(getRolesForUsers(pm, Sets.newHashSet(grantorPrincipal)));
-      for (MSentryRole role : roles) {
-        enityPrivilegeSet = role.getPrivileges();
-        if(enityPrivilegeSet != null && !enityPrivilegeSet.isEmpty()) {
-          privilegeSet.addAll(enityPrivilegeSet);
-        }
-      }
-      // Collect the privileges granted to user
-      enityPrivilegeSet = getMSentryPrivilegesByUserNameIfExists(grantorPrincipal.trim());
-      if(enityPrivilegeSet != null && !enityPrivilegeSet.isEmpty()) {
-        privilegeSet.addAll(enityPrivilegeSet);
-
-      }
-      // Compare the privileges that user has with the privilege he/she is trying to grant.
-      for (MSentryPrivilege p : privilegeSet) {
-        if (p.getGrantOption() && p.implies(mPrivilege)) {
-          hasGrant = true;
-          break;
-        }
-      }
-
-      if (!hasGrant) {
-        throw new SentryGrantDeniedException(grantorPrincipal
-            + " has no grant!");
-      }
-    }
-  }
-
-  // get adminGroups from conf
-  private Set<String> getAdminGroups() {
-    return Sets.newHashSet(conf.getStrings(
-        ServerConfig.ADMIN_GROUPS, new String[]{}));
-  }
-
-  /**
    * Retrieves an up-to-date sentry permission snapshot.
    * <p>
    * It reads hiveObj to &lt role, privileges &gt mapping from {@link MSentryPrivilege}

http://git-wip-us.apache.org/repos/asf/sentry/blob/8e050570/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStoreInterface.java
----------------------------------------------------------------------
diff --git a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStoreInterface.java b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStoreInterface.java
index 6217719..97407ff 100644
--- a/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStoreInterface.java
+++ b/sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStoreInterface.java
@@ -96,31 +96,28 @@ public interface SentryStoreInterface {
    */
   void alterSentryRoleDeleteUsers(final String roleName,
                                   final Set<String> userNames) throws Exception;
+
   /**
    * Alter a given sentry role to grant a set of privileges.
    * Internally calls alterSentryRoleGrantPrivilege.
    *
-   * @param grantorPrincipal User name
    * @param roleName Role name
    * @param privileges Set of privileges
    * @throws Exception
    */
-  void alterSentryRoleGrantPrivileges(final String grantorPrincipal,
-                                      final String roleName,
+  void alterSentryRoleGrantPrivileges(final String roleName,
                                       final Set<TSentryPrivilege> privileges) throws Exception;
 
   /**
    * Alter a given sentry role to revoke a set of privileges.
    * Internally calls alterSentryRoleRevokePrivilege.
    *
-   * @param grantorPrincipal User name
    * @param roleName the given role name
    * @param tPrivileges a Set of privileges
    * @throws Exception
    *
    */
-  void alterSentryRoleRevokePrivileges(final String grantorPrincipal,
-                                       final String roleName,
+  void alterSentryRoleRevokePrivileges(final String roleName,
                                        final Set<TSentryPrivilege> tPrivileges)
     throws Exception;
 
@@ -515,33 +512,30 @@ public interface SentryStoreInterface {
    * corresponding permission change to MSentryPermChange table in a single transaction.
    * Internally calls alterSentryRoleGrantPrivilege.
    *
-   * @param grantorPrincipal User name
    * @param roleName the given role name
    * @param privileges a Set of privileges
    * @param privilegesUpdateMap the corresponding <privilege, DeltaTransactionBlock> map
    * @throws Exception
    *
    */
-  void alterSentryRoleGrantPrivileges(final String grantorPrincipal,
-                                      final String roleName,
+  void alterSentryRoleGrantPrivileges(final String roleName,
                                       final Set<TSentryPrivilege> privileges,
                                       final Map<TSentryPrivilege, Update> privilegesUpdateMap)
     throws Exception;
 
+
   /**
    * Alter a given sentry role to revoke a set of privileges, as well as persist the
    * corresponding permission change to MSentryPermChange table in a single transaction.
    * Internally calls alterSentryRoleRevokePrivilege.
    *
-   * @param grantorPrincipal User name
    * @param roleName the given role name
    * @param tPrivileges a Set of privileges
    * @param privilegesUpdateMap the corresponding <privilege, Update> map
    * @throws Exception
    *
    */
-  void alterSentryRoleRevokePrivileges(final String grantorPrincipal,
-                                       final String roleName, final Set<TSentryPrivilege> tPrivileges,
+  void alterSentryRoleRevokePrivileges(final String roleName, final Set<TSentryPrivilege> tPrivileges,
                                        final Map<TSentryPrivilege, Update> privilegesUpdateMap)
     throws Exception;
 

http://git-wip-us.apache.org/repos/asf/sentry/blob/8e050570/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/api/service/thrift/SentryPolicyStoreProcessorTestUtils.java
----------------------------------------------------------------------
diff --git a/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/api/service/thrift/SentryPolicyStoreProcessorTestUtils.java b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/api/service/thrift/SentryPolicyStoreProcessorTestUtils.java
new file mode 100644
index 0000000..7a48cca
--- /dev/null
+++ b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/api/service/thrift/SentryPolicyStoreProcessorTestUtils.java
@@ -0,0 +1,238 @@
+/**
+ * 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.api.service.thrift;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Collections;
+import java.util.Set;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.sentry.api.common.ApiConstants;
+import org.apache.sentry.api.common.ApiConstants.PrivilegeScope;
+import org.apache.sentry.api.common.Status;
+import org.apache.sentry.api.common.ThriftConstants;
+import org.apache.sentry.provider.db.service.persistent.SentryStoreInterface;
+import org.mockito.Mockito;
+
+// Test utils for the TestSentryPolicyStoreProcessor class
+class SentryPolicyStoreProcessorTestUtils {
+  // Constants variables
+  private static final int NO_TIME = 0;
+  private final Configuration conf;
+  private final SentryPolicyStoreProcessor processor;
+  private final SentryStoreInterface sentryStore;
+
+  SentryPolicyStoreProcessorTestUtils(Configuration conf, SentryStoreInterface sentryStore) throws Exception {
+    this.conf = conf;
+    this.sentryStore = sentryStore;
+    this.processor = new SentryPolicyStoreProcessor(
+      ApiConstants.SentryPolicyServiceConstants.SENTRY_POLICY_SERVICE_NAME, conf, sentryStore);
+  }
+
+  PolicyStoreProcessorTestVerifier givenUser(String userName) {
+    return new PolicyStoreProcessorTestVerifier(userName);
+  }
+
+  static TSentryPrivilege newPrivilegeOnDatabase(String action, String serverName, String dbName) {
+    TSentryPrivilege privilege = new TSentryPrivilege(){{
+      setPrivilegeScope(PrivilegeScope.DATABASE.toString());
+      setServerName(serverName);
+      setDbName(dbName);
+      setCreateTime(NO_TIME);
+      setAction(action);
+    }};
+
+    return privilege;
+  }
+
+  static TSentryPrivilege newPrivilegeOnDatabaseWithGrant(String action, String serverName, String dbName) {
+    TSentryPrivilege privilege = new TSentryPrivilege(){{
+      setPrivilegeScope(PrivilegeScope.DATABASE.toString());
+      setServerName(serverName);
+      setDbName(dbName);
+      setCreateTime(NO_TIME);
+      setAction(action);
+      setGrantOption(TSentryGrantOption.TRUE);
+    }};
+
+    return privilege;
+  }
+
+  static TSentryPrivilege newPrivilegeOnTable(String action, String serverName, String dbName, String tableName) {
+    TSentryPrivilege privilege = new TSentryPrivilege(){{
+      setPrivilegeScope(PrivilegeScope.TABLE.toString());
+      setServerName(serverName);
+      setDbName(dbName);
+      setTableName(tableName);
+      setCreateTime(NO_TIME);
+      setAction(action);
+    }};
+
+    return privilege;
+  }
+
+  static TSentryPrivilege newPrivilegeOnTableWithGrant(String action, String serverName, String dbName, String tableName) {
+    TSentryPrivilege privilege = new TSentryPrivilege(){{
+      setPrivilegeScope(PrivilegeScope.TABLE.toString());
+      setServerName(serverName);
+      setDbName(dbName);
+      setTableName(tableName);
+      setCreateTime(NO_TIME);
+      setAction(action);
+      setGrantOption(TSentryGrantOption.TRUE);
+    }};
+
+    return privilege;
+  }
+
+  static TSentryPrivilege newPrivilegeOnColumn(String action, String serverName, String dbName, String tableName, String column) {
+    TSentryPrivilege privilege = new TSentryPrivilege(){{
+      setPrivilegeScope(PrivilegeScope.COLUMN.toString());
+      setServerName(serverName);
+      setDbName(dbName);
+      setTableName(tableName);
+      setColumnName(column);
+      setCreateTime(NO_TIME);
+      setAction(action);
+      setGrantOption(TSentryGrantOption.FALSE);
+    }};
+
+    return privilege;
+  }
+
+  class PolicyStoreProcessorTestVerifier {
+    // Constant variables
+    private final String REQUESTOR_USER;
+
+    PolicyStoreProcessorTestVerifier(String requestorUser) {
+      this.REQUESTOR_USER = requestorUser;
+    }
+
+    PolicyGrantPrivilegeVerifier grantPrivilegeToRole(TSentryPrivilege privilege, String roleName) {
+      return new PolicyGrantPrivilegeVerifier(REQUESTOR_USER, privilege, roleName);
+    }
+
+    PolicyRevokePrivilegeVerifier revokePrivilegeFromRole(TSentryPrivilege privilege, String roleName) {
+      return new PolicyRevokePrivilegeVerifier(REQUESTOR_USER, privilege, roleName);
+    }
+  }
+
+  class PolicyRevokePrivilegeVerifier {
+    // Constant variables
+    private final String REQUESTOR_USER;
+    private final TSentryPrivilege privilege;
+
+    // Class variables
+    private String roleName;
+
+    PolicyRevokePrivilegeVerifier(String requestorUser, TSentryPrivilege privilege, String roleName) {
+      this.REQUESTOR_USER = requestorUser;
+      this.privilege = privilege;
+      this.roleName = roleName;
+    }
+
+    PolicyRevokePrivilegeVerifier whenRequestStorePrivilegesReturn(Set<TSentryPrivilege> privileges)
+      throws Exception {
+
+      Set<String> groups = SentryPolicyStoreProcessor.getGroupsFromUserName(conf, REQUESTOR_USER);
+      Mockito.when(sentryStore.listSentryPrivilegesByUsersAndGroups(
+        Mockito.eq(groups),
+        Mockito.eq(Collections.singleton(REQUESTOR_USER)),
+        Mockito.eq(Mockito.any()),
+        null)
+      ).thenReturn(privileges);
+
+      return this;
+    }
+
+    void verify(Status status) throws Exception {
+      TAlterSentryRoleRevokePrivilegeRequest revokeRequest = new TAlterSentryRoleRevokePrivilegeRequest();
+      revokeRequest.setProtocol_version(ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT);
+      revokeRequest.setRequestorUserName(REQUESTOR_USER);
+      revokeRequest.setRoleName(roleName);
+      revokeRequest.setPrivilege(privilege);
+
+      TAlterSentryRoleRevokePrivilegeResponse response =
+        processor.alter_sentry_role_revoke_privilege(revokeRequest);
+      if (response.getStatus().getValue() == Status.OK.getCode()) {
+        Mockito.verify(sentryStore).alterSentryRoleRevokePrivileges(revokeRequest.getRoleName(),
+          revokeRequest.getPrivileges());
+      } else {
+        Mockito.verify(sentryStore, Mockito.times(0))
+          .alterSentryRoleRevokePrivileges(Mockito.anyString(), Mockito.anySet());
+      }
+
+      assertEquals("Revoke " + privilege.getAction() + " response is not valid",
+        status.getCode(), response.getStatus().getValue());
+
+      Mockito.reset(sentryStore);
+    }
+  }
+
+  class PolicyGrantPrivilegeVerifier {
+    // Constant variables
+    private final String REQUESTOR_USER;
+    private final TSentryPrivilege privilege;
+
+    // Class variables
+    private String roleName;
+
+    PolicyGrantPrivilegeVerifier(String requestorUser, TSentryPrivilege privilege, String roleName) {
+      this.REQUESTOR_USER = requestorUser;
+      this.privilege = privilege;
+      this.roleName = roleName;
+    }
+
+    PolicyGrantPrivilegeVerifier whenRequestStorePrivilegesReturn(Set<TSentryPrivilege> privileges)
+      throws Exception {
+
+      Set<String> groups = SentryPolicyStoreProcessor.getGroupsFromUserName(conf, REQUESTOR_USER);
+      Mockito.when(sentryStore.listSentryPrivilegesByUsersAndGroups(
+          Mockito.eq(groups),
+          Mockito.eq(Collections.singleton(REQUESTOR_USER)),
+          Mockito.eq(Mockito.any()),
+          null)
+      ).thenReturn(privileges);
+
+      return this;
+    }
+
+    void verify(Status status) throws Exception {
+      TAlterSentryRoleGrantPrivilegeRequest grantRequest = new TAlterSentryRoleGrantPrivilegeRequest();
+      grantRequest.setProtocol_version(ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT);
+      grantRequest.setRequestorUserName(REQUESTOR_USER);
+      grantRequest.setRoleName(roleName);
+      grantRequest.setPrivilege(privilege);
+
+      TAlterSentryRoleGrantPrivilegeResponse response =
+        processor.alter_sentry_role_grant_privilege(grantRequest);
+      if (response.getStatus().getValue() == Status.OK.getCode()) {
+        Mockito.verify(sentryStore).alterSentryRoleGrantPrivileges(grantRequest.getRoleName(),
+          grantRequest.getPrivileges());
+      } else {
+        Mockito.verify(sentryStore, Mockito.times(0))
+          .alterSentryRoleGrantPrivileges(Mockito.anyString(), Mockito.anySet());
+      }
+
+      assertEquals("Grant " + privilege.getAction() + " response is not valid",
+        status.getCode(), response.getStatus().getValue());
+
+      Mockito.reset(sentryStore);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/8e050570/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 16ff79e..8cea339 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
@@ -17,6 +17,8 @@
  */
 package org.apache.sentry.api.service.thrift;
 
+import static org.apache.sentry.core.model.db.AccessConstants.ALL;
+import static org.apache.sentry.core.model.db.AccessConstants.SELECT;
 import static org.apache.sentry.service.common.ServiceConstants.ServerConfig.SENTRY_DB_POLICY_STORE_OWNER_AS_PRIVILEGE;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -672,4 +674,161 @@ public class TestSentryPolicyStoreProcessor {
     Assert.assertFalse("SELECT privileges should be permitted",
       response.getStatus().getMessage().contains("SELECT"));
   }
+
+  @Test
+  public void testGrantToRoleWithGrantCheck() throws Exception {
+    final String DB = "db";
+    final String ROLE = "role";
+    final String USER = "user";
+
+    SentryPolicyStoreProcessorTestUtils test = new SentryPolicyStoreProcessorTestUtils(conf, sentryStore);
+
+    final TSentryPrivilege ALL_ON_DB = test.newPrivilegeOnDatabase(ALL, SERVERNAME, DB);
+    final TSentryPrivilege ALL_ON_DB_WGRANT = test.newPrivilegeOnDatabaseWithGrant(ALL, SERVERNAME, DB);
+    final TSentryPrivilege SELECT_ON_DB = test.newPrivilegeOnDatabase(SELECT, SERVERNAME, DB);
+    final TSentryPrivilege SELECT_ON_DB_WGRANT = test.newPrivilegeOnDatabaseWithGrant(SELECT, SERVERNAME, DB);
+
+    // Admin user can grant privileges
+    test.givenUser(ADMIN_USER).grantPrivilegeToRole(ALL_ON_DB, ROLE)
+      .verify(Status.OK);
+    test.givenUser(ADMIN_USER).grantPrivilegeToRole(ALL_ON_DB_WGRANT, ROLE)
+      .verify(Status.OK);
+    test.givenUser(ADMIN_USER).grantPrivilegeToRole(SELECT_ON_DB, ROLE)
+      .verify(Status.OK);
+    test.givenUser(ADMIN_USER).grantPrivilegeToRole(SELECT_ON_DB_WGRANT, ROLE)
+      .verify(Status.OK);
+
+    // User without grant option cannot grant privileges
+    test.givenUser(USER).grantPrivilegeToRole(ALL_ON_DB, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.emptySet())
+      .verify(Status.ACCESS_DENIED);
+    test.givenUser(USER).grantPrivilegeToRole(ALL_ON_DB_WGRANT, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.emptySet())
+      .verify(Status.ACCESS_DENIED);
+    test.givenUser(USER).grantPrivilegeToRole(SELECT_ON_DB, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.emptySet())
+      .verify(Status.ACCESS_DENIED);
+    test.givenUser(USER).grantPrivilegeToRole(SELECT_ON_DB_WGRANT, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.emptySet())
+      .verify(Status.ACCESS_DENIED);
+    test.givenUser(USER).grantPrivilegeToRole(ALL_ON_DB, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.singleton(SELECT_ON_DB_WGRANT))
+      .verify(Status.ACCESS_DENIED);
+
+    // User with grant option can grant privileges
+    test.givenUser(USER).grantPrivilegeToRole(ALL_ON_DB, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.singleton(ALL_ON_DB_WGRANT))
+      .verify(Status.OK);
+    test.givenUser(USER).grantPrivilegeToRole(SELECT_ON_DB, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.singleton(ALL_ON_DB_WGRANT))
+      .verify(Status.OK);
+    test.givenUser(USER).grantPrivilegeToRole(SELECT_ON_DB, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.singleton(SELECT_ON_DB_WGRANT))
+      .verify(Status.OK);
+
+    // User with grant option can grant privileges with grant option
+    test.givenUser(USER).grantPrivilegeToRole(ALL_ON_DB_WGRANT, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.singleton(ALL_ON_DB_WGRANT))
+      .verify(Status.OK);
+    test.givenUser(USER).grantPrivilegeToRole(SELECT_ON_DB_WGRANT, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.singleton(ALL_ON_DB_WGRANT))
+      .verify(Status.OK);
+    test.givenUser(USER).grantPrivilegeToRole(SELECT_ON_DB_WGRANT, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.singleton(SELECT_ON_DB_WGRANT))
+      .verify(Status.OK);
+  }
+
+  @Test
+  public void testRevokeFromRoleWithGrantCheck() throws Exception {
+    final String DB = "db";
+    final String ROLE = "role";
+    final String USER = "user";
+
+    SentryPolicyStoreProcessorTestUtils test = new SentryPolicyStoreProcessorTestUtils(conf, sentryStore);
+
+    final TSentryPrivilege ALL_ON_DB = test.newPrivilegeOnDatabase(ALL, SERVERNAME, DB);
+    final TSentryPrivilege ALL_ON_DB_WGRANT = test.newPrivilegeOnDatabaseWithGrant(ALL, SERVERNAME, DB);
+    final TSentryPrivilege SELECT_ON_DB = test.newPrivilegeOnDatabase(SELECT, SERVERNAME, DB);
+    final TSentryPrivilege SELECT_ON_DB_WGRANT = test.newPrivilegeOnDatabaseWithGrant(SELECT, SERVERNAME, DB);
+
+    // Admin user can revoke privileges
+    test.givenUser(ADMIN_USER).revokePrivilegeFromRole(ALL_ON_DB, ROLE)
+      .verify(Status.OK);
+    test.givenUser(ADMIN_USER).revokePrivilegeFromRole(ALL_ON_DB_WGRANT, ROLE)
+      .verify(Status.OK);
+    test.givenUser(ADMIN_USER).revokePrivilegeFromRole(SELECT_ON_DB, ROLE)
+      .verify(Status.OK);
+    test.givenUser(ADMIN_USER).revokePrivilegeFromRole(SELECT_ON_DB_WGRANT, ROLE)
+      .verify(Status.OK);
+
+    // User without grant option cannot revoke privileges
+    test.givenUser(USER).revokePrivilegeFromRole(ALL_ON_DB, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.emptySet())
+      .verify(Status.ACCESS_DENIED);
+    test.givenUser(USER).revokePrivilegeFromRole(ALL_ON_DB_WGRANT, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.emptySet())
+      .verify(Status.ACCESS_DENIED);
+    test.givenUser(USER).revokePrivilegeFromRole(SELECT_ON_DB, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.emptySet())
+      .verify(Status.ACCESS_DENIED);
+    test.givenUser(USER).revokePrivilegeFromRole(SELECT_ON_DB_WGRANT, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.emptySet())
+      .verify(Status.ACCESS_DENIED);
+    test.givenUser(USER).revokePrivilegeFromRole(ALL_ON_DB, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.singleton(SELECT_ON_DB_WGRANT))
+      .verify(Status.ACCESS_DENIED);
+
+    // User with grant option can revoke privileges
+    test.givenUser(USER).revokePrivilegeFromRole(ALL_ON_DB, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.singleton(ALL_ON_DB_WGRANT))
+      .verify(Status.OK);
+    test.givenUser(USER).revokePrivilegeFromRole(SELECT_ON_DB, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.singleton(ALL_ON_DB_WGRANT))
+      .verify(Status.OK);
+    test.givenUser(USER).revokePrivilegeFromRole(SELECT_ON_DB, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.singleton(SELECT_ON_DB_WGRANT))
+      .verify(Status.OK);
+
+    // User with grant option can revoke privileges with grant option
+    test.givenUser(USER).revokePrivilegeFromRole(ALL_ON_DB_WGRANT, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.singleton(ALL_ON_DB_WGRANT))
+      .verify(Status.OK);
+    test.givenUser(USER).revokePrivilegeFromRole(SELECT_ON_DB_WGRANT, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.singleton(ALL_ON_DB_WGRANT))
+      .verify(Status.OK);
+    test.givenUser(USER).revokePrivilegeFromRole(SELECT_ON_DB_WGRANT, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.singleton(SELECT_ON_DB_WGRANT))
+      .verify(Status.OK);
+  }
+
+  @Test
+  public void testGrantCheckWithColumn() throws Exception {
+    final String DB = "db";
+    final String TABLE = "table";
+    final String COLUMN = "column";
+    final String ROLE = "role";
+    final String USER = "user";
+
+    SentryPolicyStoreProcessorTestUtils test = new SentryPolicyStoreProcessorTestUtils(conf, sentryStore);
+
+    final TSentryPrivilege SELECT_ON_TABLE = test.newPrivilegeOnTable(SELECT, SERVERNAME, DB, TABLE);
+    final TSentryPrivilege SELECT_ON_TABLE_WGRANT = test.newPrivilegeOnTableWithGrant(SELECT, SERVERNAME, DB, TABLE);
+    final TSentryPrivilege SELECT_ON_COLUMN = test.newPrivilegeOnColumn(SELECT, SERVERNAME, DB, TABLE, COLUMN);
+
+    // Admin user can revoke privileges
+    test.givenUser(ADMIN_USER).grantPrivilegeToRole(SELECT_ON_TABLE_WGRANT, ROLE)
+      .verify(Status.OK);
+    test.givenUser(ADMIN_USER).grantPrivilegeToRole(SELECT_ON_COLUMN, ROLE)
+      .verify(Status.OK);
+
+    // User with grant option on table can grant select on a column
+    test.givenUser(USER).grantPrivilegeToRole(SELECT_ON_COLUMN, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.singleton(SELECT_ON_TABLE_WGRANT))
+      .verify(Status.OK);
+
+    // User without grant option on table cannot grant select on a column
+    test.givenUser(USER).grantPrivilegeToRole(SELECT_ON_COLUMN, ROLE)
+      .whenRequestStorePrivilegesReturn(Collections.singleton(SELECT_ON_TABLE))
+      .verify(Status.ACCESS_DENIED);
+  }
 }

http://git-wip-us.apache.org/repos/asf/sentry/blob/8e050570/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/service/persistent/TestHMSFollowerSentryStoreIntegration.java
----------------------------------------------------------------------
diff --git a/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/service/persistent/TestHMSFollowerSentryStoreIntegration.java b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/service/persistent/TestHMSFollowerSentryStoreIntegration.java
index a886836..1d87b0b 100644
--- a/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/service/persistent/TestHMSFollowerSentryStoreIntegration.java
+++ b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/provider/db/service/persistent/TestHMSFollowerSentryStoreIntegration.java
@@ -154,7 +154,6 @@ public class TestHMSFollowerSentryStoreIntegration {
 
     // configure permission of the table
     String roleName1 = "list-privs-r1";
-    String grantor = "g1";
     sentryStore.createSentryRole(roleName1);
 
     TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
@@ -177,11 +176,11 @@ public class TestHMSFollowerSentryStoreIntegration {
     privilege_server.setServerName(serverName1);
     privilege_server.setCreateTime(System.currentTimeMillis());
 
-    sentryStore.alterSentryGrantPrivilege(grantor, ServiceConstants.SentryPrincipalType.ROLE, roleName1, privilege1, null);
+    sentryStore.alterSentryGrantPrivilege(ServiceConstants.SentryPrincipalType.ROLE, roleName1, privilege1, null);
 
-    sentryStore.alterSentryGrantPrivilege(grantor, ServiceConstants.SentryPrincipalType.ROLE, roleName1, privilege1_2, null);
-    sentryStore.alterSentryGrantPrivilege(grantor, ServiceConstants.SentryPrincipalType.ROLE, roleName1, privilege_server, null);
-    sentryStore.alterSentryGrantPrivilege(grantor, ServiceConstants.SentryPrincipalType.ROLE, roleName1, privilege1_3, null);
+    sentryStore.alterSentryGrantPrivilege(ServiceConstants.SentryPrincipalType.ROLE, roleName1, privilege1_2, null);
+    sentryStore.alterSentryGrantPrivilege(ServiceConstants.SentryPrincipalType.ROLE, roleName1, privilege_server, null);
+    sentryStore.alterSentryGrantPrivilege(ServiceConstants.SentryPrincipalType.ROLE, roleName1, privilege1_3, null);
 
     // Create notification events to drop the table
     StorageDescriptor sd = new StorageDescriptor();
@@ -214,7 +213,6 @@ public class TestHMSFollowerSentryStoreIntegration {
 
     // configure permission of the database
     String roleName1 = "list-privs-r1";
-    String grantor = "g1";
     sentryStore.createSentryRole(roleName1);
 
     TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
@@ -237,11 +235,11 @@ public class TestHMSFollowerSentryStoreIntegration {
     privilege_server.setServerName(serverName1);
     privilege_server.setCreateTime(System.currentTimeMillis());
 
-    sentryStore.alterSentryGrantPrivilege(grantor, ServiceConstants.SentryPrincipalType.ROLE, roleName1, privilege1, null);
+    sentryStore.alterSentryGrantPrivilege(ServiceConstants.SentryPrincipalType.ROLE, roleName1, privilege1, null);
 
-    sentryStore.alterSentryGrantPrivilege(grantor, ServiceConstants.SentryPrincipalType.ROLE, roleName1, privilege1_2, null);
-    sentryStore.alterSentryGrantPrivilege(grantor, ServiceConstants.SentryPrincipalType.ROLE, roleName1, privilege_server, null);
-    sentryStore.alterSentryGrantPrivilege(grantor, ServiceConstants.SentryPrincipalType.ROLE, roleName1, privilege1_3, null);
+    sentryStore.alterSentryGrantPrivilege(ServiceConstants.SentryPrincipalType.ROLE, roleName1, privilege1_2, null);
+    sentryStore.alterSentryGrantPrivilege(ServiceConstants.SentryPrincipalType.ROLE, roleName1, privilege_server, null);
+    sentryStore.alterSentryGrantPrivilege(ServiceConstants.SentryPrincipalType.ROLE, roleName1, privilege1_3, null);
 
     // Create notification events to drop the database
     NotificationEvent notificationEvent = new NotificationEvent(1, 0, EventType.DROP_DATABASE.toString(),