View Javadoc

1   /* Copyright 2004, 2005 Acegi Technology Pty Limited
2    *
3    * Licensed under the Apache License, Version 2.0 (the "License");
4    * you may not use this file except in compliance with the License.
5    * You may obtain a copy of the License at
6    *
7    *     http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  
16  package org.acegisecurity.vote;
17  
18  import org.acegisecurity.AuthorizationServiceException;
19  import org.acegisecurity.ConfigAttribute;
20  import org.acegisecurity.acl.AclEntry;
21  import org.acegisecurity.acl.AclManager;
22  
23  import org.aopalliance.intercept.MethodInvocation;
24  
25  import org.springframework.util.Assert;
26  
27  import java.lang.reflect.Method;
28  
29  
30  /***
31   * <p>
32   * Given a domain object instance passed as a method argument, ensures the
33   * principal has appropriate permission as defined by the {@link AclManager}.
34   * </p>
35   * 
36   * <p>
37   * The <code>AclManager</code> is used to retrieve the access control list
38   * (ACL) permissions associated with a domain object instance for the current
39   * <code>Authentication</code> object. This class is designed to process
40   * {@link AclEntry}s that are subclasses of {@link
41   * org.acegisecurity.acl.basic.BasicAclEntry} only. Generally these are
42   * obtained by using the {@link
43   * org.acegisecurity.acl.basic.BasicAclProvider}.
44   * </p>
45   * 
46   * <p>
47   * The voter will vote if any  {@link ConfigAttribute#getAttribute()} matches
48   * the {@link #processConfigAttribute}. The provider will then locate the
49   * first method argument of type {@link #processDomainObjectClass}. Assuming
50   * that method argument is non-null, the provider will then lookup the ACLs
51   * from the <code>AclManager</code> and ensure the principal is {@link
52   * org.acegisecurity.acl.basic.BasicAclEntry#isPermitted(int)} for at least
53   * one of the {@link #requirePermission}s.
54   * </p>
55   * 
56   * <p>
57   * If the method argument is <code>null</code>, the voter will abstain from
58   * voting. If the method argument could not be found, an {@link
59   * org.acegisecurity.AuthorizationServiceException} will be thrown.
60   * </p>
61   * 
62   * <p>
63   * In practical terms users will typically setup a number of
64   * <code>BasicAclEntryVoter</code>s. Each will have a different {@link
65   * #processDomainObjectClass}, {@link #processConfigAttribute} and {@link
66   * #requirePermission} combination. For example, a small application might
67   * employ the following instances of <code>BasicAclEntryVoter</code>:
68   * 
69   * <ul>
70   * <li>
71   * Process domain object class <code>BankAccount</code>, configuration
72   * attribute <code>VOTE_ACL_BANK_ACCONT_READ</code>, require permission
73   * <code>SimpleAclEntry.READ</code>
74   * </li>
75   * <li>
76   * Process domain object class <code>BankAccount</code>, configuration
77   * attribute <code>VOTE_ACL_BANK_ACCOUNT_WRITE</code>, require permission list
78   * <code>SimpleAclEntry.WRITE</code> and <code>SimpleAclEntry.CREATE</code>
79   * (allowing the principal to have <b>either</b> of these two permissions
80   * </li>
81   * <li>
82   * Process domain object class <code>Customer</code>, configuration attribute
83   * <code>VOTE_ACL_CUSTOMER_READ</code>, require permission
84   * <code>SimpleAclEntry.READ</code>
85   * </li>
86   * <li>
87   * Process domain object class <code>Customer</code>, configuration attribute
88   * <code>VOTE_ACL_CUSTOMER_WRITE</code>, require permission list
89   * <code>SimpleAclEntry.WRITE</code> and <code>SimpleAclEntry.CREATE</code>
90   * </li>
91   * </ul>
92   * 
93   * Alternatively, you could have used a common superclass or interface for the
94   * {@link #processDomainObjectClass} if both <code>BankAccount</code> and
95   * <code>Customer</code> had common parents.
96   * </p>
97   * 
98   * <p>
99   * If the principal does not have sufficient permissions, the voter will vote
100  * to deny access.
101  * </p>
102  * 
103  * <p>
104  * The <code>AclManager</code> is allowed to return any implementations of
105  * <code>AclEntry</code> it wishes. However, this provider will only be able
106  * to validate against <code>AbstractBasicAclEntry</code>s, and thus a vote to
107  * deny access will be made if no <code>AclEntry</code> is of type
108  * <code>AbstractBasicAclEntry</code>.
109  * </p>
110  * 
111  * <p>
112  * All comparisons and prefixes are case sensitive.
113  * </p>
114  *
115  * @author Ben Alex
116  * @version $Id: AbstractAclVoter.java,v 1.2 2005/11/17 00:55:47 benalex Exp $
117  */
118 public abstract class AbstractAclVoter implements AccessDecisionVoter {
119     //~ Instance fields ========================================================
120 
121     private Class processDomainObjectClass;
122 
123     //~ Methods ================================================================
124 
125     public void setProcessDomainObjectClass(Class processDomainObjectClass) {
126         Assert.notNull(processDomainObjectClass,
127             "processDomainObjectClass cannot be set to null");
128         this.processDomainObjectClass = processDomainObjectClass;
129     }
130 
131     public Class getProcessDomainObjectClass() {
132         return processDomainObjectClass;
133     }
134 
135     /***
136      * This implementation supports only
137      * <code>MethodSecurityInterceptor</code>, because it queries the
138      * presented <code>MethodInvocation</code>.
139      *
140      * @param clazz the secure object
141      *
142      * @return <code>true</code> if the secure object is
143      *         <code>MethodInvocation</code>, <code>false</code> otherwise
144      */
145     public boolean supports(Class clazz) {
146         return (MethodInvocation.class.isAssignableFrom(clazz));
147     }
148 
149     protected Object getDomainObjectInstance(Object secureObject) {
150         MethodInvocation invocation = (MethodInvocation) secureObject;
151 
152         // Check if this MethodInvocation provides the required argument
153         Method method = invocation.getMethod();
154         Class[] params = method.getParameterTypes();
155 
156         for (int i = 0; i < params.length; i++) {
157             if (processDomainObjectClass.isAssignableFrom(params[i])) {
158                 return invocation.getArguments()[i];
159             }
160         }
161 
162         throw new AuthorizationServiceException("MethodInvocation: "
163             + invocation + " did not provide any argument of type: "
164             + processDomainObjectClass);
165     }
166 }