1
2
3
4
5
6
7
8
9
10
11
12
13
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
120
121 private Class processDomainObjectClass;
122
123
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
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 }