[jboss-cvs] Picketbox SVN: r48 - in trunk: picketbox/src/test/resources/config and 1 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Mon Mar 1 19:28:17 EST 2010


Author: sguilhen at redhat.com
Date: 2010-03-01 19:28:17 -0500 (Mon, 01 Mar 2010)
New Revision: 48

Modified:
   trunk/picketbox/src/test/java/org/picketbox/test/api/InstanceBasedAuthorizationUnitTestCase.java
   trunk/picketbox/src/test/resources/config/acl-authorization.conf
   trunk/security-jboss-sx/acl/src/main/java/org/jboss/security/acl/ACLProviderImpl.java
Log:
SECURITY-460: ACLProviderImpl.isAccessGranted now recursively checks the parent resource ACL when there is no ACL associated with a specific resource. This behavior is enabled by setting the checkParentACL property to true. Test cases have been added to InstanceBasedAuthorizationUnitTestCase.

Modified: trunk/picketbox/src/test/java/org/picketbox/test/api/InstanceBasedAuthorizationUnitTestCase.java
===================================================================
--- trunk/picketbox/src/test/java/org/picketbox/test/api/InstanceBasedAuthorizationUnitTestCase.java	2010-03-01 17:49:16 UTC (rev 47)
+++ trunk/picketbox/src/test/java/org/picketbox/test/api/InstanceBasedAuthorizationUnitTestCase.java	2010-03-02 00:28:17 UTC (rev 48)
@@ -29,6 +29,7 @@
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 
 import javax.security.auth.Subject;
 
@@ -40,9 +41,12 @@
 import org.jboss.security.acl.ACLEntryImpl;
 import org.jboss.security.acl.ACLPersistenceStrategy;
 import org.jboss.security.acl.BasicACLPermission;
+import org.jboss.security.acl.BitMaskPermission;
 import org.jboss.security.acl.CompositeACLPermission;
+import org.jboss.security.acl.EntitlementEntry;
 import org.jboss.security.authorization.AuthorizationContext;
 import org.jboss.security.authorization.Resource;
+import org.jboss.security.authorization.ResourceKeys;
 import org.jboss.security.authorization.ResourceType;
 import org.jboss.security.identity.plugins.IdentityFactory;
 import org.picketbox.config.PicketBoxConfiguration;
@@ -64,19 +68,22 @@
 
    private final String configFile = "config/acl-authorization.conf";
 
+   private final Resource importantResource1 = new TestResource("file://documents/project/important-file1");
+   
+   private final Resource importantResource2 = new TestResource("file://documents/project/important-file2");
+   
    private boolean initialized;
 
    @Override
    protected void setUp() throws Exception
    {
       super.setUp();
-      // setup the test ACLs only once.
+      // setup the static test ACLs only once.
       if (!this.initialized)
       {
          ACLPersistenceStrategy strategy = new MemoryOnlyACLStrategy();
 
-         // create an ACL for an important project file.
-         Resource importantResource = new TestResource("file://documents/project/important-file");
+         // create an ACL for important-file1.
          // project managers may read, update and delete the file.
          ACLEntry entry1 = new ACLEntryImpl(new CompositeACLPermission(BasicACLPermission.values()), "manager");
          // project developers can only view the contents of the file.
@@ -85,20 +92,26 @@
          entries.add(entry1);
          entries.add(entry2);
          // create and register the ACLs in the persistence strategy.
-         strategy.createACL(importantResource, entries);
+         strategy.createACL(this.importantResource1, entries);
+         
+         // we won't create an ACL for important-file2 - we will make it a child resource of important-file1.
+         Collection<Resource> children = new ArrayList<Resource>();
+         children.add(importantResource2);
+         this.importantResource2.getMap().put(ResourceKeys.PARENT_RESOURCE, this.importantResource1);
+         this.importantResource1.getMap().put(ResourceKeys.CHILD_RESOURCES, children);
          this.initialized = true;
       }
    }
 
    /**
     * <p>
-    * This test verifies if the instance-based authorization mechanism correctly grants/denies access to resources
-    * according to the permissions that have been specified in ACLs.  
+    * This test verifies if the instance-based authorization mechanism correctly grants/denies access to a resource
+    * according to the permissions that have been specified in the resource's ACL (importantResource1).  
     * </p>
     * 
     * @throws Exception if an error occurs while running the test.
     */
