sentry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From s..@apache.org
Subject [2/6] sentry git commit: SENTRY-711: Implement grant user to role (Colin Ma, reviewed by Dapeng Sun)
Date Thu, 14 Apr 2016 01:57:58 GMT
http://git-wip-us.apache.org/repos/asf/sentry/blob/68949951/sentry-provider/sentry-provider-db/src/gen/thrift/gen-javabean/org/apache/sentry/provider/db/service/thrift/TSentryPrivilegeMap.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/gen/thrift/gen-javabean/org/apache/sentry/provider/db/service/thrift/TSentryPrivilegeMap.java b/sentry-provider/sentry-provider-db/src/gen/thrift/gen-javabean/org/apache/sentry/provider/db/service/thrift/TSentryPrivilegeMap.java
index e7beee1..3d85d9f 100644
--- a/sentry-provider/sentry-provider-db/src/gen/thrift/gen-javabean/org/apache/sentry/provider/db/service/thrift/TSentryPrivilegeMap.java
+++ b/sentry-provider/sentry-provider-db/src/gen/thrift/gen-javabean/org/apache/sentry/provider/db/service/thrift/TSentryPrivilegeMap.java
@@ -359,26 +359,26 @@ public class TSentryPrivilegeMap implements org.apache.thrift.TBase<TSentryPrivi
           case 1: // PRIVILEGE_MAP
             if (schemeField.type == org.apache.thrift.protocol.TType.MAP) {
               {
-                org.apache.thrift.protocol.TMap _map88 = iprot.readMapBegin();
-                struct.privilegeMap = new HashMap<String,Set<TSentryPrivilege>>(2*_map88.size);
-                for (int _i89 = 0; _i89 < _map88.size; ++_i89)
+                org.apache.thrift.protocol.TMap _map112 = iprot.readMapBegin();
+                struct.privilegeMap = new HashMap<String,Set<TSentryPrivilege>>(2*_map112.size);
+                for (int _i113 = 0; _i113 < _map112.size; ++_i113)
                 {
-                  String _key90; // required
-                  Set<TSentryPrivilege> _val91; // required
-                  _key90 = iprot.readString();
+                  String _key114; // required
+                  Set<TSentryPrivilege> _val115; // required
+                  _key114 = iprot.readString();
                   {
-                    org.apache.thrift.protocol.TSet _set92 = iprot.readSetBegin();
-                    _val91 = new HashSet<TSentryPrivilege>(2*_set92.size);
-                    for (int _i93 = 0; _i93 < _set92.size; ++_i93)
+                    org.apache.thrift.protocol.TSet _set116 = iprot.readSetBegin();
+                    _val115 = new HashSet<TSentryPrivilege>(2*_set116.size);
+                    for (int _i117 = 0; _i117 < _set116.size; ++_i117)
                     {
-                      TSentryPrivilege _elem94; // required
-                      _elem94 = new TSentryPrivilege();
-                      _elem94.read(iprot);
-                      _val91.add(_elem94);
+                      TSentryPrivilege _elem118; // required
+                      _elem118 = new TSentryPrivilege();
+                      _elem118.read(iprot);
+                      _val115.add(_elem118);
                     }
                     iprot.readSetEnd();
                   }
-                  struct.privilegeMap.put(_key90, _val91);
+                  struct.privilegeMap.put(_key114, _val115);
                 }
                 iprot.readMapEnd();
               }
@@ -404,14 +404,14 @@ public class TSentryPrivilegeMap implements org.apache.thrift.TBase<TSentryPrivi
         oprot.writeFieldBegin(PRIVILEGE_MAP_FIELD_DESC);
         {
           oprot.writeMapBegin(new org.apache.thrift.protocol.TMap(org.apache.thrift.protocol.TType.STRING, org.apache.thrift.protocol.TType.SET, struct.privilegeMap.size()));
-          for (Map.Entry<String, Set<TSentryPrivilege>> _iter95 : struct.privilegeMap.entrySet())
+          for (Map.Entry<String, Set<TSentryPrivilege>> _iter119 : struct.privilegeMap.entrySet())
           {
-            oprot.writeString(_iter95.getKey());
+            oprot.writeString(_iter119.getKey());
             {
-              oprot.writeSetBegin(new org.apache.thrift.protocol.TSet(org.apache.thrift.protocol.TType.STRUCT, _iter95.getValue().size()));
-              for (TSentryPrivilege _iter96 : _iter95.getValue())
+              oprot.writeSetBegin(new org.apache.thrift.protocol.TSet(org.apache.thrift.protocol.TType.STRUCT, _iter119.getValue().size()));
+              for (TSentryPrivilege _iter120 : _iter119.getValue())
               {
-                _iter96.write(oprot);
+                _iter120.write(oprot);
               }
               oprot.writeSetEnd();
             }
@@ -439,14 +439,14 @@ public class TSentryPrivilegeMap implements org.apache.thrift.TBase<TSentryPrivi
       TTupleProtocol oprot = (TTupleProtocol) prot;
       {
         oprot.writeI32(struct.privilegeMap.size());
-        for (Map.Entry<String, Set<TSentryPrivilege>> _iter97 : struct.privilegeMap.entrySet())
+        for (Map.Entry<String, Set<TSentryPrivilege>> _iter121 : struct.privilegeMap.entrySet())
         {
-          oprot.writeString(_iter97.getKey());
+          oprot.writeString(_iter121.getKey());
           {
-            oprot.writeI32(_iter97.getValue().size());
-            for (TSentryPrivilege _iter98 : _iter97.getValue())
+            oprot.writeI32(_iter121.getValue().size());
+            for (TSentryPrivilege _iter122 : _iter121.getValue())
             {
-              _iter98.write(oprot);
+              _iter122.write(oprot);
             }
           }
         }
@@ -457,25 +457,25 @@ public class TSentryPrivilegeMap implements org.apache.thrift.TBase<TSentryPrivi
     public void read(org.apache.thrift.protocol.TProtocol prot, TSentryPrivilegeMap struct) throws org.apache.thrift.TException {
       TTupleProtocol iprot = (TTupleProtocol) prot;
       {
-        org.apache.thrift.protocol.TMap _map99 = new org.apache.thrift.protocol.TMap(org.apache.thrift.protocol.TType.STRING, org.apache.thrift.protocol.TType.SET, iprot.readI32());
-        struct.privilegeMap = new HashMap<String,Set<TSentryPrivilege>>(2*_map99.size);
-        for (int _i100 = 0; _i100 < _map99.size; ++_i100)
+        org.apache.thrift.protocol.TMap _map123 = new org.apache.thrift.protocol.TMap(org.apache.thrift.protocol.TType.STRING, org.apache.thrift.protocol.TType.SET, iprot.readI32());
+        struct.privilegeMap = new HashMap<String,Set<TSentryPrivilege>>(2*_map123.size);
+        for (int _i124 = 0; _i124 < _map123.size; ++_i124)
         {
-          String _key101; // required
-          Set<TSentryPrivilege> _val102; // required
-          _key101 = iprot.readString();
+          String _key125; // required
+          Set<TSentryPrivilege> _val126; // required
+          _key125 = iprot.readString();
           {
-            org.apache.thrift.protocol.TSet _set103 = new org.apache.thrift.protocol.TSet(org.apache.thrift.protocol.TType.STRUCT, iprot.readI32());
-            _val102 = new HashSet<TSentryPrivilege>(2*_set103.size);
-            for (int _i104 = 0; _i104 < _set103.size; ++_i104)
+            org.apache.thrift.protocol.TSet _set127 = new org.apache.thrift.protocol.TSet(org.apache.thrift.protocol.TType.STRUCT, iprot.readI32());
+            _val126 = new HashSet<TSentryPrivilege>(2*_set127.size);
+            for (int _i128 = 0; _i128 < _set127.size; ++_i128)
             {
-              TSentryPrivilege _elem105; // required
-              _elem105 = new TSentryPrivilege();
-              _elem105.read(iprot);
-              _val102.add(_elem105);
+              TSentryPrivilege _elem129; // required
+              _elem129 = new TSentryPrivilege();
+              _elem129.read(iprot);
+              _val126.add(_elem129);
             }
           }
-          struct.privilegeMap.put(_key101, _val102);
+          struct.privilegeMap.put(_key125, _val126);
         }
       }
       struct.setPrivilegeMapIsSet(true);

http://git-wip-us.apache.org/repos/asf/sentry/blob/68949951/sentry-provider/sentry-provider-db/src/gen/thrift/gen-javabean/org/apache/sentry/provider/db/service/thrift/TSentryRole.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/gen/thrift/gen-javabean/org/apache/sentry/provider/db/service/thrift/TSentryRole.java b/sentry-provider/sentry-provider-db/src/gen/thrift/gen-javabean/org/apache/sentry/provider/db/service/thrift/TSentryRole.java
index 7645e25..0faa12a 100644
--- a/sentry-provider/sentry-provider-db/src/gen/thrift/gen-javabean/org/apache/sentry/provider/db/service/thrift/TSentryRole.java
+++ b/sentry-provider/sentry-provider-db/src/gen/thrift/gen-javabean/org/apache/sentry/provider/db/service/thrift/TSentryRole.java
@@ -528,14 +528,14 @@ public class TSentryRole implements org.apache.thrift.TBase<TSentryRole, TSentry
           case 2: // GROUPS
             if (schemeField.type == org.apache.thrift.protocol.TType.SET) {
               {
-                org.apache.thrift.protocol.TSet _set40 = iprot.readSetBegin();
-                struct.groups = new HashSet<TSentryGroup>(2*_set40.size);
-                for (int _i41 = 0; _i41 < _set40.size; ++_i41)
+                org.apache.thrift.protocol.TSet _set56 = iprot.readSetBegin();
+                struct.groups = new HashSet<TSentryGroup>(2*_set56.size);
+                for (int _i57 = 0; _i57 < _set56.size; ++_i57)
                 {
-                  TSentryGroup _elem42; // required
-                  _elem42 = new TSentryGroup();
-                  _elem42.read(iprot);
-                  struct.groups.add(_elem42);
+                  TSentryGroup _elem58; // required
+                  _elem58 = new TSentryGroup();
+                  _elem58.read(iprot);
+                  struct.groups.add(_elem58);
                 }
                 iprot.readSetEnd();
               }
@@ -574,9 +574,9 @@ public class TSentryRole implements org.apache.thrift.TBase<TSentryRole, TSentry
         oprot.writeFieldBegin(GROUPS_FIELD_DESC);
         {
           oprot.writeSetBegin(new org.apache.thrift.protocol.TSet(org.apache.thrift.protocol.TType.STRUCT, struct.groups.size()));
-          for (TSentryGroup _iter43 : struct.groups)
+          for (TSentryGroup _iter59 : struct.groups)
           {
-            _iter43.write(oprot);
+            _iter59.write(oprot);
           }
           oprot.writeSetEnd();
         }
@@ -607,9 +607,9 @@ public class TSentryRole implements org.apache.thrift.TBase<TSentryRole, TSentry
       oprot.writeString(struct.roleName);
       {
         oprot.writeI32(struct.groups.size());
-        for (TSentryGroup _iter44 : struct.groups)
+        for (TSentryGroup _iter60 : struct.groups)
         {
-          _iter44.write(oprot);
+          _iter60.write(oprot);
         }
       }
       oprot.writeString(struct.grantorPrincipal);
@@ -621,14 +621,14 @@ public class TSentryRole implements org.apache.thrift.TBase<TSentryRole, TSentry
       struct.roleName = iprot.readString();
       struct.setRoleNameIsSet(true);
       {
-        org.apache.thrift.protocol.TSet _set45 = new org.apache.thrift.protocol.TSet(org.apache.thrift.protocol.TType.STRUCT, iprot.readI32());
-        struct.groups = new HashSet<TSentryGroup>(2*_set45.size);
-        for (int _i46 = 0; _i46 < _set45.size; ++_i46)
+        org.apache.thrift.protocol.TSet _set61 = new org.apache.thrift.protocol.TSet(org.apache.thrift.protocol.TType.STRUCT, iprot.readI32());
+        struct.groups = new HashSet<TSentryGroup>(2*_set61.size);
+        for (int _i62 = 0; _i62 < _set61.size; ++_i62)
         {
-          TSentryGroup _elem47; // required
-          _elem47 = new TSentryGroup();
-          _elem47.read(iprot);
-          struct.groups.add(_elem47);
+          TSentryGroup _elem63; // required
+          _elem63 = new TSentryGroup();
+          _elem63.read(iprot);
+          struct.groups.add(_elem63);
         }
       }
       struct.setGroupsIsSet(true);

http://git-wip-us.apache.org/repos/asf/sentry/blob/68949951/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/SimpleDBProviderBackend.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/SimpleDBProviderBackend.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/SimpleDBProviderBackend.java
index b996095..2f5aa9d 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/SimpleDBProviderBackend.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/SimpleDBProviderBackend.java
@@ -63,14 +63,25 @@ public class SimpleDBProviderBackend implements ProviderBackend {
    * {@inheritDoc}
    */
   @Override
-  public ImmutableSet<String> getPrivileges(Set<String> groups, ActiveRoleSet roleSet, Authorizable... authorizableHierarchy) {
+  public ImmutableSet<String> getPrivileges(Set<String> groups, ActiveRoleSet roleSet,
+      Authorizable... authorizableHierarchy) {
+    return getPrivileges(groups, null, roleSet, authorizableHierarchy);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public ImmutableSet<String> getPrivileges(Set<String> groups, Set<String> users,
+      ActiveRoleSet roleSet, Authorizable... authorizableHierarchy) {
     int retries = Math.max(retryCount + 1, 1); // if customer configs retryCount as Integer.MAX_VALUE, try only once
     while (retries > 0) {
       retries--;
       SentryPolicyServiceClient policyServiceClient = null;
       try {
         policyServiceClient = SentryServiceClientFactory.create(conf);
-        return ImmutableSet.copyOf(policyServiceClient.listPrivilegesForProvider(groups, roleSet, authorizableHierarchy));
+        return ImmutableSet.copyOf(policyServiceClient.listPrivilegesForProvider(groups, users,
+            roleSet, authorizableHierarchy));
       } catch (Exception e) {
         //TODO: differentiate transient errors and permanent errors
         String msg = "Unable to obtain privileges from server: " + e.getMessage() + ".";

http://git-wip-us.apache.org/repos/asf/sentry/blob/68949951/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/SentryGenericProviderBackend.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/SentryGenericProviderBackend.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/SentryGenericProviderBackend.java
index 474d05c..222b6fd 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/SentryGenericProviderBackend.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/SentryGenericProviderBackend.java
@@ -140,6 +140,13 @@ public class SentryGenericProviderBackend implements ProviderBackend {
   }
 
   @Override
+  public ImmutableSet<String> getPrivileges(Set<String> groups, Set<String> users,
+                                              ActiveRoleSet roleSet, Authorizable... authorizableHierarchy) {
+    // SentryGenericProviderBackend doesn't support getPrivileges for user now.
+    return getPrivileges(groups, roleSet, authorizableHierarchy);
+  }
+
+  @Override
   public void close() {
   }
 

http://git-wip-us.apache.org/repos/asf/sentry/blob/68949951/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/log/entity/JsonLogEntityFactory.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/log/entity/JsonLogEntityFactory.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/log/entity/JsonLogEntityFactory.java
index c29b88e..71402d3 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/log/entity/JsonLogEntityFactory.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/log/entity/JsonLogEntityFactory.java
@@ -30,8 +30,12 @@ import org.apache.sentry.provider.db.log.util.CommandUtil;
 import org.apache.sentry.provider.db.log.util.Constants;
 import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleAddGroupsRequest;
 import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleAddGroupsResponse;
+import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleAddUsersRequest;
+import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleAddUsersResponse;
 import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleDeleteGroupsRequest;
 import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleDeleteGroupsResponse;
+import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleDeleteUsersRequest;
+import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleDeleteUsersResponse;
 import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleGrantPrivilegeRequest;
 import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleGrantPrivilegeResponse;
 import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleRevokePrivilegeRequest;
@@ -176,6 +180,42 @@ public class JsonLogEntityFactory {
     return groups.toString();
   }
 
+  public JsonLogEntity createJsonLogEntity(TAlterSentryRoleAddUsersRequest request,
+      TAlterSentryRoleAddUsersResponse response, Configuration conf) {
+    AuditMetadataLogEntity amle = createCommonHAMLE(conf, response.getStatus(),
+        request.getRequestorUserName(), request.getClass().getName());
+    String users = getUsersStr(request.getUsersIterator());
+    amle.setOperationText(CommandUtil.createCmdForRoleAddUser(request.getRoleName(), users));
+
+    return amle;
+  }
+
+  public JsonLogEntity createJsonLogEntity(TAlterSentryRoleDeleteUsersRequest request,
+      TAlterSentryRoleDeleteUsersResponse response, Configuration conf) {
+    AuditMetadataLogEntity amle = createCommonHAMLE(conf, response.getStatus(),
+        request.getRequestorUserName(), request.getClass().getName());
+    String users = getUsersStr(request.getUsersIterator());
+    amle.setOperationText(CommandUtil.createCmdForRoleDeleteUser(request.getRoleName(), users));
+
+    return amle;
+  }
+
+  private String getUsersStr(Iterator<String> iter) {
+    StringBuilder users = new StringBuilder("");
+    if (iter != null) {
+      boolean commaFlg = false;
+      while (iter.hasNext()) {
+        if (commaFlg) {
+          users.append(", ");
+        } else {
+          commaFlg = true;
+        }
+        users.append(iter.next());
+      }
+    }
+    return users.toString();
+  }
+
   public String isAllowed(TSentryResponseStatus status) {
     if (status.equals(Status.OK())) {
       return Constants.TRUE;

http://git-wip-us.apache.org/repos/asf/sentry/blob/68949951/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/log/util/CommandUtil.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/log/util/CommandUtil.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/log/util/CommandUtil.java
index d6aecd1..c2c8e4e 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/log/util/CommandUtil.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/log/util/CommandUtil.java
@@ -46,38 +46,46 @@ public class CommandUtil {
   }
 
   public static String createCmdForRoleAddGroup(String roleName, String groups) {
-    return createCmdForRoleAddOrDeleteGroup(roleName, groups, true);
+    return createCmdForRoleGrant(roleName, groups, true, true);
   }
 
   public static String createCmdForRoleDeleteGroup(String roleName, String groups) {
-    return createCmdForRoleAddOrDeleteGroup(roleName, groups, false);
+    return createCmdForRoleGrant(roleName, groups, false, true);
   }
 
-  private static String createCmdForRoleAddOrDeleteGroup(String roleName,
- String groups,
-      boolean isAddGroup) {
+  private static String createCmdForRoleGrant(String roleName, String principals,
+      boolean isGrant, boolean isGroup) {
     StringBuilder sb = new StringBuilder();
-    if (isAddGroup) {
+    if (isGrant) {
       sb.append("GRANT ROLE ");
     } else {
       sb.append("REVOKE ROLE ");
     }
     sb.append(roleName);
-    if (isAddGroup) {
+    if (isGrant) {
       sb.append(" TO ");
     } else {
       sb.append(" FROM ");
     }
 
-    if (!StringUtils.isEmpty(groups)) {
-      sb.append("GROUP ").append(groups);
+    String principalType = isGroup ? "GROUP" : "USER";
+    if (!StringUtils.isEmpty(principals)) {
+      sb.append(principalType).append(" ").append(principals);
     } else {
-      sb = new StringBuilder("Missing group information.");
+      sb = new StringBuilder("Missing " + principalType + " information.");
     }
 
     return sb.toString();
   }
 
+  public static String createCmdForRoleAddUser(String roleName, String users) {
+    return createCmdForRoleGrant(roleName, users, true, false);
+  }
+
+  public static String createCmdForRoleDeleteUser(String roleName, String users) {
+    return createCmdForRoleGrant(roleName, users, false, false);
+  }
+
   public static String createCmdForGrantPrivilege(
       TAlterSentryRoleGrantPrivilegeRequest request) {
     return createCmdForGrantOrRevokePrivileges(request.getRoleName(),

http://git-wip-us.apache.org/repos/asf/sentry/blob/68949951/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/log/util/Constants.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/log/util/Constants.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/log/util/Constants.java
index b0a87ae..1a470ab 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/log/util/Constants.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/log/util/Constants.java
@@ -21,12 +21,7 @@ package org.apache.sentry.provider.db.log.util;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleAddGroupsRequest;
-import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleDeleteGroupsRequest;
-import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleGrantPrivilegeRequest;
-import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleRevokePrivilegeRequest;
-import org.apache.sentry.provider.db.service.thrift.TCreateSentryRoleRequest;
-import org.apache.sentry.provider.db.service.thrift.TDropSentryRoleRequest;
+import org.apache.sentry.provider.db.service.thrift.*;
 
 public class Constants {
   public final static String AUDIT_LOGGER_NAME = "sentry.hive.authorization.ddl.logger";
@@ -51,6 +46,8 @@ public class Constants {
   public final static String OPERATION_DROP_ROLE = "DROP_ROLE";
   public final static String OPERATION_ADD_ROLE = "ADD_ROLE_TO_GROUP";
   public final static String OPERATION_DELETE_ROLE = "DELETE_ROLE_FROM_GROUP";
+  public final static String OPERATION_ADD_ROLE_USER = "ADD_ROLE_TO_USER";
+  public final static String OPERATION_DELETE_ROLE_USER = "DELETE_ROLE_FROM_USER";
   public final static String OPERATION_GRANT_PRIVILEGE = "GRANT_PRIVILEGE";
   public final static String OPERATION_REVOKE_PRIVILEGE = "REVOKE_PRIVILEGE";
 
@@ -81,6 +78,13 @@ public class Constants {
     requestTypeToOperationMap.put(
         TAlterSentryRoleDeleteGroupsRequest.class.getName(),
         Constants.OPERATION_DELETE_ROLE);
+    requestTypeToOperationMap.put(
+        TAlterSentryRoleAddUsersRequest.class.getName(),
+        Constants.OPERATION_ADD_ROLE_USER);
+    requestTypeToOperationMap.put(
+        TAlterSentryRoleDeleteUsersRequest.class.getName(),
+        Constants.OPERATION_DELETE_ROLE_USER);
+
     // for generic model audit log
     requestTypeToOperationMap.put(
         org.apache.sentry.provider.db.generic.service.thrift.TCreateSentryRoleRequest.class
@@ -116,6 +120,12 @@ public class Constants {
         TAlterSentryRoleDeleteGroupsRequest.class.getName(),
         Constants.OBJECT_TYPE_ROLE);
     requestTypeToObjectTypeMap.put(
+        TAlterSentryRoleAddUsersRequest.class.getName(),
+        Constants.OBJECT_TYPE_ROLE);
+    requestTypeToObjectTypeMap.put(
+        TAlterSentryRoleDeleteUsersRequest.class.getName(),
+        Constants.OBJECT_TYPE_ROLE);
+    requestTypeToObjectTypeMap.put(
         TAlterSentryRoleGrantPrivilegeRequest.class.getName(),
         Constants.OBJECT_TYPE_PRINCIPAL);
     requestTypeToObjectTypeMap.put(

http://git-wip-us.apache.org/repos/asf/sentry/blob/68949951/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryRole.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryRole.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryRole.java
index 24514ea..0484eaa 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryRole.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryRole.java
@@ -41,6 +41,8 @@ public class MSentryRole {
 
   // set of groups this role belongs to
   private Set<MSentryGroup> groups;
+  // set of users this role belongs to
+  private Set<MSentryUser> users;
   private long createTime;
 
   public MSentryRole(String roleName, long createTime) {
@@ -49,6 +51,7 @@ public class MSentryRole {
     privileges = new HashSet<MSentryPrivilege>();
     gmPrivileges = new HashSet<MSentryGMPrivilege>();
     groups = new HashSet<MSentryGroup>();
+    users = new HashSet<MSentryUser>();
   }
 
   public long getCreateTime() {
@@ -91,6 +94,14 @@ public class MSentryRole {
     return groups;
   }
 
+  public Set<MSentryUser> getUsers() {
+    return users;
+  }
+
+  public void setUsers(Set<MSentryUser> users) {
+    this.users = users;
+  }
+
   public void removePrivilege(MSentryPrivilege privilege) {
     if (privileges.remove(privilege)) {
       privilege.removeRole(this);
@@ -142,6 +153,22 @@ public class MSentryRole {
     }
   }
 
+  public void appendUsers(Set<MSentryUser> users) {
+    this.users.addAll(users);
+  }
+
+  public void appendUser(MSentryUser user) {
+    if (users.add(user)) {
+      user.appendRole(this);
+    }
+  }
+
+  public void removeUser(MSentryUser user) {
+    if (users.remove(user)) {
+      user.removeRole(this);
+    }
+  }
+
   public void removePrivileges() {
     // copy is required since privilege.removeRole will call remotePrivilege
     for (MSentryPrivilege privilege : ImmutableSet.copyOf(privileges)) {
@@ -153,7 +180,7 @@ public class MSentryRole {
   @Override
   public String toString() {
     return "MSentryRole [roleName=" + roleName + ", privileges=[..]" + ", gmPrivileges=[..]"
-        + ", groups=[...]" + ", createTime=" + createTime + "]";
+        + ", groups=[...]" + ", users=[...]" + ", createTime=" + createTime + "]";
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/sentry/blob/68949951/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryUser.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryUser.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryUser.java
new file mode 100644
index 0000000..ff57249
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryUser.java
@@ -0,0 +1,116 @@
+/**
+ * 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.provider.db.service.model;
+
+import java.util.Set;
+
+import javax.jdo.annotations.PersistenceCapable;
+
+/**
+ * Database backed Sentry User. Any changes to this object
+ * require re-running the maven build so DN an re-enhance.
+ */
+@PersistenceCapable
+public class MSentryUser {
+
+  /**
+   * User name is unique
+   */
+  private String userName;
+  // set of roles granted to this user
+  private Set<MSentryRole> roles;
+  private long createTime;
+
+  public MSentryUser(String userName, long createTime, Set<MSentryRole> roles) {
+    this.setUserName(userName);
+    this.createTime = createTime;
+    this.roles = roles;
+  }
+
+  public long getCreateTime() {
+    return createTime;
+  }
+
+  public void setCreateTime(long createTime) {
+    this.createTime = createTime;
+  }
+
+  public Set<MSentryRole> getRoles() {
+    return roles;
+  }
+
+  public String getUserName() {
+    return userName;
+  }
+
+  public void setUserName(String userName) {
+    this.userName = userName;
+  }
+
+  public void appendRole(MSentryRole role) {
+    if (roles.add(role)) {
+      role.appendUser(this);
+    }
+  }
+
+  public void removeRole(MSentryRole role) {
+    if (roles.remove(role)) {
+      role.removeUser(this);
+    }
+  }
+
+  @Override
+  public String toString() {
+    return "MSentryUser [userName=" + userName + ", roles=[...]" + ", createTime=" + createTime
+        + "]";
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + ((userName == null) ? 0 : userName.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;
+    }
+    MSentryUser other = (MSentryUser) obj;
+    if (createTime != other.createTime) {
+      return false;
+    }
+    if (userName == null) {
+      if (other.userName != null) {
+        return false;
+      }
+    } else if (!userName.equals(other.userName)) {
+      return false;
+    }
+    return true;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/sentry/blob/68949951/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo
index d8f69b5..b3b9494 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo
@@ -7,15 +7,15 @@
   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.
---> 
+-->
 <!DOCTYPE jdo PUBLIC "-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN"
   "http://java.sun.com/dtd/jdo_2_0.dtd">
 <!--
@@ -25,36 +25,54 @@
   Non-indexed VARCHAR: 4000 bytes (max length on Oracle 9i/10g/11g)
 
 -->
-<jdo>  
-  <package name="org.apache.sentry.provider.db.service.model">  
-    <class name="MSentryGroup" identity-type="datastore" table="SENTRY_GROUP" detachable="true">  
+<jdo>
+  <package name="org.apache.sentry.provider.db.service.model">
+    <class name="MSentryGroup" identity-type="datastore" table="SENTRY_GROUP" detachable="true">
       <datastore-identity>
         <column name="GROUP_ID"/>
       </datastore-identity>
-      <field name="groupName">  
+      <field name="groupName">
         <column name="GROUP_NAME" length="128" jdbc-type="VARCHAR"/>
         <index name="SentryGroupName" unique="true"/>
       </field>
       <field name = "createTime">
-      	<column name = "CREATE_TIME" jdbc-type="BIGINT"/>
+        <column name = "CREATE_TIME" jdbc-type="BIGINT"/>
       </field>
 
       <field name="roles" mapped-by="groups">
          <collection element-type="org.apache.sentry.provider.db.service.model.MSentryRole"/>
       </field>
-        
+
+    </class>
+
+    <class name="MSentryUser" identity-type="datastore" table="SENTRY_USER" detachable="true">
+      <datastore-identity>
+        <column name="USER_ID"/>
+      </datastore-identity>
+      <field name="userName">
+        <column name="USER_NAME" length="128" jdbc-type="VARCHAR"/>
+        <index name="SentryUserName" unique="true"/>
+      </field>
+      <field name = "createTime">
+        <column name = "CREATE_TIME" jdbc-type="BIGINT"/>
+      </field>
+
+      <field name="roles" mapped-by="users">
+         <collection element-type="org.apache.sentry.provider.db.service.model.MSentryRole"/>
+      </field>
+
     </class>
-    
-    <class name="MSentryRole" identity-type="datastore" table="SENTRY_ROLE" detachable="true">  
+
+    <class name="MSentryRole" identity-type="datastore" table="SENTRY_ROLE" detachable="true">
       <datastore-identity>
         <column name="ROLE_ID"/>
       </datastore-identity>
-      <field name="roleName">  
+      <field name="roleName">
         <column name="ROLE_NAME" length="128" jdbc-type="VARCHAR"/>
         <index name="SentryRoleName" unique="true"/>
       </field>
       <field name = "createTime">
-      	<column name = "CREATE_TIME" jdbc-type="BIGINT"/>
+        <column name = "CREATE_TIME" jdbc-type="BIGINT"/>
       </field>
       <field name = "privileges" table="SENTRY_ROLE_DB_PRIVILEGE_MAP" default-fetch-group="true">
         <collection element-type="org.apache.sentry.provider.db.service.model.MSentryPrivilege"/>
@@ -76,7 +94,7 @@
             </element>
       </field>
 
-      <field name = "groups" table="SENTRY_ROLE_GROUP_MAP" >
+      <field name = "groups" table="SENTRY_ROLE_GROUP_MAP" default-fetch-group="true">
         <collection element-type="org.apache.sentry.provider.db.service.model.MSentryGroup"/>
             <join>
                 <column name="ROLE_ID"/>
@@ -85,13 +103,23 @@
                 <column name="GROUP_ID"/>
             </element>
       </field>
+
+      <field name = "users" table="SENTRY_ROLE_USER_MAP" default-fetch-group="true">
+        <collection element-type="org.apache.sentry.provider.db.service.model.MSentryUser"/>
+            <join>
+                <column name="ROLE_ID"/>
+            </join>
+            <element>
+                <column name="USER_ID"/>
+            </element>
+      </field>
     </class>
 
     <class name="MSentryPrivilege" identity-type="datastore" table="SENTRY_DB_PRIVILEGE" detachable="true">
       <datastore-identity>
         <column name="DB_PRIVILEGE_ID"/>
       </datastore-identity>
-    <index name="PRIVILEGE_INDEX" unique="true">
+      <index name="PRIVILEGE_INDEX" unique="true">
         <field name="serverName"/>
         <field name="dbName"/>
         <field name="tableName"/>
@@ -99,30 +127,30 @@
         <field name="URI"/>
         <field name="action"/>
         <field name="grantOption"/>
-	  </index>
-      <field name="privilegeScope">  
+      </index>
+      <field name="privilegeScope">
         <column name="PRIVILEGE_SCOPE" length="40" jdbc-type="VARCHAR"/>
       </field>
-      <field name="serverName">  
+      <field name="serverName">
         <column name="SERVER_NAME" length="4000" jdbc-type="VARCHAR"/>
       </field>
-      <field name="dbName">  
+      <field name="dbName">
         <column name="DB_NAME" length="4000" jdbc-type="VARCHAR"/>
       </field>
-      <field name="tableName">  
+      <field name="tableName">
         <column name="TABLE_NAME" length="4000" jdbc-type="VARCHAR"/>
       </field>
       <field name="columnName">
         <column name="COLUMN_NAME" length="4000" jdbc-type="VARCHAR"/>
       </field>
-      <field name="URI">  
+      <field name="URI">
         <column name="URI" length="4000" jdbc-type="VARCHAR"/>
       </field>
-      <field name="action">  
+      <field name="action">
         <column name="ACTION" length="40" jdbc-type="VARCHAR"/>
       </field>
       <field name = "createTime">
-      	<column name = "CREATE_TIME" jdbc-type="BIGINT"/>
+        <column name = "CREATE_TIME" jdbc-type="BIGINT"/>
       </field>
       <field name="grantOption">
         <column name="WITH_GRANT_OPTION" length="1" jdbc-type="CHAR"/>
@@ -136,7 +164,7 @@
       <datastore-identity>
         <column name="GM_PRIVILEGE_ID"/>
       </datastore-identity>
-    <index name="GM_PRIVILEGE_INDEX" unique="true">
+      <index name="GM_PRIVILEGE_INDEX" unique="true">
         <field name="componentName"/>
         <field name="serviceName"/>
         <field name="resourceName0"/>
@@ -149,7 +177,7 @@
         <field name="resourceType3"/>
         <field name="action"/>
         <field name="grantOption"/>
-	  </index>
+      </index>
       <field name="componentName">
         <column name="COMPONENT_NAME" length="100" jdbc-type="VARCHAR"/>
       </field>

http://git-wip-us.apache.org/repos/asf/sentry/blob/68949951/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java
index 22162eb..9e41a33 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java
@@ -58,6 +58,7 @@ import org.apache.sentry.provider.db.SentryNoSuchObjectException;
 import org.apache.sentry.provider.db.service.model.MSentryGroup;
 import org.apache.sentry.provider.db.service.model.MSentryPrivilege;
 import org.apache.sentry.provider.db.service.model.MSentryRole;
+import org.apache.sentry.provider.db.service.model.MSentryUser;
 import org.apache.sentry.provider.db.service.model.MSentryVersion;
 import org.apache.sentry.provider.db.service.thrift.SentryConfigurationException;
 import org.apache.sentry.provider.db.service.thrift.SentryPolicyStoreProcessor;
@@ -369,6 +370,7 @@ public class SentryStore {
       }
     };
   }
+
   public Gauge<Long> getGroupCountGauge() {
     return new Gauge< Long >() {
       @Override
@@ -378,6 +380,15 @@ public class SentryStore {
     };
   }
 
+  public Gauge<Long> getUserCountGauge() {
+    return new Gauge<Long>() {
+      @Override
+      public Long getValue() {
+        return getCount(MSentryUser.class);
+      }
+    };
+  }
+
   /**
    * Lets the test code know how many privs are in the db, so that we know
    * if they are in fact being cleaned up when not being referenced any more.
@@ -396,6 +407,7 @@ public class SentryStore {
       pm = openTransaction();
       pm.newQuery(MSentryRole.class).deletePersistentAll();
       pm.newQuery(MSentryGroup.class).deletePersistentAll();
+      pm.newQuery(MSentryUser.class).deletePersistentAll();
       pm.newQuery(MSentryPrivilege.class).deletePersistentAll();
       commitUpdateTransaction(pm);
       rollbackTransaction = false;
@@ -814,7 +826,7 @@ public class SentryStore {
     }
   }
 
-  public CommitContext alterSentryRoleAddGroups( String grantorPrincipal, String roleName,
+  public CommitContext alterSentryRoleAddGroups(String grantorPrincipal, String roleName,
       Set<TSentryGroup> groupNames)
           throws SentryNoSuchObjectException {
     boolean rollbackTransaction = true;
@@ -861,6 +873,79 @@ public class SentryStore {
     }
   }
 
+  public CommitContext alterSentryRoleAddUsers(String roleName,
+      Set<String> userNames) throws SentryNoSuchObjectException {
+    boolean rollbackTransaction = true;
+    PersistenceManager pm = null;
+    roleName = roleName.trim().toLowerCase();
+    try {
+      pm = openTransaction();
+      MSentryRole role = getMSentryRole(pm, roleName);
+      if (role == null) {
+        throw new SentryNoSuchObjectException("Role: " + roleName);
+      } else {
+        Query query = pm.newQuery(MSentryUser.class);
+        query.setFilter("this.userName == t");
+        query.declareParameters("java.lang.String t");
+        query.setUnique(true);
+        List<MSentryUser> users = Lists.newArrayList();
+        for (String userName : userNames) {
+          userName = userName.trim();
+          MSentryUser user = (MSentryUser) query.execute(userName);
+          if (user == null) {
+            user = new MSentryUser(userName, System.currentTimeMillis(), Sets.newHashSet(role));
+          }
+          user.appendRole(role);
+          users.add(user);
+        }
+        pm.makePersistentAll(users);
+        CommitContext commit = commitUpdateTransaction(pm);
+        rollbackTransaction = false;
+        return commit;
+      }
+    } finally {
+      if (rollbackTransaction) {
+        rollbackTransaction(pm);
+      }
+    }
+  }
+
+  public CommitContext alterSentryRoleDeleteUsers(String roleName, Set<String> userNames)
+      throws SentryNoSuchObjectException {
+    boolean rollbackTransaction = true;
+    PersistenceManager pm = null;
+    roleName = roleName.trim().toLowerCase();
+    try {
+      pm = openTransaction();
+      MSentryRole role = getMSentryRole(pm, roleName);
+      if (role == null) {
+        throw new SentryNoSuchObjectException("Role: " + roleName);
+      } else {
+        Query query = pm.newQuery(MSentryUser.class);
+        query.setFilter("this.userName == t");
+        query.declareParameters("java.lang.String t");
+        query.setUnique(true);
+        List<MSentryUser> users = Lists.newArrayList();
+        for (String userName : userNames) {
+          userName = userName.trim();
+          MSentryUser user = (MSentryUser) query.execute(userName);
+          if (user != null) {
+            user.removeRole(role);
+            users.add(user);
+          }
+        }
+        pm.makePersistentAll(users);
+        CommitContext commit = commitUpdateTransaction(pm);
+        rollbackTransaction = false;
+        return commit;
+      }
+    } finally {
+      if (rollbackTransaction) {
+        rollbackTransaction(pm);
+      }
+    }
+  }
+
   public CommitContext alterSentryRoleDeleteGroups(String roleName,
       Set<TSentryGroup> groupNames)
           throws SentryNoSuchObjectException {
@@ -1064,15 +1149,13 @@ public class SentryStore {
     }
   }
 
-  public TSentryPrivilegeMap listSentryPrivilegesByAuthorizable(
-      Set<String> groups, TSentryActiveRoleSet activeRoles,
+  public TSentryPrivilegeMap listSentryPrivilegesByAuthorizable(Set<String> groups,
+      TSentryActiveRoleSet activeRoles,
       TSentryAuthorizable authHierarchy, boolean isAdmin)
       throws SentryInvalidInputException {
     Map<String, Set<TSentryPrivilege>> resultPrivilegeMap = Maps.newTreeMap();
-    Set<String> roles = Sets.newHashSet();
-    if (groups != null && !groups.isEmpty()) {
-      roles = getRolesToQuery(groups, new TSentryActiveRoleSet(true, null));
-    }
+    Set<String> roles = getRolesToQuery(groups, null, new TSentryActiveRoleSet(true, null));
+
     if (activeRoles != null && !activeRoles.isAll()) {
       // need to check/convert to lowercase here since this is from user input
       for (String aRole : activeRoles.getRoles()) {
@@ -1208,23 +1291,60 @@ public class SentryStore {
   }
 
   public Set<String> getRoleNamesForGroups(Set<String> groups) {
-    Set<String> result = new HashSet<String>();
+    if (groups == null || groups.isEmpty()) {
+      return ImmutableSet.of();
+    }
     boolean rollbackTransaction = true;
     PersistenceManager pm = null;
     try {
       pm = openTransaction();
-      Query query = pm.newQuery(MSentryGroup.class);
-      query.setFilter("this.groupName == t");
-      query.declareParameters("java.lang.String t");
-      query.setUnique(true);
-      for (String group : groups) {
-        MSentryGroup sentryGroup = (MSentryGroup) query.execute(group.trim());
-        if (sentryGroup != null) {
-          for (MSentryRole role : sentryGroup.getRoles()) {
-            result.add(role.getRoleName());
-          }
-        }
+      Set<String> result = getRoleNamesForGroupsCore(pm, groups);
+      rollbackTransaction = false;
+      commitTransaction(pm);
+      return result;
+    } finally {
+      if (rollbackTransaction) {
+        rollbackTransaction(pm);
+      }
+    }
+  }
+
+  private Set<String> getRoleNamesForGroupsCore(PersistenceManager pm, Set<String> groups) {
+    return convertToRoleNameSet(getRolesForGroups(pm, groups));
+  }
+
+  public Set<String> getRoleNamesForUsers(Set<String> users) {
+    if (users == null || users.isEmpty()) {
+      return ImmutableSet.of();
+    }
+    boolean rollbackTransaction = true;
+    PersistenceManager pm = null;
+    try {
+      pm = openTransaction();
+      Set<String> result = getRoleNamesForUsersCore(pm,users);
+      rollbackTransaction = false;
+      commitTransaction(pm);
+      return result;
+    } finally {
+      if (rollbackTransaction) {
+        rollbackTransaction(pm);
       }
+    }
+  }
+
+  private Set<String> getRoleNamesForUsersCore(PersistenceManager pm, Set<String> users) {
+    return convertToRoleNameSet(getRolesForUsers(pm, users));
+  }
+
+  public Set<TSentryRole> getTSentryRolesByUserNames(Set<String> users) {
+    boolean rollbackTransaction = true;
+    PersistenceManager pm = null;
+    try {
+      pm = openTransaction();
+      Set<MSentryRole> mSentryRoles = getRolesForUsers(pm, users);
+      // Since {@link MSentryRole#getGroups()} is lazy-loading, the converting should be call
+      // before transaction committed.
+      Set<TSentryRole> result = convertToTSentryRoles(mSentryRoles);
       rollbackTransaction = false;
       commitTransaction(pm);
       return result;
@@ -1236,31 +1356,50 @@ public class SentryStore {
   }
 
   public Set<MSentryRole> getRolesForGroups(PersistenceManager pm, Set<String> groups) {
-    Set<MSentryRole> result = new HashSet<MSentryRole>();
-    Query query = pm.newQuery(MSentryGroup.class);
-    query.setFilter("this.groupName == t");
-    query.declareParameters("java.lang.String t");
-    query.setUnique(true);
-    for (String group : groups) {
-      MSentryGroup sentryGroup = (MSentryGroup) query.execute(group.trim());
-      if (sentryGroup != null) {
-        result.addAll(sentryGroup.getRoles());
+    Set<MSentryRole> result = Sets.newHashSet();
+    if (groups != null) {
+      Query query = pm.newQuery(MSentryGroup.class);
+      query.setFilter("this.groupName == t");
+      query.declareParameters("java.lang.String t");
+      query.setUnique(true);
+      for (String group : groups) {
+        MSentryGroup sentryGroup = (MSentryGroup) query.execute(group.trim());
+        if (sentryGroup != null) {
+          result.addAll(sentryGroup.getRoles());
+        }
       }
     }
     return result;
   }
 
-  public Set<String> listAllSentryPrivilegesForProvider(Set<String> groups, TSentryActiveRoleSet roleSet) throws SentryInvalidInputException {
-    return listSentryPrivilegesForProvider(groups, roleSet, null);
+  public Set<MSentryRole> getRolesForUsers(PersistenceManager pm, Set<String> users) {
+    Set<MSentryRole> result = Sets.newHashSet();
+    if (users != null) {
+      Query query = pm.newQuery(MSentryUser.class);
+      query.setFilter("this.userName == t");
+      query.declareParameters("java.lang.String t");
+      query.setUnique(true);
+      for (String user : users) {
+        MSentryUser sentryUser = (MSentryUser) query.execute(user.trim());
+        if (sentryUser != null) {
+          result.addAll(sentryUser.getRoles());
+        }
+      }
+    }
+    return result;
+  }
+
+  public Set<String> listAllSentryPrivilegesForProvider(Set<String> groups, Set<String> users,
+      TSentryActiveRoleSet roleSet) throws SentryInvalidInputException {
+    return listSentryPrivilegesForProvider(groups, users, roleSet, null);
   }
 
 
-  public Set<String> listSentryPrivilegesForProvider(Set<String> groups,
+  public Set<String> listSentryPrivilegesForProvider(Set<String> groups, Set<String> users,
       TSentryActiveRoleSet roleSet, TSentryAuthorizable authHierarchy) throws SentryInvalidInputException {
     Set<String> result = Sets.newHashSet();
-    Set<String> rolesToQuery = getRolesToQuery(groups, roleSet);
+    Set<String> rolesToQuery = getRolesToQuery(groups, users, roleSet);
     List<MSentryPrivilege> mSentryPrivileges = getMSentryPrivileges(rolesToQuery, authHierarchy);
-
     for (MSentryPrivilege priv : mSentryPrivileges) {
       result.add(toAuthorizable(priv));
     }
@@ -1268,21 +1407,33 @@ public class SentryStore {
     return result;
   }
 
-
-  public boolean hasAnyServerPrivileges(Set<String> groups, TSentryActiveRoleSet roleSet, String server) {
-    Set<String> rolesToQuery = getRolesToQuery(groups, roleSet);
+  public boolean hasAnyServerPrivileges(Set<String> groups, Set<String> users,
+      TSentryActiveRoleSet roleSet, String server) {
+    Set<String> rolesToQuery = getRolesToQuery(groups, users, roleSet);
     return hasAnyServerPrivileges(rolesToQuery, server);
   }
 
-
-
-  private Set<String> getRolesToQuery(Set<String> groups,
+  private Set<String> getRolesToQuery(Set<String> groups, Set<String> users,
       TSentryActiveRoleSet roleSet) {
     Set<String> activeRoleNames = toTrimedLower(roleSet.getRoles());
 
-    Set<String> roleNamesForGroups = toTrimedLower(getRoleNamesForGroups(groups));
-    Set<String> rolesToQuery = roleSet.isAll() ? roleNamesForGroups : Sets.intersection(activeRoleNames, roleNamesForGroups);
-    return rolesToQuery;
+    Set<String> roleNames = Sets.newHashSet();
+    boolean rollbackTransaction = true;
+    PersistenceManager pm = null;
+    try {
+      pm = openTransaction();
+      roleNames.addAll(toTrimedLower(getRoleNamesForGroupsCore(pm, groups)));
+      roleNames.addAll(toTrimedLower(getRoleNamesForUsersCore(pm, users)));
+      rollbackTransaction = false;
+      commitTransaction(pm);
+      Set<String> rolesToQuery = roleSet.isAll() ? roleNames : Sets.intersection(activeRoleNames,
+          roleNames);
+      return rolesToQuery;
+    } finally {
+      if (rollbackTransaction) {
+        rollbackTransaction(pm);
+      }
+    }
   }
 
   @VisibleForTesting
@@ -1352,6 +1503,14 @@ public class SentryStore {
     return roles;
   }
 
+  private Set<String> convertToRoleNameSet(Set<MSentryRole> mSentryRoles) {
+    Set<String> roleNameSet = Sets.newHashSet();
+    for (MSentryRole role : mSentryRoles) {
+      roleNameSet.add(role.getRoleName());
+    }
+    return roleNameSet;
+  }
+
   private TSentryRole convertToTSentryRole(MSentryRole mSentryRole) {
     TSentryRole role = new TSentryRole();
     role.setRoleName(mSentryRole.getRoleName());
@@ -1696,16 +1855,13 @@ public class SentryStore {
     if (grantorPrincipal == null) {
       throw new SentryInvalidInputException("grantorPrincipal should not be null");
     }
+
     Set<String> groups = SentryPolicyStoreProcessor.getGroupsFromUserName(conf, grantorPrincipal);
-    if (groups == null || groups.isEmpty()) {
-      throw new SentryGrantDeniedException(grantorPrincipal
-          + " has no grant!");
-    }
 
     // if grantor is in adminGroup, don't need to do check
     Set<String> admins = getAdminGroups();
     boolean isAdminGroup = false;
-    if (admins != null && !admins.isEmpty()) {
+    if (groups != null && admins != null && !admins.isEmpty()) {
       for (String g : groups) {
         if (admins.contains(g)) {
           isAdminGroup = true;
@@ -1716,9 +1872,11 @@ public class SentryStore {
 
     if (!isAdminGroup) {
       boolean hasGrant = false;
+      // get all privileges for group and user
       Set<MSentryRole> roles = getRolesForGroups(pm, groups);
+      roles.addAll(getRolesForUsers(pm, Sets.newHashSet(grantorPrincipal)));
       if (roles != null && !roles.isEmpty()) {
-        for (MSentryRole role: roles) {
+        for (MSentryRole role : roles) {
           Set<MSentryPrivilege> privilegeSet = role.getPrivileges();
           if (privilegeSet != null && !privilegeSet.isEmpty()) {
             // if role has a privilege p with grant option

http://git-wip-us.apache.org/repos/asf/sentry/blob/68949951/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/NotificationHandler.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/NotificationHandler.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/NotificationHandler.java
index 506d433..b1a4b7f 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/NotificationHandler.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/NotificationHandler.java
@@ -68,4 +68,12 @@ public abstract class NotificationHandler {
     CommitContext context, TAlterSentryRoleDeleteGroupsRequest request,
     TAlterSentryRoleDeleteGroupsResponse response) {
   }
+
+  public void alter_sentry_role_add_users(CommitContext context,
+      TAlterSentryRoleAddUsersRequest request, TAlterSentryRoleAddUsersResponse response) {
+  }
+
+  public void alter_sentry_role_delete_users(CommitContext context,
+      TAlterSentryRoleDeleteUsersRequest request, TAlterSentryRoleDeleteUsersResponse response) {
+  }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/sentry/blob/68949951/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/NotificationHandlerInvoker.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/NotificationHandlerInvoker.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/NotificationHandlerInvoker.java
index 9d9e867..856ef9a 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/NotificationHandlerInvoker.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/NotificationHandlerInvoker.java
@@ -143,4 +143,34 @@ public class NotificationHandlerInvoker extends NotificationHandler {
       }
     }
   }
+
+  @Override
+  public void alter_sentry_role_add_users(CommitContext context,
+      TAlterSentryRoleAddUsersRequest request, TAlterSentryRoleAddUsersResponse response) {
+    for (NotificationHandler handler : handlers) {
+      try {
+        LOGGER.debug("Calling " + handler);
+        handler.alter_sentry_role_add_users(context, new TAlterSentryRoleAddUsersRequest(request),
+            new TAlterSentryRoleAddUsersResponse(response));
+      } catch (Exception ex) {
+        LOGGER.error("Unexpected error in " + handler + ". Request: " + request + ", Response: "
+            + response, ex);
+      }
+    }
+  }
+
+  @Override
+  public void alter_sentry_role_delete_users(CommitContext context,
+      TAlterSentryRoleDeleteUsersRequest request, TAlterSentryRoleDeleteUsersResponse response) {
+    for (NotificationHandler handler : handlers) {
+      try {
+        LOGGER.debug("Calling " + handler);
+        handler.alter_sentry_role_delete_users(context, new TAlterSentryRoleDeleteUsersRequest(
+            request), new TAlterSentryRoleDeleteUsersResponse(response));
+      } catch (Exception ex) {
+        LOGGER.error("Unexpected error in " + handler + ". Request: " + request + ", Response: "
+            + response, ex);
+      }
+    }
+  }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/sentry/blob/68949951/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyServiceClient.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyServiceClient.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyServiceClient.java
index b6deadb..46f4785 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyServiceClient.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyServiceClient.java
@@ -35,6 +35,9 @@ public interface SentryPolicyServiceClient {
   void dropRoleIfExists(String requestorUserName, String roleName)
       throws SentryUserException;
 
+  Set<TSentryRole> listRolesByUserName(String requestorUserName, String userName)
+      throws SentryUserException;
+
   Set<TSentryRole> listRolesByGroupName(String requestorUserName, String groupName)
       throws SentryUserException;
 
@@ -142,8 +145,8 @@ public interface SentryPolicyServiceClient {
       String db, String table, List<String> columns, String action, Boolean grantOption)
       throws SentryUserException;
 
-  Set<String> listPrivilegesForProvider(Set<String> groups, ActiveRoleSet roleSet,
-      Authorizable... authorizable) throws SentryUserException;
+  Set<String> listPrivilegesForProvider(Set<String> groups, Set<String> users,
+      ActiveRoleSet roleSet, Authorizable... authorizable) throws SentryUserException;
 
   void grantRoleToGroup(String requestorUserName, String groupName, String roleName)
       throws SentryUserException;
@@ -157,6 +160,18 @@ public interface SentryPolicyServiceClient {
   void revokeRoleFromGroups(String requestorUserName, String roleName, Set<String> groups)
       throws SentryUserException;
 
+  void grantRoleToUser(String requestorUserName, String userName, String roleName)
+      throws SentryUserException;
+
+  void revokeRoleFromUser(String requestorUserName, String userName, String roleName)
+      throws SentryUserException;
+
+  void grantRoleToUsers(String requestorUserName, String roleName, Set<String> users)
+      throws SentryUserException;
+
+  void revokeRoleFromUsers(String requestorUserName, String roleName, Set<String> users)
+      throws SentryUserException;
+
   void dropPrivileges(String requestorUserName,
       List<? extends Authorizable> authorizableObjects) throws SentryUserException;
 

http://git-wip-us.apache.org/repos/asf/sentry/blob/68949951/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyServiceClientDefaultImpl.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyServiceClientDefaultImpl.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyServiceClientDefaultImpl.java
index 3f8e489..0da3cfd 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyServiceClientDefaultImpl.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyServiceClientDefaultImpl.java
@@ -247,7 +247,34 @@ public class SentryPolicyServiceClientDefaultImpl implements SentryPolicyService
     }
   }
 
-  public synchronized Set<TSentryPrivilege> listAllPrivilegesByRoleName(String requestorUserName, String roleName)
+  /**
+   * Gets sentry role objects for a given userName using the Sentry service
+   *
+   * @param requestorUserName
+   *        : user on whose behalf the request is issued
+   * @param userName
+   *        : userName to look up (can't be empty)
+   * @return Set of thrift sentry role objects
+   * @throws SentryUserException
+   */
+  public Set<TSentryRole> listRolesByUserName(String requestorUserName, String userName)
+      throws SentryUserException {
+    TListSentryRolesForUserRequest request = new TListSentryRolesForUserRequest();
+    request.setProtocol_version(ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT);
+    request.setRequestorUserName(requestorUserName);
+    request.setUserName(userName);
+    TListSentryRolesResponse response;
+    try {
+      response = client.list_sentry_roles_by_user(request);
+      Status.throwIfNotOk(response.getStatus());
+      return response.getRoles();
+    } catch (TException e) {
+      throw new SentryUserException(THRIFT_EXCEPTION_MESSAGE, e);
+    }
+  }
+
+  public synchronized Set<TSentryPrivilege> listAllPrivilegesByRoleName(String requestorUserName,
+      String roleName)
                  throws SentryUserException {
     return listPrivilegesByRoleName(requestorUserName, roleName, null);
   }
@@ -288,7 +315,10 @@ public class SentryPolicyServiceClientDefaultImpl implements SentryPolicyService
 
   public synchronized Set<TSentryRole> listUserRoles(String requestorUserName)
       throws SentryUserException {
-    return listRolesByGroupName(requestorUserName, AccessConstants.ALL);
+    Set<TSentryRole> tSentryRoles = Sets.newHashSet();
+    tSentryRoles.addAll(listRolesByGroupName(requestorUserName, AccessConstants.ALL));
+    tSentryRoles.addAll(listRolesByUserName(requestorUserName, requestorUserName));
+    return tSentryRoles;
   }
 
   public synchronized TSentryPrivilege grantURIPrivilege(String requestorUserName,
@@ -667,8 +697,8 @@ public class SentryPolicyServiceClientDefaultImpl implements SentryPolicyService
     return TSentryGrantOption.FALSE;
   }
 
-  public synchronized Set<String> listPrivilegesForProvider(Set<String> groups, ActiveRoleSet roleSet, Authorizable... authorizable)
-  throws SentryUserException {
+  public synchronized Set<String> listPrivilegesForProvider(Set<String> groups, Set<String> users,
+      ActiveRoleSet roleSet, Authorizable... authorizable) throws SentryUserException {
     TSentryActiveRoleSet thriftRoleSet = new TSentryActiveRoleSet(roleSet.isAll(), roleSet.getRoles());
     TListSentryPrivilegesForProviderRequest request =
         new TListSentryPrivilegesForProviderRequest(ThriftConstants.
@@ -678,6 +708,9 @@ public class SentryPolicyServiceClientDefaultImpl implements SentryPolicyService
           .newArrayList(authorizable));
       request.setAuthorizableHierarchy(tSentryAuthorizable);
     }
+    if (users != null) {
+      request.setUsers(users);
+    }
     try {
       TListSentryPrivilegesForProviderResponse response = client.list_sentry_privileges_for_provider(request);
       Status.throwIfNotOk(response.getStatus());
@@ -731,6 +764,44 @@ public class SentryPolicyServiceClientDefaultImpl implements SentryPolicyService
     }
   }
 
+  @Override
+  public synchronized void grantRoleToUser(String requestorUserName, String userName,
+      String roleName) throws SentryUserException {
+    grantRoleToUsers(requestorUserName, roleName, Sets.newHashSet(userName));
+  }
+
+  @Override
+  public synchronized void revokeRoleFromUser(String requestorUserName, String userName,
+      String roleName) throws SentryUserException {
+    revokeRoleFromUsers(requestorUserName, roleName, Sets.newHashSet(userName));
+  }
+
+  @Override
+  public synchronized void grantRoleToUsers(String requestorUserName, String roleName,
+      Set<String> users) throws SentryUserException {
+    TAlterSentryRoleAddUsersRequest request = new TAlterSentryRoleAddUsersRequest(
+        ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT, requestorUserName, roleName, users);
+    try {
+      TAlterSentryRoleAddUsersResponse response = client.alter_sentry_role_add_users(request);
+      Status.throwIfNotOk(response.getStatus());
+    } catch (TException e) {
+      throw new SentryUserException(THRIFT_EXCEPTION_MESSAGE, e);
+    }
+  }
+
+  @Override
+  public synchronized void revokeRoleFromUsers(String requestorUserName, String roleName,
+      Set<String> users) throws SentryUserException {
+    TAlterSentryRoleDeleteUsersRequest request = new TAlterSentryRoleDeleteUsersRequest(
+        ThriftConstants.TSENTRY_SERVICE_VERSION_CURRENT, requestorUserName, roleName, users);
+    try {
+      TAlterSentryRoleDeleteUsersResponse response = client.alter_sentry_role_delete_users(request);
+      Status.throwIfNotOk(response.getStatus());
+    } catch (TException e) {
+      throw new SentryUserException(THRIFT_EXCEPTION_MESSAGE, e);
+    }
+  }
+
   private Set<TSentryGroup> convert2TGroups(Set<String> groups) {
     Set<TSentryGroup> tGroups = Sets.newHashSet();
     if (groups != null) {

http://git-wip-us.apache.org/repos/asf/sentry/blob/68949951/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java
index 52af345..a922d3b 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java
@@ -27,11 +27,13 @@ import java.util.Map;
 import java.util.Set;
 import java.util.regex.Pattern;
 
+import org.apache.commons.lang.StringUtils;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.sentry.SentryUserException;
 import org.apache.sentry.core.model.db.AccessConstants;
 import org.apache.sentry.provider.common.GroupMappingService;
 import org.apache.sentry.provider.common.PolicyFileConstants;
+import org.apache.sentry.provider.common.SentryGroupNotFoundException;
 import org.apache.sentry.provider.db.SentryAccessDeniedException;
 import org.apache.sentry.provider.db.SentryAlreadyExistsException;
 import org.apache.sentry.provider.db.SentryInvalidInputException;
@@ -459,8 +461,9 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
       validateClientVersion(request.getProtocol_version());
       authorize(request.getRequestorUserName(),
           getRequestorGroups(request.getRequestorUserName()));
-      CommitContext commitContext = sentryStore.alterSentryRoleAddGroups(request.getRequestorUserName(),
-                                    request.getRoleName(), request.getGroups());
+      CommitContext commitContext = sentryStore.alterSentryRoleAddGroups(
+          request.getRequestorUserName(), request.getRoleName(),
+          request.getGroups());
       response.setStatus(Status.OK());
       notificationHandlerInvoker.alter_sentry_role_add_groups(commitContext,
           request, response);
@@ -497,6 +500,88 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
   }
 
   @Override
+  public TAlterSentryRoleAddUsersResponse alter_sentry_role_add_users(
+      TAlterSentryRoleAddUsersRequest request) throws TException {
+    final Timer.Context timerContext = sentryMetrics.grantRoleTimer.time();
+    TAlterSentryRoleAddUsersResponse response = new TAlterSentryRoleAddUsersResponse();
+    try {
+      validateClientVersion(request.getProtocol_version());
+      authorize(request.getRequestorUserName(), getRequestorGroups(request.getRequestorUserName()));
+      CommitContext commitContext = sentryStore.alterSentryRoleAddUsers(request.getRoleName(),
+          request.getUsers());
+      response.setStatus(Status.OK());
+      notificationHandlerInvoker.alter_sentry_role_add_users(commitContext, request, response);
+    } catch (SentryNoSuchObjectException e) {
+      String msg = "Role: " + request + " does not exist.";
+      LOGGER.error(msg, e);
+      response.setStatus(Status.NoSuchObject(msg, e));
+    } catch (SentryAccessDeniedException e) {
+      LOGGER.error(e.getMessage(), e);
+      response.setStatus(Status.AccessDenied(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();
+    }
+
+    try {
+      AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance()
+          .createJsonLogEntity(request, response, conf).toJsonFormatLog());
+    } catch (Exception e) {
+      // if any exception, log the exception.
+      String msg = "Error creating audit log for add role to user: " + e.getMessage();
+      LOGGER.error(msg, e);
+    }
+    return response;
+  }
+
+  @Override
+  public TAlterSentryRoleDeleteUsersResponse alter_sentry_role_delete_users(
+      TAlterSentryRoleDeleteUsersRequest request) throws TException {
+    final Timer.Context timerContext = sentryMetrics.grantRoleTimer.time();
+    TAlterSentryRoleDeleteUsersResponse response = new TAlterSentryRoleDeleteUsersResponse();
+    try {
+      validateClientVersion(request.getProtocol_version());
+      authorize(request.getRequestorUserName(), getRequestorGroups(request.getRequestorUserName()));
+      CommitContext commitContext = sentryStore.alterSentryRoleDeleteUsers(request.getRoleName(),
+          request.getUsers());
+      response.setStatus(Status.OK());
+      notificationHandlerInvoker.alter_sentry_role_delete_users(commitContext, request, response);
+    } catch (SentryNoSuchObjectException e) {
+      String msg = "Role: " + request + " does not exist.";
+      LOGGER.error(msg, e);
+      response.setStatus(Status.NoSuchObject(msg, e));
+    } catch (SentryAccessDeniedException e) {
+      LOGGER.error(e.getMessage(), e);
+      response.setStatus(Status.AccessDenied(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();
+    }
+
+    try {
+      AUDIT_LOGGER.info(JsonLogEntityFactory.getInstance()
+          .createJsonLogEntity(request, response, conf).toJsonFormatLog());
+   } catch (Exception e) {
+      // if any exception, log the exception.
+      String msg = "Error creating audit log for delete role from user: " + e.getMessage();
+      LOGGER.error(msg, e);
+    }
+    return response;
+  }
+
+  @Override
   public TAlterSentryRoleDeleteGroupsResponse alter_sentry_role_delete_groups(
     TAlterSentryRoleDeleteGroupsRequest request) throws TException {
     final Timer.Context timerContext = sentryMetrics.revokeRoleTimer.time();
@@ -592,6 +677,59 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
     return response;
   }
 
+  public TListSentryRolesResponse list_sentry_roles_by_user(TListSentryRolesForUserRequest request)
+      throws TException {
+    final Timer.Context timerContext = sentryMetrics.listRolesByGroupTimer.time();
+    TListSentryRolesResponse response = new TListSentryRolesResponse();
+    TSentryResponseStatus status;
+    Set<TSentryRole> roleSet = new HashSet<TSentryRole>();
+    String requestor = request.getRequestorUserName();
+    String userName = request.getUserName();
+    boolean checkAllGroups = false;
+    try {
+      validateClientVersion(request.getProtocol_version());
+      // userName can't be empty
+      if (StringUtils.isEmpty(userName)) {
+        throw new SentryAccessDeniedException("The user name can't be empty.");
+      }
+
+      Set<String> requestorGroups = getRequestorGroups(requestor);
+      Set<String> userGroups = getRequestorGroups(userName);
+      boolean isAdmin = inAdminGroups(requestorGroups);
+
+      // Only admin users can list other user's roles in the system
+      // Non admin users are only allowed to list only their own roles related user and group
+      if (!isAdmin && !userName.equals(requestor)) {
+        throw new SentryAccessDeniedException("Access denied to list the roles for " + userName);
+      }
+      roleSet = sentryStore.getTSentryRolesByUserNames(Sets.newHashSet(userName));
+      response.setRoles(roleSet);
+      response.setStatus(Status.OK());
+    } catch (SentryGroupNotFoundException e) {
+      LOGGER.error(e.getMessage(), e);
+      String msg = "Group couldn't be retrieved for " + requestor + " or " + userName + ".";
+      response.setStatus(Status.AccessDenied(msg, e));
+    } catch (SentryNoSuchObjectException e) {
+      response.setRoles(roleSet);
+      String msg = "Role: " + request + " couldn't be retrieved.";
+      LOGGER.error(msg, e);
+      response.setStatus(Status.NoSuchObject(msg, e));
+    } catch (SentryAccessDeniedException e) {
+      LOGGER.error(e.getMessage(), e);
+      response.setStatus(Status.AccessDenied(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;
+  }
+
   @Override
   public TListSentryPrivilegesResponse list_sentry_privileges_by_role(
       TListSentryPrivilegesRequest request) throws TException {
@@ -651,12 +789,15 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
     response.setPrivileges(new HashSet<String>());
     try {
       validateClientVersion(request.getProtocol_version());
-      Set<String> privilegesForProvider = sentryStore.listSentryPrivilegesForProvider(
-          request.getGroups(), request.getRoleSet(), request.getAuthorizableHierarchy());
+      Set<String> privilegesForProvider =
+          sentryStore.listSentryPrivilegesForProvider(request.getGroups(), request.getUsers(),
+              request.getRoleSet(), request.getAuthorizableHierarchy());
       response.setPrivileges(privilegesForProvider);
-      if (privilegesForProvider == null || privilegesForProvider.size() == 0 && request.getAuthorizableHierarchy() != null
-        && sentryStore.hasAnyServerPrivileges(
-          request.getGroups(), request.getRoleSet(), request.getAuthorizableHierarchy().getServer())) {
+      if (privilegesForProvider == null
+          || privilegesForProvider.size() == 0
+          && request.getAuthorizableHierarchy() != null
+          && sentryStore.hasAnyServerPrivileges(request.getGroups(), request.getUsers(),
+              request.getRoleSet(), request.getAuthorizableHierarchy().getServer())) {
 
         // REQUIRED for ensuring 'default' Db is accessible by any user
         // with privileges to atleast 1 object with the specific server as root

http://git-wip-us.apache.org/repos/asf/sentry/blob/68949951/sentry-provider/sentry-provider-db/src/main/resources/sentry_policy_service.thrift
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/resources/sentry_policy_service.thrift b/sentry-provider/sentry-provider-db/src/main/resources/sentry_policy_service.thrift
index f1e577c..2088eae 100644
--- a/sentry-provider/sentry-provider-db/src/main/resources/sentry_policy_service.thrift
+++ b/sentry-provider/sentry-provider-db/src/main/resources/sentry_policy_service.thrift
@@ -89,7 +89,19 @@ struct TAlterSentryRoleAddGroupsResponse {
 1: required sentry_common_service.TSentryResponseStatus status
 }
 
-# REVOLE ROLE r1 FROM GROUP g1
+# GRANT ROLE r1 TO USER u1
+struct TAlterSentryRoleAddUsersRequest {
+1: required i32 protocol_version = sentry_common_service.TSENTRY_SERVICE_V1,
+2: required string requestorUserName, # user on whose behalf the request is issued
+3: required string roleName,
+4: required set<string> users
+}
+
+struct TAlterSentryRoleAddUsersResponse {
+1: required sentry_common_service.TSentryResponseStatus status
+}
+
+# REVOKE ROLE r1 FROM GROUP g1
 struct TAlterSentryRoleDeleteGroupsRequest {
 1: required i32 protocol_version = sentry_common_service.TSENTRY_SERVICE_V2,
 2: required string requestorUserName, # user on whose behalf the request is issued
@@ -100,6 +112,17 @@ struct TAlterSentryRoleDeleteGroupsResponse {
 1: required sentry_common_service.TSentryResponseStatus status
 }
 
+# REVOKE ROLE r1 FROM USER u1
+struct TAlterSentryRoleDeleteUsersRequest {
+1: required i32 protocol_version = sentry_common_service.TSENTRY_SERVICE_V1,
+2: required string requestorUserName, # user on whose behalf the request is issued
+3: required string roleName,
+4: required set<string> users
+}
+struct TAlterSentryRoleDeleteUsersResponse {
+1: required sentry_common_service.TSentryResponseStatus status
+}
+
 # GRANT ... ON ... TO ROLE ...
 struct TAlterSentryRoleGrantPrivilegeRequest {
 1: required i32 protocol_version = sentry_common_service.TSENTRY_SERVICE_V2,
@@ -132,6 +155,13 @@ struct TListSentryRolesRequest {
 2: required string requestorUserName, # user on whose behalf the request is issued
 3: optional string groupName # for this group, or all roles for all groups if null
 }
+
+struct TListSentryRolesForUserRequest {
+1: required i32 protocol_version = sentry_common_service.TSENTRY_SERVICE_V1,
+2: required string requestorUserName, # user on whose behalf the request is issued
+3: required string userName
+}
+
 # used only for TListSentryRolesResponse
 struct TSentryRole {
 1: required string roleName,
@@ -198,6 +228,7 @@ struct TListSentryPrivilegesForProviderRequest {
 2: required set<string> groups,
 3: required TSentryActiveRoleSet roleSet,
 4: optional TSentryAuthorizable authorizableHierarchy,
+5: optional set<string> users
 }
 struct TListSentryPrivilegesForProviderResponse {
 1: required sentry_common_service.TSentryResponseStatus status
@@ -271,7 +302,11 @@ service SentryPolicyService
   TAlterSentryRoleAddGroupsResponse alter_sentry_role_add_groups(1:TAlterSentryRoleAddGroupsRequest request)
   TAlterSentryRoleDeleteGroupsResponse alter_sentry_role_delete_groups(1:TAlterSentryRoleDeleteGroupsRequest request)
 
+  TAlterSentryRoleAddUsersResponse alter_sentry_role_add_users(1:TAlterSentryRoleAddUsersRequest request)
+  TAlterSentryRoleDeleteUsersResponse alter_sentry_role_delete_users(1:TAlterSentryRoleDeleteUsersRequest request)
+
   TListSentryRolesResponse list_sentry_roles_by_group(1:TListSentryRolesRequest request)
+  TListSentryRolesResponse list_sentry_roles_by_user(1:TListSentryRolesForUserRequest request)
 
   TListSentryPrivilegesResponse list_sentry_privileges_by_role(1:TListSentryPrivilegesRequest request)
 

http://git-wip-us.apache.org/repos/asf/sentry/blob/68949951/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/log/util/TestCommandUtil.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/log/util/TestCommandUtil.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/log/util/TestCommandUtil.java
index 8620f62..8cf0e70 100644
--- a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/log/util/TestCommandUtil.java
+++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/log/util/TestCommandUtil.java
@@ -22,8 +22,6 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 
-import org.junit.Assert;
-
 import org.apache.sentry.core.model.db.AccessConstants;
 import org.apache.sentry.provider.db.generic.service.thrift.TAuthorizable;
 import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleGrantPrivilegeRequest;
@@ -31,6 +29,7 @@ import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleRevokePrivil
 import org.apache.sentry.provider.db.service.thrift.TSentryGrantOption;
 import org.apache.sentry.provider.db.service.thrift.TSentryPrivilege;
 import org.apache.sentry.service.thrift.ServiceConstants.PrivilegeScope;
+import org.junit.Assert;
 import org.junit.Test;
 
 import com.google.common.collect.Sets;
@@ -82,6 +81,34 @@ public class TestCommandUtil extends Assert {
   }
 
   @Test
+  public void testCreateCmdForRoleAddOrDeleteUser1() {
+    String createRoleAddGroupCmdResult =
+        CommandUtil.createCmdForRoleAddUser("testRole", getUserStr(1));
+    String createRoleAddGroupCmdExcepted = "GRANT ROLE testRole TO USER testUser1";
+    String createRoleDeleteGroupCmdResult =
+        CommandUtil.createCmdForRoleDeleteUser("testRole", getUserStr(1));
+    String createRoleDeleteGroupCmdExcepted = "REVOKE ROLE testRole FROM USER testUser1";
+
+    assertEquals(createRoleAddGroupCmdExcepted, createRoleAddGroupCmdResult);
+    assertEquals(createRoleDeleteGroupCmdExcepted, createRoleDeleteGroupCmdResult);
+  }
+
+  @Test
+  public void testCreateCmdForRoleAddOrDeleteUser2() {
+    String createRoleAddGroupCmdResult =
+        CommandUtil.createCmdForRoleAddUser("testRole", getUserStr(3));
+    String createRoleAddGroupCmdExcepted =
+        "GRANT ROLE testRole TO USER testUser1, testUser2, testUser3";
+    String createRoleDeleteGroupCmdResult =
+        CommandUtil.createCmdForRoleDeleteUser("testRole", getUserStr(3));
+    String createRoleDeleteGroupCmdExcepted =
+        "REVOKE ROLE testRole FROM USER testUser1, testUser2, testUser3";
+
+    assertEquals(createRoleAddGroupCmdExcepted, createRoleAddGroupCmdResult);
+    assertEquals(createRoleDeleteGroupCmdExcepted, createRoleDeleteGroupCmdResult);
+  }
+
+  @Test
   public void testCreateCmdForGrantOrRevokePrivilege1() {
     TAlterSentryRoleGrantPrivilegeRequest grantRequest = getGrantPrivilegeRequest();
     TAlterSentryRoleRevokePrivilegeRequest revokeRequest = getRevokePrivilegeRequest();
@@ -329,6 +356,17 @@ public class TestCommandUtil extends Assert {
     return sb.toString();
   }
 
+  private String getUserStr(int num) {
+    StringBuilder sb = new StringBuilder();
+    for (int i = 0; i < num; i++) {
+      if (i > 0) {
+        sb.append(", ");
+      }
+      sb.append("testUser" + (i + 1));
+    }
+    return sb.toString();
+  }
+
   private TAlterSentryRoleGrantPrivilegeRequest getGrantPrivilegeRequest() {
     TAlterSentryRoleGrantPrivilegeRequest request = new TAlterSentryRoleGrantPrivilegeRequest();
     request.setRoleName("testRole");


Mime
View raw message