1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.acegisecurity.afterinvocation;
17
18 import java.util.Iterator;
19
20 import org.acegisecurity.AccessDeniedException;
21 import org.acegisecurity.AcegiMessageSource;
22 import org.acegisecurity.Authentication;
23 import org.acegisecurity.ConfigAttribute;
24 import org.acegisecurity.ConfigAttributeDefinition;
25 import org.acegisecurity.acl.AclEntry;
26 import org.acegisecurity.acl.AclManager;
27 import org.acegisecurity.acl.basic.BasicAclEntry;
28 import org.acegisecurity.acl.basic.SimpleAclEntry;
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.springframework.beans.factory.InitializingBean;
32 import org.springframework.context.MessageSource;
33 import org.springframework.context.MessageSourceAware;
34 import org.springframework.context.support.MessageSourceAccessor;
35 import org.springframework.util.Assert;
36
37
38 /***
39 * <p>
40 * Given a domain object instance returned from a secure object invocation,
41 * ensures the principal has appropriate permission as defined by the {@link
42 * AclManager}.
43 * </p>
44 *
45 * <p>
46 * The <code>AclManager</code> is used to retrieve the access control list
47 * (ACL) permissions associated with a domain object instance for the current
48 * <code>Authentication</code> object. This class is designed to process
49 * {@link AclEntry}s that are subclasses of {@link
50 * org.acegisecurity.acl.basic.BasicAclEntry} only. Generally these are
51 * obtained by using the {@link org.acegisecurity.acl.basic.BasicAclProvider}.
52 * </p>
53 *
54 * <p>
55 * This after invocation provider will fire if any {@link
56 * ConfigAttribute#getAttribute()} matches the {@link
57 * #processConfigAttribute}. The provider will then lookup the ACLs from the
58 * <code>AclManager</code> and ensure the principal is {@link
59 * org.acegisecurity.acl.basic.BasicAclEntry#isPermitted(int)} for at least
60 * one of the {@link #requirePermission}s.
61 * </p>
62 *
63 * <p>
64 * Often users will setup a <code>BasicAclEntryAfterInvocationProvider</code>
65 * with a {@link #processConfigAttribute} of <code>AFTER_ACL_READ</code> and a
66 * {@link #requirePermission} of <code>SimpleAclEntry.READ</code>. These are
67 * also the defaults.
68 * </p>
69 *
70 * <p>
71 * If the principal does not have sufficient permissions, an
72 * <code>AccessDeniedException</code> will be thrown.
73 * </p>
74 *
75 * <p>
76 * The <code>AclManager</code> is allowed to return any implementations of
77 * <code>AclEntry</code> it wishes. However, this provider will only be able
78 * to validate against <code>BasicAclEntry</code>s, and thus access will be
79 * denied if no <code>AclEntry</code> is of type <code>BasicAclEntry</code>.
80 * </p>
81 *
82 * <p>
83 * If the provided <code>returnObject</code> is <code>null</code>, permission
84 * will always be granted and <code>null</code> will be returned.
85 * </p>
86 *
87 * <p>
88 * All comparisons and prefixes are case sensitive.
89 * </p>
90 */
91 public class BasicAclEntryAfterInvocationProvider
92 implements AfterInvocationProvider, InitializingBean, MessageSourceAware {
93
94
95 protected static final Log logger = LogFactory.getLog(BasicAclEntryAfterInvocationProvider.class);
96
97
98
99 private AclManager aclManager;
100 protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
101 private String processConfigAttribute = "AFTER_ACL_READ";
102 private int[] requirePermission = {SimpleAclEntry.READ};
103
104
105
106 public void afterPropertiesSet() throws Exception {
107 Assert.notNull(processConfigAttribute,
108 "A processConfigAttribute is mandatory");
109 Assert.notNull(aclManager, "An aclManager is mandatory");
110 Assert.notNull(messages, "A message source must be set");
111
112 if ((requirePermission == null) || (requirePermission.length == 0)) {
113 throw new IllegalArgumentException(
114 "One or more requirePermission entries is mandatory");
115 }
116 }
117
118 public Object decide(Authentication authentication, Object object,
119 ConfigAttributeDefinition config, Object returnedObject)
120 throws AccessDeniedException {
121 Iterator iter = config.getConfigAttributes();
122
123 while (iter.hasNext()) {
124 ConfigAttribute attr = (ConfigAttribute) iter.next();
125
126 if (this.supports(attr)) {
127
128 if (returnedObject == null) {
129
130
131 if (logger.isDebugEnabled()) {
132 logger.debug("Return object is null, skipping");
133 }
134
135 return null;
136 }
137
138 AclEntry[] acls = aclManager.getAcls(returnedObject,
139 authentication);
140
141 if ((acls == null) || (acls.length == 0)) {
142 throw new AccessDeniedException(messages.getMessage(
143 "BasicAclEntryAfterInvocationProvider.noPermission",
144 new Object[] {authentication.getName(), returnedObject},
145 "Authentication {0} has NO permissions at all to the domain object {1}"));
146 }
147
148 for (int i = 0; i < acls.length; i++) {
149
150 if (acls[i] instanceof BasicAclEntry) {
151 BasicAclEntry processableAcl = (BasicAclEntry) acls[i];
152
153
154 for (int y = 0; y < requirePermission.length; y++) {
155 if (processableAcl.isPermitted(requirePermission[y])) {
156 if (logger.isDebugEnabled()) {
157 logger.debug(
158 "Principal DOES have permission to return object: "
159 + returnedObject + " due to ACL: "
160 + processableAcl.toString());
161 }
162
163 return returnedObject;
164 }
165 }
166 }
167 }
168
169
170 throw new AccessDeniedException(messages.getMessage(
171 "BasicAclEntryAfterInvocationProvider.insufficientPermission",
172 new Object[] {authentication.getName(), returnedObject},
173 "Authentication {0} has ACL permissions to the domain object, but not the required ACL permission to the domain object {1}"));
174 }
175 }
176
177 return returnedObject;
178 }
179
180 public AclManager getAclManager() {
181 return aclManager;
182 }
183
184 public String getProcessConfigAttribute() {
185 return processConfigAttribute;
186 }
187
188 public int[] getRequirePermission() {
189 return requirePermission;
190 }
191
192 public void setAclManager(AclManager aclManager) {
193 this.aclManager = aclManager;
194 }
195
196 public void setMessageSource(MessageSource messageSource) {
197 this.messages = new MessageSourceAccessor(messageSource);
198 }
199
200 public void setProcessConfigAttribute(String processConfigAttribute) {
201 this.processConfigAttribute = processConfigAttribute;
202 }
203
204 public void setRequirePermission(int[] requirePermission) {
205 this.requirePermission = requirePermission;
206 }
207
208 public boolean supports(ConfigAttribute attribute) {
209 if ((attribute.getAttribute() != null)
210 && attribute.getAttribute().equals(getProcessConfigAttribute())) {
211 return true;
212 } else {
213 return false;
214 }
215 }
216
217 /***
218 * This implementation supports any type of class, because it does not
219 * query the presented secure object.
220 *
221 * @param clazz the secure object
222 *
223 * @return always <code>true</code>
224 */
225 public boolean supports(Class clazz) {
226 return true;
227 }
228 }