001: /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
002: *
003: * Licensed under the Apache License, Version 2.0 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at
006: *
007: * http://www.apache.org/licenses/LICENSE-2.0
008: *
009: * Unless required by applicable law or agreed to in writing, software
010: * distributed under the License is distributed on an "AS IS" BASIS,
011: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: * See the License for the specific language governing permissions and
013: * limitations under the License.
014: */
015: package org.acegisecurity.afterinvocation;
016:
017: import org.acegisecurity.AccessDeniedException;
018: import org.acegisecurity.AcegiMessageSource;
019: import org.acegisecurity.Authentication;
020: import org.acegisecurity.ConfigAttribute;
021: import org.acegisecurity.ConfigAttributeDefinition;
022:
023: import org.acegisecurity.acls.AclService;
024: import org.acegisecurity.acls.Permission;
025:
026: import org.apache.commons.logging.Log;
027: import org.apache.commons.logging.LogFactory;
028:
029: import org.springframework.context.MessageSource;
030: import org.springframework.context.MessageSourceAware;
031: import org.springframework.context.support.MessageSourceAccessor;
032:
033: import java.util.Iterator;
034:
035: /**
036: * <p>Given a domain object instance returned from a secure object invocation, ensures the principal has
037: * appropriate permission as defined by the {@link AclService}.</p>
038: * <p>The <code>AclService</code> is used to retrieve the access control list (ACL) permissions associated with a
039: * domain object instance for the current <code>Authentication</code> object.</p>
040: * <p>This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()} matches the {@link
041: * #processConfigAttribute}. The provider will then lookup the ACLs from the <code>AclService</code> and ensure the
042: * principal is {@link org.acegisecurity.acls.Acl#isGranted(org.acegisecurity.acls.Permission[],
043: org.acegisecurity.acls.sid.Sid[], boolean) Acl.isGranted(Permission[], Sid[], boolean)}
044: * when presenting the {@link #requirePermission} array to that method.</p>
045: * <p>Often users will setup an <code>AclEntryAfterInvocationProvider</code> with a {@link
046: * #processConfigAttribute} of <code>AFTER_ACL_READ</code> and a {@link #requirePermission} of
047: * <code>BasePermission.READ</code>. These are also the defaults.</p>
048: * <p>If the principal does not have sufficient permissions, an <code>AccessDeniedException</code> will be thrown.</p>
049: * <p>If the provided <code>returnObject</code> is <code>null</code>, permission will always be granted and
050: * <code>null</code> will be returned.</p>
051: * <p>All comparisons and prefixes are case sensitive.</p>
052: */
053: public class AclEntryAfterInvocationProvider extends
054: AbstractAclProvider implements MessageSourceAware {
055: //~ Static fields/initializers =====================================================================================
056:
057: protected static final Log logger = LogFactory
058: .getLog(AclEntryAfterInvocationProvider.class);
059:
060: //~ Instance fields ================================================================================================
061:
062: protected MessageSourceAccessor messages = AcegiMessageSource
063: .getAccessor();
064:
065: //~ Constructors ===================================================================================================
066:
067: public AclEntryAfterInvocationProvider(AclService aclService,
068: Permission[] requirePermission) {
069: super (aclService, "AFTER_ACL_READ", requirePermission);
070: }
071:
072: //~ Methods ========================================================================================================
073:
074: public Object decide(Authentication authentication, Object object,
075: ConfigAttributeDefinition config, Object returnedObject)
076: throws AccessDeniedException {
077: Iterator iter = config.getConfigAttributes();
078:
079: while (iter.hasNext()) {
080: ConfigAttribute attr = (ConfigAttribute) iter.next();
081:
082: if (this .supports(attr)) {
083: // Need to make an access decision on this invocation
084: if (returnedObject == null) {
085: // AclManager interface contract prohibits nulls
086: // As they have permission to null/nothing, grant access
087: if (logger.isDebugEnabled()) {
088: logger.debug("Return object is null, skipping");
089: }
090:
091: return null;
092: }
093:
094: if (!getProcessDomainObjectClass().isAssignableFrom(
095: returnedObject.getClass())) {
096: if (logger.isDebugEnabled()) {
097: logger
098: .debug("Return object is not applicable for this provider, skipping");
099: }
100:
101: return returnedObject;
102: }
103:
104: if (hasPermission(authentication, returnedObject)) {
105: return returnedObject;
106: } else {
107: if (logger.isDebugEnabled()) {
108: logger.debug("Denying access");
109: }
110:
111: throw new AccessDeniedException(
112: messages
113: .getMessage(
114: "BasicAclEntryAfterInvocationProvider.noPermission",
115: new Object[] {
116: authentication
117: .getName(),
118: returnedObject },
119: "Authentication {0} has NO permissions to the domain object {1}"));
120: }
121: }
122: }
123:
124: return returnedObject;
125: }
126:
127: public void setMessageSource(MessageSource messageSource) {
128: this .messages = new MessageSourceAccessor(messageSource);
129: }
130: }
|