1 /* Copyright 2004, 2005, 2006 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 package org.acegisecurity.afterinvocation;
16
17 import org.acegisecurity.AccessDeniedException;
18 import org.acegisecurity.Authentication;
19 import org.acegisecurity.AuthorizationServiceException;
20 import org.acegisecurity.ConfigAttribute;
21 import org.acegisecurity.ConfigAttributeDefinition;
22
23 import org.acegisecurity.acls.AclService;
24 import org.acegisecurity.acls.Permission;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28
29 import java.util.Collection;
30 import java.util.Iterator;
31
32
33 /**
34 * <p>Given a <code>Collection</code> of domain object instances returned from a secure object invocation, remove
35 * any <code>Collection</code> elements the principal does not have appropriate permission to access as defined by the
36 * {@link AclService}.</p>
37 * <p>The <code>AclService</code> is used to retrieve the access control list (ACL) permissions associated with
38 * each <code>Collection</code> domain object instance element for the current <code>Authentication</code> object.</p>
39 * <p>This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()} matches the {@link
40 * #processConfigAttribute}. The provider will then lookup the ACLs from the <code>AclService</code> and ensure the
41 * principal is
42 * {@link org.acegisecurity.acls.Acl#isGranted(org.acegisecurity.acls.Permission[],
43 * org.acegisecurity.acls.sid.Sid[], boolean) Acl.isGranted(Permission[], Sid[], boolean)}
44 * when presenting the {@link #requirePermission} array to that method.</p>
45 * <p>If the principal does not have permission, that element will not be included in the returned
46 * <code>Collection</code>.</p>
47 * <p>Often users will setup a <code>BasicAclEntryAfterInvocationProvider</code> with a {@link
48 * #processConfigAttribute} of <code>AFTER_ACL_COLLECTION_READ</code> and a {@link #requirePermission} of
49 * <code>BasePermission.READ</code>. These are also the defaults.</p>
50 * <p>If the provided <code>returnObject</code> is <code>null</code>, a <code>null</code><code>Collection</code>
51 * will be returned. If the provided <code>returnObject</code> is not a <code>Collection</code>, an {@link
52 * AuthorizationServiceException} will be thrown.</p>
53 * <p>All comparisons and prefixes are case sensitive.</p>
54 *
55 * @author Ben Alex
56 * @author Paulo Neves
57 * @version $Id: AclEntryAfterInvocationCollectionFilteringProvider.java 1784 2007-02-24 21:00:24Z luke_t $
58 */
59 public class AclEntryAfterInvocationCollectionFilteringProvider extends AbstractAclProvider {
60 //~ Static fields/initializers =====================================================================================
61
62 protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationCollectionFilteringProvider.class);
63
64 //~ Constructors ===================================================================================================
65
66 public AclEntryAfterInvocationCollectionFilteringProvider(AclService aclService, Permission[] requirePermission) {
67 super(aclService, "AFTER_ACL_COLLECTION_READ", requirePermission);
68 }
69
70 //~ Methods ========================================================================================================
71
72 public Object decide(Authentication authentication, Object object, ConfigAttributeDefinition config,
73 Object returnedObject) throws AccessDeniedException {
74 Iterator iter = config.getConfigAttributes();
75
76 while (iter.hasNext()) {
77 ConfigAttribute attr = (ConfigAttribute) iter.next();
78
79 if (this.supports(attr)) {
80 // Need to process the Collection for this invocation
81 if (returnedObject == null) {
82 if (logger.isDebugEnabled()) {
83 logger.debug("Return object is null, skipping");
84 }
85
86 return null;
87 }
88
89 Filterer filterer = null;
90
91 if (returnedObject instanceof Collection) {
92 Collection collection = (Collection) returnedObject;
93 filterer = new CollectionFilterer(collection);
94 } else if (returnedObject.getClass().isArray()) {
95 Object[] array = (Object[]) returnedObject;
96 filterer = new ArrayFilterer(array);
97 } else {
98 throw new AuthorizationServiceException("A Collection or an array (or null) was required as the "
99 + "returnedObject, but the returnedObject was: " + returnedObject);
100 }
101
102 // Locate unauthorised Collection elements
103 Iterator collectionIter = filterer.iterator();
104
105 while (collectionIter.hasNext()) {
106 Object domainObject = collectionIter.next();
107
108 boolean hasPermission = false;
109
110 if (domainObject == null) {
111 hasPermission = true;
112 } else if (!getProcessDomainObjectClass().isAssignableFrom(domainObject.getClass())) {
113 hasPermission = true;
114 } else {
115 hasPermission = hasPermission(authentication, domainObject);
116
117 if (!hasPermission) {
118 filterer.remove(domainObject);
119
120 if (logger.isDebugEnabled()) {
121 logger.debug("Principal is NOT authorised for element: " + domainObject);
122 }
123 }
124 }
125 }
126
127 return filterer.getFilteredObject();
128 }
129 }
130
131 return returnedObject;
132 }
133 }