diff --git a/core/src/main/java/org/acegisecurity/vote/AbstractAclVoter.java b/core/src/main/java/org/acegisecurity/vote/AbstractAclVoter.java new file mode 100644 index 0000000000..66033e0c00 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/vote/AbstractAclVoter.java @@ -0,0 +1,166 @@ +/* Copyright 2004, 2005 Acegi Technology Pty Limited + * + * Licensed 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 net.sf.acegisecurity.vote; + +import net.sf.acegisecurity.AuthorizationServiceException; +import net.sf.acegisecurity.ConfigAttribute; +import net.sf.acegisecurity.acl.AclEntry; +import net.sf.acegisecurity.acl.AclManager; + +import org.aopalliance.intercept.MethodInvocation; + +import org.springframework.util.Assert; + +import java.lang.reflect.Method; + + +/** + *

+ * Given a domain object instance passed as a method argument, ensures the + * principal has appropriate permission as defined by the {@link AclManager}. + *

+ * + *

+ * The AclManager is used to retrieve the access control list + * (ACL) permissions associated with a domain object instance for the current + * Authentication object. This class is designed to process + * {@link AclEntry}s that are subclasses of {@link + * net.sf.acegisecurity.acl.basic.BasicAclEntry} only. Generally these are + * obtained by using the {@link + * net.sf.acegisecurity.acl.basic.BasicAclProvider}. + *

+ * + *

+ * The voter will vote if any {@link ConfigAttribute#getAttribute()} matches + * the {@link #processConfigAttribute}. The provider will then locate the + * first method argument of type {@link #processDomainObjectClass}. Assuming + * that method argument is non-null, the provider will then lookup the ACLs + * from the AclManager and ensure the principal is {@link + * net.sf.acegisecurity.acl.basic.BasicAclEntry#isPermitted(int)} for at least + * one of the {@link #requirePermission}s. + *

+ * + *

+ * If the method argument is null, the voter will abstain from + * voting. If the method argument could not be found, an {@link + * net.sf.acegisecurity.AuthorizationServiceException} will be thrown. + *

+ * + *

+ * In practical terms users will typically setup a number of + * BasicAclEntryVoters. Each will have a different {@link + * #processDomainObjectClass}, {@link #processConfigAttribute} and {@link + * #requirePermission} combination. For example, a small application might + * employ the following instances of BasicAclEntryVoter: + * + *

+ * + * Alternatively, you could have used a common superclass or interface for the + * {@link #processDomainObjectClass} if both BankAccount and + * Customer had common parents. + *

+ * + *

+ * If the principal does not have sufficient permissions, the voter will vote + * to deny access. + *

+ * + *

+ * The AclManager is allowed to return any implementations of + * AclEntry it wishes. However, this provider will only be able + * to validate against AbstractBasicAclEntrys, and thus a vote to + * deny access will be made if no AclEntry is of type + * AbstractBasicAclEntry. + *

+ * + *

+ * All comparisons and prefixes are case sensitive. + *

+ * + * @author Ben Alex + * @version $Id$ + */ +public abstract class AbstractAclVoter implements AccessDecisionVoter { + //~ Instance fields ======================================================== + + private Class processDomainObjectClass; + + //~ Methods ================================================================ + + public void setProcessDomainObjectClass(Class processDomainObjectClass) { + Assert.notNull(processDomainObjectClass, + "processDomainObjectClass cannot be set to null"); + this.processDomainObjectClass = processDomainObjectClass; + } + + public Class getProcessDomainObjectClass() { + return processDomainObjectClass; + } + + /** + * This implementation supports only + * MethodSecurityInterceptor, because it queries the + * presented MethodInvocation. + * + * @param clazz the secure object + * + * @return true if the secure object is + * MethodInvocation, false otherwise + */ + public boolean supports(Class clazz) { + return (MethodInvocation.class.isAssignableFrom(clazz)); + } + + protected Object getDomainObjectInstance(Object secureObject) { + MethodInvocation invocation = (MethodInvocation) secureObject; + + // Check if this MethodInvocation provides the required argument + Method method = invocation.getMethod(); + Class[] params = method.getParameterTypes(); + + for (int i = 0; i < params.length; i++) { + if (processDomainObjectClass.isAssignableFrom(params[i])) { + return invocation.getArguments()[i]; + } + } + + throw new AuthorizationServiceException("MethodInvocation: " + + invocation + " did not provide any argument of type: " + + processDomainObjectClass); + } +} diff --git a/core/src/main/java/org/acegisecurity/vote/BasicAclEntryVoter.java b/core/src/main/java/org/acegisecurity/vote/BasicAclEntryVoter.java index 7568dd457a..78454affdd 100644 --- a/core/src/main/java/org/acegisecurity/vote/BasicAclEntryVoter.java +++ b/core/src/main/java/org/acegisecurity/vote/BasicAclEntryVoter.java @@ -23,8 +23,6 @@ import net.sf.acegisecurity.acl.AclEntry; import net.sf.acegisecurity.acl.AclManager; import net.sf.acegisecurity.acl.basic.BasicAclEntry; -import org.aopalliance.intercept.MethodInvocation; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -49,8 +47,8 @@ import java.util.Iterator; * (ACL) permissions associated with a domain object instance for the current * Authentication object. This class is designed to process * {@link AclEntry}s that are subclasses of {@link - * net.sf.acegisecurity.acl.basic.BasicAclEntry} only. Generally these - * are obtained by using the {@link + * net.sf.acegisecurity.acl.basic.BasicAclEntry} only. Generally these are + * obtained by using the {@link * net.sf.acegisecurity.acl.basic.BasicAclProvider}. *

* @@ -60,8 +58,8 @@ import java.util.Iterator; * first method argument of type {@link #processDomainObjectClass}. Assuming * that method argument is non-null, the provider will then lookup the ACLs * from the AclManager and ensure the principal is {@link - * net.sf.acegisecurity.acl.basic.BasicAclEntry#isPermitted(int)} for - * at least one of the {@link #requirePermission}s. + * net.sf.acegisecurity.acl.basic.BasicAclEntry#isPermitted(int)} for at least + * one of the {@link #requirePermission}s. *

* *

@@ -126,8 +124,8 @@ import java.util.Iterator; * @author Ben Alex * @version $Id$ */ -public class BasicAclEntryVoter implements AccessDecisionVoter, - InitializingBean { +public class BasicAclEntryVoter extends AbstractAclVoter + implements InitializingBean { //~ Static fields/initializers ============================================= private static final Log logger = LogFactory.getLog(BasicAclEntryVoter.class); @@ -135,7 +133,6 @@ public class BasicAclEntryVoter implements AccessDecisionVoter, //~ Instance fields ======================================================== private AclManager aclManager; - private Class processDomainObjectClass; private String internalMethod; private String processConfigAttribute; private int[] requirePermission; @@ -179,14 +176,6 @@ public class BasicAclEntryVoter implements AccessDecisionVoter, return processConfigAttribute; } - public void setProcessDomainObjectClass(Class processDomainObjectClass) { - this.processDomainObjectClass = processDomainObjectClass; - } - - public Class getProcessDomainObjectClass() { - return processDomainObjectClass; - } - public void setRequirePermission(int[] requirePermission) { this.requirePermission = requirePermission; } @@ -199,8 +188,6 @@ public class BasicAclEntryVoter implements AccessDecisionVoter, Assert.notNull(processConfigAttribute, "A processConfigAttribute is mandatory"); Assert.notNull(aclManager, "An aclManager is mandatory"); - Assert.notNull(processDomainObjectClass, - "A processDomainObjectClass is mandatory"); if ((requirePermission == null) || (requirePermission.length == 0)) { throw new IllegalArgumentException( @@ -217,20 +204,6 @@ public class BasicAclEntryVoter implements AccessDecisionVoter, } } - /** - * This implementation supports only - * MethodSecurityInterceptor, because it queries the - * presented MethodInvocation. - * - * @param clazz the secure object - * - * @return true if the secure object is - * MethodInvocation, false otherwise - */ - public boolean supports(Class clazz) { - return (MethodInvocation.class.isAssignableFrom(clazz)); - } - public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) { Iterator iter = config.getConfigAttributes(); @@ -305,7 +278,7 @@ public class BasicAclEntryVoter implements AccessDecisionVoter, for (int i = 0; i < acls.length; i++) { // Locate processable AclEntrys if (acls[i] instanceof BasicAclEntry) { - BasicAclEntry processableAcl = (BasicAclEntry) acls[i]; + BasicAclEntry processableAcl = (BasicAclEntry) acls[i]; // See if principal has any of the required permissions for (int y = 0; y < requirePermission.length; y++) { @@ -324,22 +297,4 @@ public class BasicAclEntryVoter implements AccessDecisionVoter, // No configuration attribute matched, so abstain return AccessDecisionVoter.ACCESS_ABSTAIN; } - - private Object getDomainObjectInstance(Object secureObject) { - MethodInvocation invocation = (MethodInvocation) secureObject; - - // Check if this MethodInvocation provides the required argument - Method method = invocation.getMethod(); - Class[] params = method.getParameterTypes(); - - for (int i = 0; i < params.length; i++) { - if (processDomainObjectClass.isAssignableFrom(params[i])) { - return invocation.getArguments()[i]; - } - } - - throw new AuthorizationServiceException("MethodInvocation: " - + invocation + " did not provide any argument of type: " - + processDomainObjectClass); - } } diff --git a/core/src/test/java/org/acegisecurity/vote/BasicAclEntryVoterTests.java b/core/src/test/java/org/acegisecurity/vote/BasicAclEntryVoterTests.java index 4160ec0a23..9f5c2e4c18 100644 --- a/core/src/test/java/org/acegisecurity/vote/BasicAclEntryVoterTests.java +++ b/core/src/test/java/org/acegisecurity/vote/BasicAclEntryVoterTests.java @@ -1,4 +1,4 @@ -/* Copyright 2004 Acegi Technology Pty Limited +/* Copyright 2004, 2005 Acegi Technology Pty Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -156,22 +156,10 @@ public class BasicAclEntryVoterTests extends TestCase { public void testStartupRejectsMissingProcessDomainObjectClass() throws Exception { - AclManager aclManager = new MockAclManager("domain1", "marissa", - new AclEntry[] {new MockAclEntry(), new SimpleAclEntry( - "marissa", new MockAclObjectIdentity(), null, - SimpleAclEntry.ADMINISTRATION), new SimpleAclEntry( - "marissa", new MockAclObjectIdentity(), null, - SimpleAclEntry.READ), new SimpleAclEntry("marissa", - new MockAclObjectIdentity(), null, SimpleAclEntry.DELETE)}); - - // Wire up a voter BasicAclEntryVoter voter = new BasicAclEntryVoter(); - voter.setAclManager(aclManager); - voter.setProcessConfigAttribute("FOO_ADMIN_OR_WRITE_ACCESS"); - voter.setRequirePermission(new int[] {SimpleAclEntry.ADMINISTRATION, SimpleAclEntry.WRITE}); try { - voter.afterPropertiesSet(); + voter.setProcessDomainObjectClass(null); fail("Should have thrown IllegalArgumentException"); } catch (IllegalArgumentException expected) { assertTrue(true);