Repository: sentry
Updated Branches:
  refs/heads/master 48422f4cc -> 266857472


SENTRY-2174: Sentry authorization provider should now generate ACL for users. 
(Kalyan Kumar kalvagadda, reviewed-by Na Li and Sergio Pena)


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

Branch: refs/heads/master
Commit: 266857472e49bccb02a70328a3d1f45eb01863a4
Parents: 48422f4
Author: Kalyan Kumar Kalvagadda <kkal...@cloudera.com>
Authored: Thu May 17 12:01:15 2018 -0500
Committer: Kalyan Kumar Kalvagadda <kkal...@cloudera.com>
Committed: Thu May 17 12:01:15 2018 -0500

----------------------------------------------------------------------
 .../apache/sentry/hdfs/SentryPermissions.java   | 164 ++++++++++++++++---
 .../sentry/hdfs/TestSentryPermissions.java      | 118 +++++++++++++
 2 files changed, 260 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/sentry/blob/26685747/sentry-hdfs/sentry-hdfs-namenode-plugin/src/main/java/org/apache/sentry/hdfs/SentryPermissions.java
----------------------------------------------------------------------
diff --git 
a/sentry-hdfs/sentry-hdfs-namenode-plugin/src/main/java/org/apache/sentry/hdfs/SentryPermissions.java
 
b/sentry-hdfs/sentry-hdfs-namenode-plugin/src/main/java/org/apache/sentry/hdfs/SentryPermissions.java
index a88d8e2..c162ec1 100644
--- 
a/sentry-hdfs/sentry-hdfs-namenode-plugin/src/main/java/org/apache/sentry/hdfs/SentryPermissions.java
+++ 
b/sentry-hdfs/sentry-hdfs-namenode-plugin/src/main/java/org/apache/sentry/hdfs/SentryPermissions.java
@@ -25,6 +25,8 @@ import org.apache.hadoop.fs.permission.AclEntryType;
 import org.apache.hadoop.fs.permission.FsAction;
 import org.apache.sentry.hdfs.service.thrift.TPrivilegeEntity;
 import org.apache.sentry.hdfs.service.thrift.TPrivilegeEntityType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class SentryPermissions implements AuthzPermissions {
 
@@ -85,12 +87,89 @@ public class SentryPermissions implements AuthzPermissions {
     }
   }
 
+  /**
+   * Defines HDFS ACL entity to which ACL's are assigned.
+   */
+  public static class HdfsAclEntity {
+    private final AclEntryType type;
+    private final String value;
+
+    private HdfsAclEntity(AclEntryType type, String value) throws 
IllegalArgumentException {
+      if(type == AclEntryType.USER || type == AclEntryType.GROUP) {
+        this.type = type;
+        this.value = value;
+      } else {
+        throw new IllegalArgumentException("Invalid AclEntryType");
+      }
+    }
+    public static HdfsAclEntity constructAclEntityForUser(String user) {
+      return new HdfsAclEntity(AclEntryType.USER, user);
+    }
+
+    public static HdfsAclEntity constructAclEntityForGroup(String group) {
+      return new HdfsAclEntity(AclEntryType.GROUP, group);
+    }
+
+    public AclEntryType getType() {
+      return type;
+    }
+
+    public String getValue() {
+      return value;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj) {
+        return true;
+      }
+      if (obj == null) {
+        return false;
+      }
+      if (getClass() != obj.getClass()) {
+        return false;
+      }
+
+      HdfsAclEntity other = (HdfsAclEntity) obj;
+      if (type == null) {
+        if (other.type != null) {
+          return false;
+        }
+      } else if (!type.equals(other.type)) {
+        return false;
+      }
+
+      if (value == null) {
+        if (other.value != null) {
+          return false;
+        }
+      } else if (!value.equals(other.value)) {
+        return false;
+      }
+
+      return true;
+    }
+
+    @Override
+    public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((type == null) ? 0 : type.hashCode());
+      result = prime * result + ((value == null) ? 0 : value.hashCode());
+
+      return result;
+    }
+  }
+
   // Comparison of authorizable object should be case insensitive.
   private final Map<String, PrivilegeInfo> privileges = new TreeMap<String, 
PrivilegeInfo>(String.CASE_INSENSITIVE_ORDER);
   private Map<String, Set<String>> authzObjChildren = new TreeMap<String, 