-   public void testInstanceBasedAuthorization() throws Exception
+   public void testAuthorizationBasedOnResourceACL() throws Exception
    {
       SecurityFactory.prepare();
       try
@@ -115,7 +128,6 @@
          assertTrue("Unexpected authentication error", result);
 
          // now bob wants to update the important project file. We must check if he has permission to do so.
-         Resource resource = new TestResource("file://documents/project/important-file");
          AuthorizationManager authzManager = SecurityFactory.getAuthorizationManager(securityDomainName);
          assertNotNull(authzManager);
 
@@ -129,7 +141,7 @@
          while (rolesEnum.hasMoreElements() && decision == AuthorizationContext.DENY)
          {
             Principal role = (Principal) rolesEnum.nextElement();
-            decision = authzManager.authorize(resource, IdentityFactory.createIdentity(role.getName()),
+            decision = authzManager.authorize(this.importantResource1, IdentityFactory.createIdentity(role.getName()),
                   BasicACLPermission.UPDATE);
          }
          // as we know, bob is a manager, so the final decision should allow him to update the project file.
@@ -151,7 +163,7 @@
          while (rolesEnum.hasMoreElements() && decision == AuthorizationContext.DENY)
          {
             Principal role = (Principal) rolesEnum.nextElement();
-            decision = authzManager.authorize(resource, IdentityFactory.createIdentity(role.getName()),
+            decision = authzManager.authorize(this.importantResource1, IdentityFactory.createIdentity(role.getName()),
                   BasicACLPermission.DELETE);
          }
          // as we know, alice is only a developer, so the final decision should prevent her from deleting the file.
@@ -163,6 +175,145 @@
       }
    }
 
+   /**
+    * <p>
+    * This test verifies if access to {@code important-file2} is granted/denied based on the permissions specified
+    * on its parent resource ACL. In other words, we haven't specified an ACL for {@code important-file2} but
+    * we made this resource a child of {@code important-file1}, so we expect the ACL of the parent resource
+    * ({@code important-file1}) to be used to perform the authorization check.
+    * </p>
+    * 
+    * @throws Exception if an error occurs while running the test.
+    */
+   public void testAuthorizationBasedOnParentResourceACL() throws Exception
+   {
+      SecurityFactory.prepare();
+      try
+      {
+         PicketBoxConfiguration idtrustConfig = new PicketBoxConfiguration();
+         idtrustConfig.load(configFile);
+
+         AuthenticationManager authManager = SecurityFactory.getAuthenticationManager(securityDomainName);
+         assertNotNull(authManager);
+
+         // bob, the project manager authenticates to the system.
+         Subject subject = new Subject();
+         boolean result = authManager.isValid(this.getPrincipal("bob"), "bobpass", subject);
+         assertTrue("Unexpected authentication error", result);
+
+         // now bob wants to update the important-file2. We must check if he has permission to do so.
+         AuthorizationManager authzManager = SecurityFactory.getAuthorizationManager(securityDomainName);
+         assertNotNull(authzManager);
+
+         // first we get bob's roles from the subject (the ACL entries have roles as keys).
+         Group roles = subject.getPrincipals(Group.class).iterator().next();
+         assertEquals("Unexpected group name", "Roles", roles.getName());
+         Enumeration<?> rolesEnum = roles.members();
+
+         // now we must check if any of bob's roles has the permission to update the file.
+         int decision = AuthorizationContext.DENY;
+         while (rolesEnum.hasMoreElements() && decision == AuthorizationContext.DENY)
+         {
+            Principal role = (Principal) rolesEnum.nextElement();
+            decision = authzManager.authorize(this.importantResource2, IdentityFactory.createIdentity(role.getName()),
+                  BasicACLPermission.UPDATE);
+         }
+         // as we know, bob is a manager, so the final decision should allow him to update the project file.
+         // this has been specified in the parent resource (important-file1) ACL.
+         assertEquals("Unexpected authorization decision", AuthorizationContext.PERMIT, decision);
+         
+         // now alice, the project developer, authenticates to the system.
+         subject = new Subject();
+         result = authManager.isValid(this.getPrincipal("alice"), "alicepass", subject);
+         assertTrue("Unexpected authentication error", result);
+         
+         // alice tries to delete the important-file2. We must check if she has sufficient permissions.
+         // first we get alice's roles from the subject.
+         roles = subject.getPrincipals(Group.class).iterator().next();
+         assertEquals("Unexpected group name", "Roles", roles.getName());
+         rolesEnum = roles.members();
+
+         // then we check if any of alice's roles has the permission to delete the file.
+         decision = AuthorizationContext.DENY;
+         while (rolesEnum.hasMoreElements() && decision == AuthorizationContext.DENY)
+         {
+            Principal role = (Principal) rolesEnum.nextElement();
+            decision = authzManager.authorize(this.importantResource2, IdentityFactory.createIdentity(role.getName()),
+                  BasicACLPermission.DELETE);
+         }
+         // as we know, alice is only a developer, so the final decision should prevent her from deleting the file.
+         assertEquals("Unexpected authorization decision", AuthorizationContext.DENY, decision);
+      }
+      finally
+      {
+         SecurityFactory.release();
+      }
+   }
+   
+   /**
+    * <p>
+    * This test verifies if the {@code ACLProvider#getEntitlements()} method returns the expected set of entries.
+    * </p>
+    * 
+    * @throws Exception if an error occurs while running the test.
+    */
+   public void testGetEntitlements() throws Exception
+   {
+      SecurityFactory.prepare();
+      try
+      {
+         PicketBoxConfiguration idtrustConfig = new PicketBoxConfiguration();
+         idtrustConfig.load(configFile);
+
+         // get all the permissions assigned to managers on important-file1 and all its child resources. 
+         AuthorizationManager authzManager = SecurityFactory.getAuthorizationManager(securityDomainName);
+         assertNotNull(authzManager);
+         
+         Set<EntitlementEntry> entries = authzManager.getEntitlements(EntitlementEntry.class, 
+               this.importantResource1, IdentityFactory.createIdentity("manager")).getEntitled();
+         assertNotNull("Unexpected null entitlement entry set", entries);
+         assertEquals("Unexpected number of entitlement entries", 2, entries.size());
+         
+         // check if each entry corresponds to each one of the important files and that managers have all permissions.
+         boolean validatedImportantFile1Entry = false;
+         boolean validatedImportantFile2Entry = false;
+         for(EntitlementEntry entry : entries)
+         {
+            if (entry.getResource().equals(this.importantResource1))
+               validatedImportantFile1Entry = true;
+            else if (entry.getResource().equals(this.importantResource2))
+               validatedImportantFile2Entry = true;
+            else
+               fail("Unexpected resource found in entitlement entry set");
+            // validate the permissions assigned to the manager role.
+            BitMaskPermission permission = (BitMaskPermission) entry.getPermission();
+            BitMaskPermission expectedPermission = new CompositeACLPermission(BasicACLPermission.values());
+            assertEquals("Unexpected permissions found in entry", expectedPermission.getMaskValue(), 
+                  permission.getMaskValue());
+         }
+         assertTrue("Important file 1 entry is missing", validatedImportantFile1Entry);
+         assertTrue("Important file 2 entry is missing", validatedImportantFile2Entry);
+         
+         // now get the entitlements entries for the developer role on important-file2.
+         entries = authzManager.getEntitlements(EntitlementEntry.class, 
+               this.importantResource2, IdentityFactory.createIdentity("developer")).getEntitled();
+         assertNotNull("Unexpected null entitlement entry set", entries);
+         assertEquals("Unexpected number of entitlement entries", 1, entries.size());
+         
+         // the single entry must contain the important-file2 resource and the READ permission.
+         EntitlementEntry entry = entries.iterator().next();
+         assertEquals("Unexpected resource found in entry", this.importantResource2, entry.getResource());
+         BitMaskPermission permission = (BitMaskPermission) entry.getPermission();
+         assertEquals("Unexpected permissions found in entry", BasicACLPermission.READ.getMaskValue(), 
+               permission.getMaskValue());
+      }
+      finally
+      {
+         SecurityFactory.release();
+      }
+      
+   }
+   
    private Principal getPrincipal(final String name)
    {
       return new Principal()
@@ -217,5 +368,11 @@
       {
          return this.resourceURI.hashCode();
       }
+      
+      @Override
+      public String toString()
+      {
+         return this.resourceURI.toString();
+      }
    }
 }

