Clover coverage report - Acegi Security System for Spring - 1.0.0-RC1
Coverage timestamp: Mon Dec 5 2005 09:05:15 EST
file stats: LOC: 228   Methods: 11
NCLOC: 113   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
BasicAclEntryAfterInvocationProvider.java 91.7% 91.9% 90.9% 91.7%
coverage coverage
 1    /* Copyright 2004, 2005 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   
 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    //~ Static fields/initializers =============================================
 94   
 95    protected static final Log logger = LogFactory.getLog(BasicAclEntryAfterInvocationProvider.class);
 96   
 97    //~ Instance fields ========================================================
 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    //~ Methods ================================================================
 105   
 106  9 public void afterPropertiesSet() throws Exception {
 107  9 Assert.notNull(processConfigAttribute,
 108    "A processConfigAttribute is mandatory");
 109  8 Assert.notNull(aclManager, "An aclManager is mandatory");
 110  7 Assert.notNull(messages, "A message source must be set");
 111   
 112  7 if ((requirePermission == null) || (requirePermission.length == 0)) {
 113  1 throw new IllegalArgumentException(
 114    "One or more requirePermission entries is mandatory");
 115    }
 116    }
 117   
 118  7 public Object decide(Authentication authentication, Object object,
 119    ConfigAttributeDefinition config, Object returnedObject)
 120    throws AccessDeniedException {
 121  7 Iterator iter = config.getConfigAttributes();
 122   
 123  7 while (iter.hasNext()) {
 124  8 ConfigAttribute attr = (ConfigAttribute) iter.next();
 125   
 126  8 if (this.supports(attr)) {
 127    // Need to make an access decision on this invocation
 128  6 if (returnedObject == null) {
 129    // AclManager interface contract prohibits nulls
 130    // As they have permission to null/nothing, grant access
 131  1 if (logger.isDebugEnabled()) {
 132  0 logger.debug("Return object is null, skipping");
 133    }
 134   
 135  1 return null;
 136    }
 137   
 138  5 AclEntry[] acls = aclManager.getAcls(returnedObject,
 139    authentication);
 140   
 141  5 if ((acls == null) || (acls.length == 0)) {
 142  1 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  4 for (int i = 0; i < acls.length; i++) {
 149    // Locate processable AclEntrys
 150  6 if (acls[i] instanceof BasicAclEntry) {
 151  5 BasicAclEntry processableAcl = (BasicAclEntry) acls[i];
 152   
 153    // See if principal has any of the required permissions
 154  5 for (int y = 0; y < requirePermission.length; y++) {
 155  5 if (processableAcl.isPermitted(requirePermission[y])) {
 156  3 if (logger.isDebugEnabled()) {
 157  0 logger.debug(
 158    "Principal DOES have permission to return object: "
 159    + returnedObject + " due to ACL: "
 160    + processableAcl.toString());
 161    }
 162   
 163  3 return returnedObject;
 164    }
 165    }
 166    }
 167    }
 168   
 169    // No permissions match
 170  1 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  1 return returnedObject;
 178    }
 179   
 180  1 public AclManager getAclManager() {
 181  1 return aclManager;
 182    }
 183   
 184  10 public String getProcessConfigAttribute() {
 185  10 return processConfigAttribute;
 186    }
 187   
 188  2 public int[] getRequirePermission() {
 189  2 return requirePermission;
 190    }
 191   
 192  8 public void setAclManager(AclManager aclManager) {
 193  8 this.aclManager = aclManager;
 194    }
 195   
 196  0 public void setMessageSource(MessageSource messageSource) {
 197  0 this.messages = new MessageSourceAccessor(messageSource);
 198    }
 199   
 200  2 public void setProcessConfigAttribute(String processConfigAttribute) {
 201  2 this.processConfigAttribute = processConfigAttribute;
 202    }
 203   
 204  2 public void setRequirePermission(int[] requirePermission) {
 205  2 this.requirePermission = requirePermission;
 206    }
 207   
 208  8 public boolean supports(ConfigAttribute attribute) {
 209  8 if ((attribute.getAttribute() != null)
 210    && attribute.getAttribute().equals(getProcessConfigAttribute())) {
 211  6 return true;
 212    } else {
 213  2 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  1 public boolean supports(Class clazz) {
 226  1 return true;
 227    }
 228    }