Set<String>>(String.CASE_INSENSITIVE_ORDER);
 
   // RoleInfo should be case insensitive.
   private final Map<String, RoleInfo> roles = new TreeMap<String, 
RoleInfo>(String.CASE_INSENSITIVE_ORDER);
+  private static Logger LOG =
+          LoggerFactory.getLogger(SentryINodeAttributesProvider.class);
+
 
   String getParentAuthzObject(String authzObject) {
     if (authzObject != null) {
@@ -127,37 +206,56 @@ public class SentryPermissions implements 
AuthzPermissions {
     }
   }
 
-  private Map<String, FsAction> getGroupPerms(String authzObj) {
-    Map<String, FsAction> groupPerms;
+  /**
+   * Retrieves all the permissions granted to the object directly and 
inherited from
+   * the parents.
+   * @param authzObj Object name for which permissions are needed.
+   * @return Sentry Permissions
+   */
+  private Map<HdfsAclEntity, FsAction> getPerms(String authzObj) {
+    Map<HdfsAclEntity, FsAction> perms;
     String parent = getParentAuthzObject(authzObj);
     if (parent == null || parent.equals(authzObj)) {
-      groupPerms = new HashMap<String, FsAction>();
+      perms = new HashMap<HdfsAclEntity, FsAction>();
     } else {
-      groupPerms = getGroupPerms(parent);
+      perms = getPerms(parent);
     }
 
     PrivilegeInfo privilegeInfo = privileges.get(authzObj);
     if (privilegeInfo != null) {
       for (Map.Entry<TPrivilegeEntity, FsAction> privs : privilegeInfo
           .getAllPermissions().entrySet()) {
-        if(privs.getKey().getType() == TPrivilegeEntityType.ROLE) {
-          constructAclEntry(privs.getKey().getValue(), privs.getValue(), 
groupPerms);
-        }
+        constructHdfsPermissions(privs.getKey(), privs.getValue(), perms);
       }
     }
-    return groupPerms;
+    return perms;
   }
 
+  /**
+   * Constructs HDFS ACL's based on the permissions granted to the object 
directly
+   * and inherited from the parents.
+   * @param authzObj Object name for which ACL are needed
+   * @return HDFS ACL's
+   */
   @Override
   public List<AclEntry> getAcls(String authzObj) {
-    Map<String, FsAction> groupPerms = getGroupPerms(authzObj);
+    Map<HdfsAclEntity, FsAction> permissions = getPerms(authzObj);
+
     List<AclEntry> retList = new LinkedList<AclEntry>();
-    for (Map.Entry<String, FsAction> groupPerm : groupPerms.entrySet()) {
+    for (Map.Entry<HdfsAclEntity, FsAction> permission : 
permissions.entrySet()) {
       AclEntry.Builder builder = new AclEntry.Builder();
-      builder.setName(groupPerm.getKey());
-      builder.setType(AclEntryType.GROUP);
+      if(permission.getKey().getType() == AclEntryType.GROUP) {
+        builder.setName(permission.getKey().getValue());
+        builder.setType(AclEntryType.GROUP);
+      } else if (permission.getKey().getType() == AclEntryType.USER){
+        builder.setName(permission.getKey().getValue());
+        builder.setType(AclEntryType.USER);
+      } else {
+        LOG.warn("Permissions for Invalid AclEntryType: %s", 
permission.getKey().getType());
+        continue;
+      }
       builder.setScope(AclEntryScope.ACCESS);
-      FsAction action = groupPerm.getValue();
+      FsAction action = permission.getValue();
       if (action == FsAction.READ || action == FsAction.WRITE
           || action == FsAction.READ_WRITE) {
         action = action.or(FsAction.EXECUTE);
@@ -168,17 +266,39 @@ public class SentryPermissions implements 
AuthzPermissions {
     return retList;
   }
 
-  private void constructAclEntry(String role, FsAction permission,
-      Map<String, FsAction> groupPerms) {
-    RoleInfo roleInfo = roles.get(role);
-    if (roleInfo != null) {
-      for (String group : roleInfo.groups) {
-        FsAction fsAction = groupPerms.get(group);
-        if (fsAction == null) {
-          fsAction = FsAction.NONE;
+  /**
+   * Constructs HDFS Permissions entry based on the privileges granted.
+   * @param privilegeEntity Privilege Entity
+   * @param permission Permission granted
+   * @param perms
+   */
+  private void constructHdfsPermissions(TPrivilegeEntity privilegeEntity, 
FsAction permission,
+    Map<HdfsAclEntity, FsAction> perms) {
+    HdfsAclEntity aclEntry;
+    FsAction fsAction;
+    if(privilegeEntity.getType() == TPrivilegeEntityType.ROLE) {
+      RoleInfo roleInfo = roles.get(privilegeEntity.getValue());
+      if (roleInfo != null) {
+        for (String group : roleInfo.groups) {
+          aclEntry = HdfsAclEntity.constructAclEntityForGroup(group);
+          // fsAction is an aggregate of permissions granted to
+          // the group on the object and it's parents.
+          fsAction = perms.get(aclEntry);
+          if (fsAction == null) {
+            fsAction = FsAction.NONE;
+          }
+          perms.put(aclEntry, fsAction.or(permission));
         }
-        groupPerms.put(group, fsAction.or(permission));
       }
+    } else if(privilegeEntity.getType() == TPrivilegeEntityType.USER) {
+      aclEntry = 
HdfsAclEntity.constructAclEntityForUser(privilegeEntity.getValue());
+      // fsAction is an aggregate of permissions granted to
+      // the user on the object and it's parents.
+      fsAction = perms.get(aclEntry);
+      if (fsAction == null) {
+        fsAction = FsAction.NONE;
+      }
+      perms.put(aclEntry, fsAction.or(permission));
     }
   }
 

http://git-wip-us.apache.org/repos/asf/sentry/blob/26685747/sentry-hdfs/sentry-hdfs-namenode-plugin/src/test/java/org/apache/sentry/hdfs/TestSentryPermissions.java
----------------------------------------------------------------------
diff --git 
a/sentry-hdfs/sentry-hdfs-namenode-plugin/src/test/java/org/apache/sentry/hdfs/TestSentryPermissions.java
 
b/sentry-hdfs/sentry-hdfs-namenode-plugin/src/test/java/org/apache/sentry/hdfs/TestSentryPermissions.java
index dbce405..f0ca787 100644
--- 
a/sentry-hdfs/sentry-hdfs-namenode-plugin/src/test/java/org/apache/sentry/hdfs/TestSentryPermissions.java
+++ 
b/sentry-hdfs/sentry-hdfs-namenode-plugin/src/test/java/org/apache/sentry/hdfs/TestSentryPermissions.java
@@ -19,6 +19,13 @@
 
 package org.apache.sentry.hdfs;
 
+import java.util.List;
+
+import org.apache.hadoop.fs.permission.AclEntryType;
+import org.apache.hadoop.fs.permission.FsAction;
+import org.apache.sentry.hdfs.service.thrift.TPrivilegeEntity;
+import org.apache.sentry.hdfs.service.thrift.TPrivilegeEntityType;
+import org.apache.hadoop.fs.permission.AclEntry;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -37,4 +44,115 @@ public class TestSentryPermissions {
     Assert.assertNotNull(perm.getRoleInfo("admin"));
     Assert.assertNull(perm.getRoleInfo("doesNotExist"));
   }
+
+  /**
+   * Adds group permissions and role info and check is the ACL are properly 
generated.
+   */
+  @Test
+  public void testSentryRolePermissions() {
+    String authorizable = "db1.tb1";
+    FsAction fsAction = FsAction.ALL;
+    SentryPermissions perms = new SentryPermissions();
+    SentryPermissions.RoleInfo roleInfo = new 
SentryPermissions.RoleInfo("role1");
+    roleInfo.addGroup("group1");
+    roleInfo.addGroup("group2");
+    TPrivilegeEntity roleEntity = new 
TPrivilegeEntity(TPrivilegeEntityType.ROLE, "role1");
+
+    perms.addRoleInfo(roleInfo);
+
+    SentryPermissions.PrivilegeInfo pInfo = new 
SentryPermissions.PrivilegeInfo(authorizable);
+    pInfo.setPermission(roleEntity, fsAction);
+
+    perms.addPrivilegeInfo(pInfo);
+
+    List<AclEntry> acls  = perms.getAcls(authorizable);
+    Assert.assertEquals("Unexpected number of ACL entries received", 2, 
acls.size());
+    Assert.assertEquals("Unexpected permission", fsAction, 
acls.get(0).getPermission());
+  }
+
+  /**
+   * Adds user permissions and check is the ACL are properly generated.
+   */
+  @Test
+  public void testSentryUserPermissions() {
+    String authorizable = "db1.tb1";
+    FsAction fsAction = FsAction.ALL;
+    TPrivilegeEntity userEntity = new 
TPrivilegeEntity(TPrivilegeEntityType.USER, "user1");
+
+    SentryPermissions perms = new SentryPermissions();
+    SentryPermissions.PrivilegeInfo pInfo = new 
SentryPermissions.PrivilegeInfo(authorizable);
+    pInfo.setPermission(userEntity, fsAction);
+
+    perms.addPrivilegeInfo(pInfo);
+
+    List<AclEntry> acls  = perms.getAcls(authorizable);
+    Assert.assertEquals("Unexpected number of ACL entries received", 1, 
acls.size());
+    Assert.assertEquals("Unexpected permission", fsAction, 
acls.get(0).getPermission());
+  }
+
+  /**
+   * Adds aggregated user permissions and check is the ACL are properly 
generated.
+   */
+  @Test
+  public void testSentryAggregatedUserPermissions() {
+    String authorizable = null;
+    // Add read permission for database
+    authorizable = "db1";
+    TPrivilegeEntity userEntity = new 
TPrivilegeEntity(TPrivilegeEntityType.USER, "user1");
+
+    SentryPermissions perms = new SentryPermissions();
+    SentryPermissions.PrivilegeInfo pInfo = new 
SentryPermissions.PrivilegeInfo(authorizable);
+    pInfo.setPermission(userEntity, FsAction.READ_EXECUTE);
+    perms.addPrivilegeInfo(pInfo);
+
+    // Add write permission for a particular table in the database.
+    authorizable = "db1.tb1";
+    pInfo = new SentryPermissions.PrivilegeInfo(authorizable);
+    pInfo.setPermission(userEntity, FsAction.WRITE_EXECUTE);
+    perms.addPrivilegeInfo(pInfo);
+
+    List<AclEntry> acls  = perms.getAcls(authorizable);
+    Assert.assertEquals("Unexpected number of ACL entries received", 1, 
acls.size());
+    Assert.assertEquals("Unexpected permission", FsAction.ALL, 
acls.get(0).getPermission());
+  }
+
+  /**
+   * Adds user and group permissions and role info and check is the ACL are 
properly generated.
+   */
+  @Test
+  public void testSentryPermissions() {
+    String authorizable = "db1.tb1";
+    FsAction fsAction = FsAction.ALL;
+    SentryPermissions perms = new SentryPermissions();
+    SentryPermissions.RoleInfo roleInfo = new 
SentryPermissions.RoleInfo("role1");
+    roleInfo.addGroup("group1");
+    roleInfo.addGroup("group2");
+    TPrivilegeEntity roleEntity = new 
TPrivilegeEntity(TPrivilegeEntityType.ROLE, "role1");
+    TPrivilegeEntity userEntity = new 
TPrivilegeEntity(TPrivilegeEntityType.USER, "user1");
+
+    perms.addRoleInfo(roleInfo);
+
+    SentryPermissions.PrivilegeInfo pInfo = new 
SentryPermissions.PrivilegeInfo(authorizable);
+    pInfo.setPermission(roleEntity, fsAction);
+    pInfo.setPermission(userEntity, fsAction);
+
+    perms.addPrivilegeInfo(pInfo);
+
+    List<AclEntry> acls  = perms.getAcls(authorizable);
+    Assert.assertEquals("Unexpected number of ACL entries received", 3, 
acls.size());
+    Assert.assertEquals("Unexpected permission", fsAction, 
acls.get(0).getPermission());
+
+    int userAclCount = 0;
+    int groupAclCount = 0;
+
+    for (AclEntry entry : acls) {
+      if(entry.getType() == AclEntryType.GROUP) {
+        groupAclCount++;
+      } else if (entry.getType() == AclEntryType.USER) {
+        userAclCount++;
+      }
+    }
+    Assert.assertEquals("Unexpected number of User ACL", 1, userAclCount);
+    Assert.assertEquals("Unexpected number of Group ACL", 2, groupAclCount);
+  }
 }

Reply via email to