Modified: trunk/picketbox/src/test/resources/config/acl-authorization.conf
===================================================================
--- trunk/picketbox/src/test/resources/config/acl-authorization.conf	2010-03-01 17:49:16 UTC (rev 47)
+++ trunk/picketbox/src/test/resources/config/acl-authorization.conf	2010-03-02 00:28:17 UTC (rev 48)
@@ -16,6 +16,7 @@
        <acl>
           <acl-module code="org.jboss.security.acl.ACLProviderImpl" flag="required">
             <module-option name="persistenceStrategy">org.picketbox.test.pojos.MemoryOnlyACLStrategy</module-option>
+            <module-option name="checkParentACL">true</module-option>
           </acl-module>
        </acl>
     </application-policy>  

Modified: trunk/security-jboss-sx/acl/src/main/java/org/jboss/security/acl/ACLProviderImpl.java
===================================================================
--- trunk/security-jboss-sx/acl/src/main/java/org/jboss/security/acl/ACLProviderImpl.java	2010-03-01 17:49:16 UTC (rev 47)
+++ trunk/security-jboss-sx/acl/src/main/java/org/jboss/security/acl/ACLProviderImpl.java	2010-03-02 00:28:17 UTC (rev 48)
@@ -48,9 +48,13 @@
 
    private static final String PERSISTENCE_STRATEGY_OPTION = "persistenceStrategy";
 
