You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@falcon.apache.org by ve...@apache.org on 2014/08/08 19:43:47 UTC

[8/9] git commit: FALCON-557 Add super-user who is authorized for all. Contributed by Venkatesh Seetharam

FALCON-557 Add super-user who is authorized for all. Contributed by Venkatesh Seetharam


Project: http://git-wip-us.apache.org/repos/asf/incubator-falcon/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-falcon/commit/84cc3684
Tree: http://git-wip-us.apache.org/repos/asf/incubator-falcon/tree/84cc3684
Diff: http://git-wip-us.apache.org/repos/asf/incubator-falcon/diff/84cc3684

Branch: refs/heads/master
Commit: 84cc3684de2b29bfd50fcaba755692dbf03cf478
Parents: 7b3e510
Author: Venkatesh Seetharam <ve...@apache.org>
Authored: Fri Aug 8 10:20:47 2014 -0700
Committer: Venkatesh Seetharam <ve...@apache.org>
Committed: Fri Aug 8 10:22:56 2014 -0700

----------------------------------------------------------------------
 .../security/DefaultAuthorizationProvider.java  | 106 ++++++++++++++-----
 common/src/main/resources/startup.properties    |   7 +-
 .../DefaultAuthorizationProviderTest.java       |  35 +++++-
 docs/src/site/twiki/Security.twiki              |  25 ++++-
 src/conf/startup.properties                     |   7 +-
 5 files changed, 145 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-falcon/blob/84cc3684/common/src/main/java/org/apache/falcon/security/DefaultAuthorizationProvider.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/falcon/security/DefaultAuthorizationProvider.java b/common/src/main/java/org/apache/falcon/security/DefaultAuthorizationProvider.java
