[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