+   private static final String CHECK_PARENT_ACL_OPTION = "checkParentACL";
+   
    /** persistence strategy used to retrieve the ACLs */
    protected ACLPersistenceStrategy strategy;
 
+   private boolean checkParentACL;
+   
    /*
     * (non-Javadoc)
     * 
@@ -62,6 +66,8 @@
       if (strategyClassName == null)
          strategyClassName = "org.jboss.security.acl.JPAPersistenceStrategy";
 
+      this.checkParentACL = Boolean.valueOf((String) options.get(CHECK_PARENT_ACL_OPTION)); 
+         
       try
       {
          Class<?> strategyClass = this.loadClass(strategyClassName);
@@ -228,7 +234,7 @@
    public boolean isAccessGranted(Resource resource, Identity identity, ACLPermission permission)
          throws AuthorizationException
    {
-      ACL acl = strategy.getACL(resource);
+      ACL acl = this.retrieveACL(resource);
       if (acl != null)
       {
          ACLEntry entry = acl.getEntry(identity);
@@ -244,6 +250,37 @@
          throw new AuthorizationException("Unable to locate an ACL for the resource " + resource);
    }
 
+   /**
+    * <p>
+    * Retrieves the ACL that is to be used to perform authorization decisions on the specified resource. If an ACL
+    * for the specified resource can be located by the strategy, this will be the returned ACL. On the other hand,
+    * if no ACL can be located for the resource then the method verifies if the {@code checkParentACL} property has
+    * been set:
+    * <ol>
+    *   <li>if {@code checkParentACL} is true, then check if the resource has a parent resource and try to locate an
+    *   ACL for the parent resource recursively. The idea here is that child resources "inherit" the permissions from
+    *   the parent resources (instead of providing an ACL that would be a copy of the parent ACL).</li>
+    *   <li>if {@code checkParentACL} is false, then {@code null} is returned.</li>
+    * </ol>
+    * 
+    * </p>
+    * 
+    * @param resource the {@code Resource} that is the target of the authorization decision.
+    * @return the {@code ACL} that is to be used to perform authorization decisions on the resource; {@code null} if
+    * no ACL can be found for the specified resource.
+    */
+   private ACL retrieveACL(Resource resource)
+   {
+      ACL acl = this.strategy.getACL(resource);
+      if (acl == null && this.checkParentACL)
+      {
+         Resource parent = (Resource) resource.getMap().get(ResourceKeys.PARENT_RESOURCE);
+         if (parent != null)
+            acl = retrieveACL(parent);
+      }
+      return acl;
+   }
+   
    /*
     * (non-Javadoc)
     * 




More information about the jboss-cvs-commits mailing list