index 5561429..c7e87f4 100644
--- a/common/src/main/java/org/apache/falcon/security/DefaultAuthorizationProvider.java
+++ b/common/src/main/java/org/apache/falcon/security/DefaultAuthorizationProvider.java
@@ -70,10 +70,26 @@ public class DefaultAuthorizationProvider implements AuthorizationProvider {
     private static final String ADMIN_USERS_KEY = FALCON_PREFIX + "admin.users";
     private static final String ADMIN_GROUPS_KEY = FALCON_PREFIX + "admin.groups";
 
+    /**
+     * The super-user is the user with the same identity as falcon process itself.
+     * Loosely, if you started falcon, then you are the super-user.
+     */
+    protected static final String SUPER_USER = System.getProperty("user.name");
+
+    /**
+     * Constant for the configuration property that indicates the super user group.
+     */
+    private static final String SUPER_USER_GROUP_KEY = FALCON_PREFIX + "superusergroup";
+
+    /**
+     * Super ser group.
+     */
+    private String superUserGroup;
     private Set<String> adminUsers;
     private Set<String> adminGroups;
 
     public DefaultAuthorizationProvider() {
+        superUserGroup = StartupProperties.get().getProperty(SUPER_USER_GROUP_KEY);
         adminUsers = getAdminNamesFromConfig(ADMIN_USERS_KEY);
         adminGroups = getAdminNamesFromConfig(ADMIN_GROUPS_KEY);
     }
@@ -104,25 +120,42 @@ public class DefaultAuthorizationProvider implements AuthorizationProvider {
                                   String entityType, String entityName,
                                   UserGroupInformation proxyUgi) throws AuthorizationException {
         Validate.notEmpty(resource, "Resource cannot be empty or null");
+        Validate.isTrue(RESOURCES.contains(resource), "Illegal resource: " + resource);
         Validate.notEmpty(action, "Action cannot be empty or null");
 
-        Set<String> groups = getGroupNames(proxyUgi);
-        String authenticatedUser = proxyUgi.getShortUserName();
-        LOG.info("Authorizing authenticatedUser={}, groups={} against resource={}, action={}, entity name={}, "
-                + "entity type={}", authenticatedUser, groups, resource, action, entityName, entityType);
+        LOG.info("Authorizing authenticatedUser={}, against resource={}, action={}, entity name={}, "
+                + "entity type={}", proxyUgi.getShortUserName(), resource, action, entityName, entityType);
+
+        if (isSuperUser(proxyUgi)) {
+            return;
+        }
 
         if ("admin".equals(resource)) {
-            authorizeAdminResource(authenticatedUser, groups, action);
+            if (!"version".equals(action)) {
+                authorizeAdminResource(proxyUgi, action);
+            }
         } else if ("entities".equals(resource) || "instance".equals(resource)) {
-            authorizeEntityResource(authenticatedUser, proxyUgi, entityName, entityType, action);
+            authorizeEntityResource(proxyUgi, entityName, entityType, action);
         } else if ("lineage".equals(resource)) {
-            authorizeLineageResource(authenticatedUser, action);
-        } else {
-            throw new AuthorizationException("Unknown resource: " + resource);
+            authorizeLineageResource(proxyUgi.getShortUserName(), action);
         }
     }
 
-    private Set<String> getGroupNames(UserGroupInformation proxyUgi) {
+    /**
+     * Determines if the authenticated user is the user who started this process
+     * or belongs to the super user group.
+     *
+     * @param authenticatedUGI UGI
+     * @return true if super user else false.
+     */
+    protected boolean isSuperUser(UserGroupInformation authenticatedUGI) {
+        final String authenticatedUser = authenticatedUGI.getShortUserName();
+        return SUPER_USER.equals(authenticatedUser)
+                || (!StringUtils.isEmpty(superUserGroup)
+                    && isUserInGroup(superUserGroup, authenticatedUGI));
+    }
+
+    protected Set<String> getGroupNames(UserGroupInformation proxyUgi) {
         HashSet<String> s = new HashSet<String>(Arrays.asList(proxyUgi.getGroupNames()));
         return Collections.unmodifiableSet(s);
     }
@@ -146,6 +179,10 @@ public class DefaultAuthorizationProvider implements AuthorizationProvider {
         LOG.info("Authorizing authenticatedUser={}, action={}, entity={}, type{}",
                 authenticatedUser, action, entityName, entityType);
 
+        if (isSuperUser(proxyUgi)) {
+            return;
+        }
+        
         checkUser(entityName, acl.getOwner(), acl.getGroup(), action, authenticatedUser, proxyUgi);
     }
 
@@ -164,7 +201,7 @@ public class DefaultAuthorizationProvider implements AuthorizationProvider {
                              String action, String authenticatedUser,
                              UserGroupInformation proxyUgi) throws AuthorizationException {
         if (isUserACLOwner(authenticatedUser, aclOwner)
-                || isUserInAclGroup(aclGroup, proxyUgi)) {
+                || isUserInGroup(aclGroup, proxyUgi)) {
             return;
         }
 
@@ -179,37 +216,52 @@ public class DefaultAuthorizationProvider implements AuthorizationProvider {
         throw new AuthorizationException(message.toString());
     }
 
+    /**
+     * Determines if the authenticated user is the entity ACL owner.
+     *
+     * @param authenticatedUser authenticated user
+     * @param aclOwner          entity ACL owner
+     * @return true if authenticated user is the entity acl owner, false otherwise.
+     */
     protected boolean isUserACLOwner(String authenticatedUser, String aclOwner) {
         return authenticatedUser.equals(aclOwner);
     }
 
-    protected boolean isUserInAclGroup(String aclGroup, UserGroupInformation proxyUgi) {
+    /**
+     * Checks if the user's group matches the entity ACL group.
+     *
+     * @param group    Entity ACL group.
+     * @param proxyUgi proxy ugi for the authenticated user.
+     * @return true if user groups contains entity acl group.
+     */
+    protected boolean isUserInGroup(String group, UserGroupInformation proxyUgi) {
         Set<String> groups = getGroupNames(proxyUgi);
-        return groups.contains(aclGroup);
+        return groups.contains(group);
     }
 
     /**
      * Check if the user has admin privileges.
      *
-     * @param user   user name.
-     * @param groups groups that the user belongs to.
-     * @param action admin action on the resource
+     * @param proxyUgi proxy ugi for the authenticated user.
+     * @param action   admin action on the resource.
      * @throws AuthorizationException if the user does not have admin privileges.
      */
-    protected void authorizeAdminResource(String user, Set<String> groups,
+    protected void authorizeAdminResource(UserGroupInformation proxyUgi,
                                           String action) throws AuthorizationException {
-        LOG.debug("Authorizing user={} for admin, action={}", user, action);
-        if (adminUsers.contains(user) || isUserInAdminGroups(groups)) {
+        final String authenticatedUser = proxyUgi.getShortUserName();
+        LOG.debug("Authorizing user={} for admin, action={}", authenticatedUser, action);
+        if (adminUsers.contains(authenticatedUser) || isUserInAdminGroups(proxyUgi)) {
             return;
         }
 
         LOG.error("Permission denied: user {} does not have admin privilege for action={}",
-                user, action);
-        throw new AuthorizationException("Permission denied: user=" + user
+                authenticatedUser, action);
+        throw new AuthorizationException("Permission denied: user=" + authenticatedUser
                 + " does not have admin privilege for action=" + action);
     }
 
-    protected boolean isUserInAdminGroups(Set<String> groups) {
+    protected boolean isUserInAdminGroups(UserGroupInformation proxyUgi) {
+        Set<String> groups = getGroupNames(proxyUgi);
         boolean isUserGroupInAdmin = false;
         for (String group : groups) {
             if (adminGroups.contains(group)) {
@@ -221,13 +273,13 @@ public class DefaultAuthorizationProvider implements AuthorizationProvider {
         return isUserGroupInAdmin;
     }
 
-    protected void authorizeEntityResource(String authenticatedUser, UserGroupInformation proxyUgi,
+    protected void authorizeEntityResource(UserGroupInformation proxyUgi,
                                            String entityName, String entityType,
                                            String action) throws AuthorizationException {
         Validate.notEmpty(entityType, "Entity type cannot be empty or null");
         LOG.debug("Authorizing authenticatedUser={} against entity/instance action={}, "
-                + "entity name={}, entity type={}", authenticatedUser, action, entityName,
-                entityType);
+                + "entity name={}, entity type={}",
+                proxyUgi.getShortUserName(), action, entityName, entityType);
 
         if (entityName != null) { // lifecycle actions
             Entity entity = getEntity(entityName, entityType);
@@ -260,8 +312,10 @@ public class DefaultAuthorizationProvider implements AuthorizationProvider {
             return ((org.apache.falcon.entity.v0.process.Process) entity).getACL();
 
         default:
-            throw new AuthorizationException("Cannot get owner for entity: " + entity.getName());
+            break;
         }
+
+        throw new AuthorizationException("Cannot get owner for entity: " + entity.getName());
     }
 
     protected void authorizeLineageResource(String authenticatedUser, String action) {

http://git-wip-us.apache.org/repos/asf/incubator-falcon/blob/84cc3684/common/src/main/resources/startup.properties
----------------------------------------------------------------------
diff --git a/common/src/main/resources/startup.properties b/common/src/main/resources/startup.properties
index 808fed6..0d49b4b 100644
--- a/common/src/main/resources/startup.properties
+++ b/common/src/main/resources/startup.properties
@@ -142,11 +142,14 @@ debug.libext.process.paths=${falcon.libext}
 # Authorization Enabled flag: false (default)|true
 *.falcon.security.authorization.enabled=false
 
+# The name of the group of super-users
+*.falcon.security.authorization.superusergroup=falcon
+
 # Admin Users, comma separated users
-*.falcon.security.authorization.admin.users=falcon,ambari-qa,seetharam
+*.falcon.security.authorization.admin.users=falcon,ambari-qa
 
 # Admin Group Membership, comma separated users
-*.falcon.security.authorization.admin.groups=falcon,testgroup,staff
+*.falcon.security.authorization.admin.groups=falcon,staff
 
 # Authorization Provider Implementation Fully Qualified Class Name
 *.falcon.security.authorization.provider=org.apache.falcon.security.DefaultAuthorizationProvider

http://git-wip-us.apache.org/repos/asf/incubator-falcon/blob/84cc3684/common/src/test/java/org/apache/falcon/security/DefaultAuthorizationProviderTest.java
----------------------------------------------------------------------
diff --git a/common/src/test/java/org/apache/falcon/security/DefaultAuthorizationProviderTest.java b/common/src/test/java/org/apache/falcon/security/DefaultAuthorizationProviderTest.java
index 0fd869e..125aa85 100644
--- a/common/src/test/java/org/apache/falcon/security/DefaultAuthorizationProviderTest.java
+++ b/common/src/test/java/org/apache/falcon/security/DefaultAuthorizationProviderTest.java
@@ -121,6 +121,35 @@ public class DefaultAuthorizationProviderTest {
         }
     }
 
+    @Test
+    public void testAuthorizeAdminResourceVersionAction() throws Exception {
+        UserGroupInformation proxyUgi = UserGroupInformation.createProxyUserForTesting(
+                "blah", realUser, new String[]{"blah-group", });
+
+        DefaultAuthorizationProvider provider = new DefaultAuthorizationProvider();
+        provider.authorizeResource("admin", "version", null, null, proxyUgi);
+    }
+
+    @Test
+    public void testAuthorizeSuperUser() throws Exception {
+        UserGroupInformation proxyUgi = UserGroupInformation.createProxyUserForTesting(
+                EntityBuilderTestUtil.USER, realUser, new String[]{"group", });
+
+        DefaultAuthorizationProvider provider = new DefaultAuthorizationProvider();
+        provider.authorizeResource("entities", "schedule", "feed", feedEntity.getName(), proxyUgi);
+        provider.authorizeResource("instance", "status", "feed", feedEntity.getName(), proxyUgi);
+    }
+
+    @Test
+    public void testAuthorizeSuperUserGroup() throws Exception {
+        UserGroupInformation proxyUgi = UserGroupInformation.createProxyUserForTesting(
+                "blah", realUser, new String[]{"falcon", });
+
+        DefaultAuthorizationProvider provider = new DefaultAuthorizationProvider();
+        provider.authorizeResource("entities", "schedule", "feed", feedEntity.getName(), proxyUgi);
+        provider.authorizeResource("instance", "status", "feed", feedEntity.getName(), proxyUgi);
+    }
+
     @DataProvider(name = "adminResourceActions")
     private Object[][] createAdminResourceActions() {
         return new Object[][] {
@@ -175,7 +204,7 @@ public class DefaultAuthorizationProviderTest {
                 "admin-user", realUser, new String[]{"admin-group", });
 
         DefaultAuthorizationProvider provider = new DefaultAuthorizationProvider();
-        provider.authorizeResource("admin", "version", null, null, proxyUgi);
+        provider.authorizeResource("admin", "stack", null, null, proxyUgi);
         Assert.fail("User does not belong to both admin-users not groups");
     }
 
@@ -236,7 +265,7 @@ public class DefaultAuthorizationProviderTest {
         provider.authorizeResource(resource, action, entityType, entityName, proxyUgi);
     }
 
-    @Test (expectedExceptions = AuthorizationException.class)
+    @Test (expectedExceptions = IllegalArgumentException.class)
     public void testAuthorizeBadResource() throws Exception {
         StartupProperties.get().setProperty("falcon.security.authorization.admin.users", "admin");
         StartupProperties.get().setProperty("falcon.security.authorization.admin.groups", "admin");
@@ -324,7 +353,7 @@ public class DefaultAuthorizationProviderTest {
                 "admin", realUser, new String[]{"admin", });
 
         DefaultAuthorizationProvider provider = new DefaultAuthorizationProvider();
-        provider.authorizeResource("entities", "status", "feed", processEntity.getName(), proxyUgi);
+        provider.authorizeResource("entities", "status", "process", feedEntity.getName(), proxyUgi);
         Assert.fail("Bad entity");
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-falcon/blob/84cc3684/docs/src/site/twiki/Security.twiki
----------------------------------------------------------------------
diff --git a/docs/src/site/twiki/Security.twiki b/docs/src/site/twiki/Security.twiki
index 4dc0f4d..f91c350 100644
--- a/docs/src/site/twiki/Security.twiki
+++ b/docs/src/site/twiki/Security.twiki
@@ -55,6 +55,19 @@ owner is the Owner of this entity.
 group is the one which has access to read.
 permission indicates the rwx is not enforced at this time.
 
+---+++ Super-User
+
+The super-user is the user with the same identity as falcon process itself. Loosely, if you
+started the falcon, then you are the super-user. The super-user can do anything in that
+permissions checks never fail for the super-user. There is no persistent notion of who was the
+super-user; when the falcon is started the process identity determines who is the super-user
+for now. The Falcon super-user does not have to be the super-user of the falcon host, nor is it
+necessary that all clusters have the same super-user. Also, an experimenter running Falcon on a
+personal workstation, conveniently becomes that installation's super-user without any configuration.
+
+Falcon also allows users to configure a super user group and allows users belonging to this
+group to be a super user.
+
 ---+++ Group Memberships
 
 Once a user has been authenticated and a username has been determined, the list of groups is
@@ -126,8 +139,8 @@ Only users belonging to admin users or groups have access to this resource. Admi
 determined by a static configuration parameter.
 
 | *Resource*                                             | *Description*                               | *Authorization*  |
+| [[restapi/AdminVersion][api/admin/version]]            | Get version of the server                   | No restriction   |
 | [[restapi/AdminStack][api/admin/stack]]                | Get stack of the server                     | Admin User/Group |
-| [[restapi/AdminVersion][api/admin/version]]            | Get version of the server                   | Admin User/Group |
 | [[restapi/AdminConfig][api/admin/config/:config-type]] | Get configuration information of the server | Admin User/Group |
 
 
@@ -230,7 +243,6 @@ startup configuration.
 *.falcon.security.authorization.enabled=true
 </verbatim>
 
-
 ---+++ Authorization Provider
 
 Falcon provides a basic implementation for Authorization bundled, org.apache.falcon.security .DefaultFalconAuthorizationProvider.
@@ -241,6 +253,15 @@ This can be overridden by custom implementations in the startup configuration.
 *.falcon.security.authorization.provider=org.apache.falcon.security.DefaultAuthorizationProvider
 </verbatim>
 
+---+++ Super User Group
+
+Super user group is determined by the configuration:
+
+<verbatim>
+# The name of the group of super-users
+*.falcon.security.authorization.superusergroup=falcon
+</verbatim>
+
 ---+++ Admin Membership
 
 Administrative users are determined by the configuration:

http://git-wip-us.apache.org/repos/asf/incubator-falcon/blob/84cc3684/src/conf/startup.properties
----------------------------------------------------------------------
diff --git a/src/conf/startup.properties b/src/conf/startup.properties
index 526656f..2c0bbbc 100644
--- a/src/conf/startup.properties
+++ b/src/conf/startup.properties
@@ -147,11 +147,14 @@ prism.configstore.listeners=org.apache.falcon.entity.v0.EntityGraph,\
 # Authorization Enabled flag: false (default)|true
 *.falcon.security.authorization.enabled=false
 
+# The name of the group of super-users
+*.falcon.security.authorization.superusergroup=falcon
+
 # Admin Users, comma separated users
-*.falcon.security.authorization.admin.users=falcon,ambari-qa,seetharam
+*.falcon.security.authorization.admin.users=falcon,ambari-qa
 
 # Admin Group Membership, comma separated users
-*.falcon.security.authorization.admin.groups=falcon,testgroup,staff
+*.falcon.security.authorization.admin.groups=falcon,staff
 
 # Authorization Provider Implementation Fully Qualified Class Name
 *.falcon.security.authorization.provider=org.apache.falcon.security.DefaultAuthorizationProvider