Coverage Report - org.acegisecurity.afterinvocation.BasicAclEntryAfterInvocationCollectionFilteringProvider
 
Classes in this File Line Coverage Branch Coverage Complexity
BasicAclEntryAfterInvocationCollectionFilteringProvider
86% 
100% 
3
 
 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.acl.AclEntry;
 24  
 import org.acegisecurity.acl.AclManager;
 25  
 import org.acegisecurity.acl.basic.BasicAclEntry;
 26  
 import org.acegisecurity.acl.basic.SimpleAclEntry;
 27  
 
 28  
 import org.apache.commons.logging.Log;
 29  
 import org.apache.commons.logging.LogFactory;
 30  
 
 31  
 import org.springframework.beans.factory.InitializingBean;
 32  
 
 33  
 import org.springframework.util.Assert;
 34  
 
 35  
 import java.util.Collection;
 36  
 import java.util.Iterator;
 37  
 
 38  
 
 39  
 /**
 40  
  * <p>Given a <code>Collection</code> of domain object instances returned from a secure object invocation, remove
 41  
  * any <code>Collection</code> elements the principal does not have appropriate permission to access as defined by the
 42  
  * {@link AclManager}.</p>
 43  
  *  <p>The <code>AclManager</code> is used to retrieve the access control list (ACL) permissions associated with
 44  
  * each <code>Collection</code>  domain object instance element for the current <code>Authentication</code> object.
 45  
  * This class is designed to process {@link AclEntry}s that are subclasses of {@link
 46  
  * org.acegisecurity.acl.basic.BasicAclEntry} only. Generally these are obtained by using the {@link
 47  
  * org.acegisecurity.acl.basic.BasicAclProvider}.</p>
 48  
  *  <p>This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()} matches the {@link
 49  
  * #processConfigAttribute}. The provider will then lookup the ACLs from the <code>AclManager</code> and ensure the
 50  
  * principal is {@link org.acegisecurity.acl.basic.BasicAclEntry#isPermitted(int)} for at least one of the {@link
 51  
  * #requirePermission}s for each <code>Collection</code> element. If the principal does not have at least one of the
 52  
  * permissions, that element will not be included in the returned <code>Collection</code>.</p>
 53  
  *  <p>Often users will setup a <code>BasicAclEntryAfterInvocationProvider</code> with a {@link
 54  
  * #processConfigAttribute} of <code>AFTER_ACL_COLLECTION_READ</code> and a {@link #requirePermission} of
 55  
  * <code>SimpleAclEntry.READ</code>. These are also the defaults.</p>
 56  
  *  <p>The <code>AclManager</code> is allowed to return any implementations of <code>AclEntry</code> it wishes.
 57  
  * However, this provider will only be able to validate against <code>BasicAclEntry</code>s, and thus a
 58  
  * <code>Collection</code> element will be filtered from the resulting <code>Collection</code> if no
 59  
  * <code>AclEntry</code> is of type <code>BasicAclEntry</code>.</p>
 60  
  *  <p>If the provided <code>returnObject</code> is <code>null</code>, a <code>null</code><code>Collection</code>
 61  
  * will be returned. If the provided <code>returnObject</code> is not a <code>Collection</code>, an {@link
 62  
  * AuthorizationServiceException} will be thrown.</p>
 63  
  *  <p>All comparisons and prefixes are case sensitive.</p>
 64  
  *
 65  
  * @author Ben Alex
 66  
  * @author Paulo Neves
 67  
  * @version $Id: BasicAclEntryAfterInvocationCollectionFilteringProvider.java 1784 2007-02-24 21:00:24Z luke_t $
 68  
  */
 69  12
 public class BasicAclEntryAfterInvocationCollectionFilteringProvider implements AfterInvocationProvider,
 70  
     InitializingBean {
 71  
     //~ Static fields/initializers =====================================================================================
 72  
 
 73  3
     protected static final Log logger = LogFactory.getLog(BasicAclEntryAfterInvocationCollectionFilteringProvider.class);
 74  
 
 75  
     //~ Instance fields ================================================================================================
 76  
 
 77  
     private AclManager aclManager;
 78  12
     private Class processDomainObjectClass = Object.class;
 79  12
     private String processConfigAttribute = "AFTER_ACL_COLLECTION_READ";
 80  12
     private int[] requirePermission = {SimpleAclEntry.READ};
 81  
 
 82  
     //~ Methods ========================================================================================================
 83  
 
 84  
     public void afterPropertiesSet() throws Exception {
 85  11
         Assert.notNull(processConfigAttribute, "A processConfigAttribute is mandatory");
 86  10
         Assert.notNull(aclManager, "An aclManager is mandatory");
 87  
 
 88  9
         if ((requirePermission == null) || (requirePermission.length == 0)) {
 89  1
             throw new IllegalArgumentException("One or more requirePermission entries is mandatory");
 90  
         }
 91  8
     }
 92  
 
 93  
     public Object decide(Authentication authentication, Object object, ConfigAttributeDefinition config,
 94  
         Object returnedObject) throws AccessDeniedException {
 95  9
         Iterator iter = config.getConfigAttributes();
 96  
 
 97  11
         while (iter.hasNext()) {
 98  10
             ConfigAttribute attr = (ConfigAttribute) iter.next();
 99  
 
 100  10
             if (this.supports(attr)) {
 101  
                 // Need to process the Collection for this invocation
 102  8
                 if (returnedObject == null) {
 103  1
                     if (logger.isDebugEnabled()) {
 104  0
                         logger.debug("Return object is null, skipping");
 105  
                     }
 106  
 
 107  1
                     return null;
 108  
                 }
 109  
 
 110  7
                 Filterer filterer = null;
 111  
 
 112  7
                 if (returnedObject instanceof Collection) {
 113  5
                     Collection collection = (Collection) returnedObject;
 114  5
                     filterer = new CollectionFilterer(collection);
 115  5
                 } else if (returnedObject.getClass().isArray()) {
 116  1
                     Object[] array = (Object[]) returnedObject;
 117  1
                     filterer = new ArrayFilterer(array);
 118  1
                 } else {
 119  1
                     throw new AuthorizationServiceException("A Collection or an array (or null) was required as the "
 120  
                             + "returnedObject, but the returnedObject was: " + returnedObject);
 121  
                 }
 122  
 
 123  
                 // Locate unauthorised Collection elements
 124  6
                 Iterator collectionIter = filterer.iterator();
 125  
 
 126  30
                 while (collectionIter.hasNext()) {
 127  24
                     Object domainObject = collectionIter.next();
 128  
 
 129  24
                     boolean hasPermission = false;
 130  
 
 131  24
                     if (domainObject == null) {
 132  0
                         hasPermission = true;
 133  24
                     } else if (!processDomainObjectClass.isAssignableFrom(domainObject.getClass())) {
 134  0
                         hasPermission = true;
 135  
                     } else {
 136  24
                         AclEntry[] acls = aclManager.getAcls(domainObject, authentication);
 137  
 
 138  24
                         if ((acls != null) && (acls.length != 0)) {
 139  18
                             for (int i = 0; i < acls.length; i++) {
 140  
                                 // Locate processable AclEntrys
 141  13
                                 if (acls[i] instanceof BasicAclEntry) {
 142  9
                                     BasicAclEntry processableAcl = (BasicAclEntry) acls[i];
 143  
 
 144  
                                     // See if principal has any of the required permissions
 145  18
                                     for (int y = 0; y < requirePermission.length; y++) {
 146  9
                                         if (processableAcl.isPermitted(requirePermission[y])) {
 147  4
                                             hasPermission = true;
 148  
 
 149  4
                                             if (logger.isDebugEnabled()) {
 150  0
                                                 logger.debug("Principal is authorised for element: " + domainObject
 151  
                                                     + " due to ACL: " + processableAcl.toString());
 152  
                                             }
 153  
                                         }
 154  
                                     }
 155  
                                 }
 156  
                             }
 157  
                         }
 158  
 
 159  24
                         if (!hasPermission) {
 160  20
                             filterer.remove(domainObject);
 161  
 
 162  20
                             if (logger.isDebugEnabled()) {
 163  0
                                 logger.debug("Principal is NOT authorised for element: " + domainObject);
 164  
                             }
 165  
                         }
 166  
                     }
 167  24
                 }
 168  
 
 169  6
                 return filterer.getFilteredObject();
 170  
             }
 171  2
         }
 172  
 
 173  1
         return returnedObject;
 174  
     }
 175  
 
 176  
     public AclManager getAclManager() {
 177  2
         return aclManager;
 178  
     }
 179  
 
 180  
     public String getProcessConfigAttribute() {
 181  12
         return processConfigAttribute;
 182  
     }
 183  
 
 184  
     public int[] getRequirePermission() {
 185  2
         return requirePermission;
 186  
     }
 187  
 
 188  
     public void setAclManager(AclManager aclManager) {
 189  10
         this.aclManager = aclManager;
 190  10
     }
 191  
 
 192  
     public void setProcessConfigAttribute(String processConfigAttribute) {
 193  2
         this.processConfigAttribute = processConfigAttribute;
 194  2
     }
 195  
 
 196  
     public void setProcessDomainObjectClass(Class processDomainObjectClass) {
 197  0
         Assert.notNull(processDomainObjectClass, "processDomainObjectClass cannot be set to null");
 198  0
         this.processDomainObjectClass = processDomainObjectClass;
 199  0
     }
 200  
 
 201  
     public void setRequirePermission(int[] requirePermission) {
 202  2
         this.requirePermission = requirePermission;
 203  2
     }
 204  
 
 205  
     /**
 206  
      * Allow setting permissions with String literals instead of integers as {@link
 207  
      * #setRequirePermission(int[])}
 208  
      *
 209  
      * @param requiredPermissions permission literals
 210  
      *
 211  
      * @see SimpleAclEntry#parsePermissions(String[]) for valid values
 212  
      */
 213  
     public void setRequirePermissionFromString(String[] requiredPermissions) {
 214  0
         setRequirePermission(SimpleAclEntry.parsePermissions(requiredPermissions));
 215  0
     }
 216  
 
 217  
     public boolean supports(ConfigAttribute attribute) {
 218  10
         if ((attribute.getAttribute() != null) && attribute.getAttribute().equals(getProcessConfigAttribute())) {
 219  8
             return true;
 220  
         } else {
 221  2
             return false;
 222  
         }
 223  
     }
 224  
 
 225  
     /**
 226  
      * This implementation supports any type of class, because it does not query the presented secure object.
 227  
      *
 228  
      * @param clazz the secure object
 229  
      *
 230  
      * @return always <code>true</code>
 231  
      */
 232  
     public boolean supports(Class clazz) {
 233  1
         return true;
 234  
     }
 235  
 }