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.AcegiMessageSource;
19 import org.acegisecurity.Authentication;
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 org.springframework.context.MessageSource;
30 import org.springframework.context.MessageSourceAware;
31 import org.springframework.context.support.MessageSourceAccessor;
32
33 import java.util.Iterator;
34
35
36 /**
37 * <p>Given a domain object instance returned from a secure object invocation, ensures the principal has
38 * appropriate permission as defined by the {@link AclService}.</p>
39 * <p>The <code>AclService</code> is used to retrieve the access control list (ACL) permissions associated with a
40 * domain object instance for the current <code>Authentication</code> object.</p>
41 * <p>This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()} matches the {@link
42 * #processConfigAttribute}. The provider will then lookup the ACLs from the <code>AclService</code> and ensure the
43 * principal is {@link org.acegisecurity.acls.Acl#isGranted(org.acegisecurity.acls.Permission[],
44 org.acegisecurity.acls.sid.Sid[], boolean) Acl.isGranted(Permission[], Sid[], boolean)}
45 * when presenting the {@link #requirePermission} array to that method.</p>
46 * <p>Often users will setup an <code>AclEntryAfterInvocationProvider</code> with a {@link
47 * #processConfigAttribute} of <code>AFTER_ACL_READ</code> and a {@link #requirePermission} of
48 * <code>BasePermission.READ</code>. These are also the defaults.</p>
49 * <p>If the principal does not have sufficient permissions, an <code>AccessDeniedException</code> will be thrown.</p>
50 * <p>If the provided <code>returnObject</code> is <code>null</code>, permission will always be granted and
51 * <code>null</code> will be returned.</p>
52 * <p>All comparisons and prefixes are case sensitive.</p>
53 */
54 public class AclEntryAfterInvocationProvider extends AbstractAclProvider implements MessageSourceAware {
55 //~ Static fields/initializers =====================================================================================
56
57 protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationProvider.class);
58
59 //~ Instance fields ================================================================================================
60
61 protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
62
63 //~ Constructors ===================================================================================================
64
65 public AclEntryAfterInvocationProvider(AclService aclService, Permission[] requirePermission) {
66 super(aclService, "AFTER_ACL_READ", requirePermission);
67 }
68
69 //~ Methods ========================================================================================================
70
71 public Object decide(Authentication authentication, Object object, ConfigAttributeDefinition config,
72 Object returnedObject) throws AccessDeniedException {
73 Iterator iter = config.getConfigAttributes();
74
75 while (iter.hasNext()) {
76 ConfigAttribute attr = (ConfigAttribute) iter.next();
77
78 if (this.supports(attr)) {
79 // Need to make an access decision on this invocation
80 if (returnedObject == null) {
81 // AclManager interface contract prohibits nulls
82 // As they have permission to null/nothing, grant access
83 if (logger.isDebugEnabled()) {
84 logger.debug("Return object is null, skipping");
85 }
86
87 return null;
88 }
89
90 if (!getProcessDomainObjectClass().isAssignableFrom(returnedObject.getClass())) {
91 if (logger.isDebugEnabled()) {
92 logger.debug("Return object is not applicable for this provider, skipping");
93 }
94
95 return returnedObject;
96 }
97
98 if (hasPermission(authentication, returnedObject)) {
99 return returnedObject;
100 } else {
101 if (logger.isDebugEnabled()) {
102 logger.debug("Denying access");
103 }
104
105 throw new AccessDeniedException(messages.getMessage(
106 "BasicAclEntryAfterInvocationProvider.noPermission",
107 new Object[] {authentication.getName(), returnedObject},
108 "Authentication {0} has NO permissions to the domain object {1}"));
109 }
110 }
111 }
112
113 return returnedObject;
114 }
115
116 public void setMessageSource(MessageSource messageSource) {
117 this.messages = new MessageSourceAccessor(messageSource);
118 }
119 }