Coverage Report - org.acegisecurity.intercept.method.MethodDefinitionMap
 
Classes in this File Line Coverage Branch Coverage Complexity
MethodDefinitionMap
99% 
100% 
3.333
 
 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  
 
 16  
 package org.acegisecurity.intercept.method;
 17  
 
 18  
 import org.acegisecurity.ConfigAttribute;
 19  
 import org.acegisecurity.ConfigAttributeDefinition;
 20  
 import org.acegisecurity.SecurityConfig;
 21  
 
 22  
 import org.apache.commons.logging.Log;
 23  
 import org.apache.commons.logging.LogFactory;
 24  
 
 25  
 import java.lang.reflect.Method;
 26  
 
 27  
 import java.util.ArrayList;
 28  
 import java.util.HashMap;
 29  
 import java.util.Iterator;
 30  
 import java.util.List;
 31  
 import java.util.Map;
 32  
 
 33  
 
 34  
 /**
 35  
  * Stores a {@link ConfigAttributeDefinition} for each method signature defined in a bean context.<p>For
 36  
  * consistency with {@link MethodDefinitionAttributes} as well as support for
 37  
  * <code>MethodDefinitionSourceAdvisor</code>, this implementation will return a
 38  
  * <code>ConfigAttributeDefinition</code> containing all configuration attributes defined against:
 39  
  *  <ul>
 40  
  *      <li>The method-specific attributes defined for the intercepted method of the intercepted class.</li>
 41  
  *      <li>The method-specific attributes defined by any explicitly implemented interface if that interface
 42  
  *      contains a method signature matching that of the intercepted method.</li>
 43  
  *  </ul>
 44  
  *  </p>
 45  
  *  <p>In general you should therefore define the <b>interface method</b>s of your secure objects, not the
 46  
  * implementations. For example, define <code>com.company.Foo.findAll=ROLE_TEST</code> but not
 47  
  * <code>com.company.FooImpl.findAll=ROLE_TEST</code>.</p>
 48  
  *
 49  
  * @author Ben Alex
 50  
  * @version $Id: MethodDefinitionMap.java 1784 2007-02-24 21:00:24Z luke_t $
 51  
  */
 52  32
 public class MethodDefinitionMap extends AbstractMethodDefinitionSource {
 53  
     //~ Static fields/initializers =====================================================================================
 54  
 
 55  2
     private static final Log logger = LogFactory.getLog(MethodDefinitionMap.class);
 56  
 
 57  
     //~ Instance fields ================================================================================================
 58  
 
 59  
     /** Map from Method to ApplicationDefinition */
 60  32
     protected Map methodMap = new HashMap();
 61  
 
 62  
     /** Map from Method to name pattern used for registration */
 63  32
     private Map nameMap = new HashMap();
 64  
 
 65  
     //~ Methods ========================================================================================================
 66  
 
 67  
     /**
 68  
      * Add configuration attributes for a secure method. Method names can end or start with <code>&#42</code>
 69  
      * for matching multiple methods.
 70  
      *
 71  
      * @param method the method to be secured
 72  
      * @param attr required authorities associated with the method
 73  
      */
 74  
     public void addSecureMethod(Method method, ConfigAttributeDefinition attr) {
 75  65
         logger.info("Adding secure method [" + method + "] with attributes [" + attr + "]");
 76  65
         this.methodMap.put(method, attr);
 77  65
     }
 78  
 
 79  
     /**
 80  
      * Add configuration attributes for a secure method. Method names can end or start with <code>&#42</code>
 81  
      * for matching multiple methods.
 82  
      *
 83  
      * @param name class and method name, separated by a dot
 84  
      * @param attr required authorities associated with the method
 85  
      *
 86  
      * @throws IllegalArgumentException DOCUMENT ME!
 87  
      */
 88  
     public void addSecureMethod(String name, ConfigAttributeDefinition attr) {
 89  64
         int lastDotIndex = name.lastIndexOf(".");
 90  
 
 91  64
         if (lastDotIndex == -1) {
 92  1
             throw new IllegalArgumentException("'" + name + "' is not a valid method name: format is FQN.methodName");
 93  
         }
 94  
 
 95  63
         String className = name.substring(0, lastDotIndex);
 96  63
         String methodName = name.substring(lastDotIndex + 1);
 97  
 
 98  
         try {
 99  63
             Class clazz = Class.forName(className, true, Thread.currentThread().getContextClassLoader());
 100  62
             addSecureMethod(clazz, methodName, attr);
 101  1
         } catch (ClassNotFoundException ex) {
 102  1
             throw new IllegalArgumentException("Class '" + className + "' not found");
 103  61
         }
 104  61
     }
 105  
 
 106  
     /**
 107  
      * Add configuration attributes for a secure method. Method names can end or start with <code>&#42</code>
 108  
      * for matching multiple methods.
 109  
      *
 110  
      * @param clazz target interface or class
 111  
      * @param mappedName mapped method name
 112  
      * @param attr required authorities associated with the method
 113  
      *
 114  
      * @throws IllegalArgumentException DOCUMENT ME!
 115  
      */
 116  
     public void addSecureMethod(Class clazz, String mappedName, ConfigAttributeDefinition attr) {
 117  62
         String name = clazz.getName() + '.' + mappedName;
 118  
 
 119  62
         if (logger.isDebugEnabled()) {
 120  0
             logger.debug("Adding secure method [" + name + "] with attributes [" + attr + "]");
 121  
         }
 122  
 
 123  62
         Method[] methods = clazz.getDeclaredMethods();
 124  62
         List matchingMethods = new ArrayList();
 125  
 
 126  366
         for (int i = 0; i < methods.length; i++) {
 127  304
             if (methods[i].getName().equals(mappedName) || isMatch(methods[i].getName(), mappedName)) {
 128  68
                 matchingMethods.add(methods[i]);
 129  
             }
 130  
         }
 131  
 
 132  62
         if (matchingMethods.isEmpty()) {
 133  1
             throw new IllegalArgumentException("Couldn't find method '" + mappedName + "' on " + clazz);
 134  
         }
 135  
 
 136  
         // register all matching methods
 137  61
         for (Iterator it = matchingMethods.iterator(); it.hasNext();) {
 138  68
             Method method = (Method) it.next();
 139  68
             String regMethodName = (String) this.nameMap.get(method);
 140  
 
 141  68
             if ((regMethodName == null) || (!regMethodName.equals(name) && (regMethodName.length() <= name.length()))) {
 142  
                 // no already registered method name, or more specific
 143  
                 // method name specification now -> (re-)register method
 144  65
                 if (regMethodName != null) {
 145  1
                     logger.debug("Replacing attributes for secure method [" + method + "]: current name [" + name
 146  
                         + "] is more specific than [" + regMethodName + "]");
 147  
                 }
 148  
 
 149  65
                 this.nameMap.put(method, name);
 150  65
                 addSecureMethod(method, attr);
 151  
             } else {
 152  3
                 logger.debug("Keeping attributes for secure method [" + method + "]: current name [" + name
 153  
                     + "] is not more specific than [" + regMethodName + "]");
 154  
             }
 155  68
         }
 156  61
     }
 157  
 
 158  
     /**
 159  
      * Obtains the configuration attributes explicitly defined against this bean. This method will not return
 160  
      * implicit configuration attributes that may be returned by {@link #lookupAttributes(Method)} as it does not have
 161  
      * access to a method invocation at this time.
 162  
      *
 163  
      * @return the attributes explicitly defined against this bean
 164  
      */
 165  
     public Iterator getConfigAttributeDefinitions() {
 166  16
         return methodMap.values().iterator();
 167  
     }
 168  
 
 169  
     /**
 170  
      * Obtains the number of configuration attributes explicitly defined against this bean. This method will
 171  
      * not return implicit configuration attributes that may be returned by {@link #lookupAttributes(Method)} as it
 172  
      * does not have access to a method invocation at this time.
 173  
      *
 174  
      * @return the number of configuration attributes explicitly defined against this bean
 175  
      */
 176  
     public int getMethodMapSize() {
 177  5
         return this.methodMap.size();
 178  
     }
 179  
 
 180  
     /**
 181  
      * Return if the given method name matches the mapped name. The default implementation checks for "xxx" and
 182  
      * "xxx" matches.
 183  
      *
 184  
      * @param methodName the method name of the class
 185  
      * @param mappedName the name in the descriptor
 186  
      *
 187  
      * @return if the names match
 188  
      */
 189  
     private boolean isMatch(String methodName, String mappedName) {
 190  293
         return (mappedName.endsWith("*") && methodName.startsWith(mappedName.substring(0, mappedName.length() - 1)))
 191  
         || (mappedName.startsWith("*") && methodName.endsWith(mappedName.substring(1, mappedName.length())));
 192  
     }
 193  
 
 194  
     protected ConfigAttributeDefinition lookupAttributes(Method method) {
 195  47
         ConfigAttributeDefinition definition = new ConfigAttributeDefinition();
 196  
 
 197  
         // Add attributes explictly defined for this method invocation
 198  47
         ConfigAttributeDefinition directlyAssigned = (ConfigAttributeDefinition) this.methodMap.get(method);
 199  47
         merge(definition, directlyAssigned);
 200  
 
 201  
         // Add attributes explicitly defined for this method invocation's interfaces
 202  47
         Class[] interfaces = method.getDeclaringClass().getInterfaces();
 203  
 
 204  63
         for (int i = 0; i < interfaces.length; i++) {
 205  16
             Class clazz = interfaces[i];
 206  
 
 207  
             try {
 208  
                 // Look for the method on the current interface
 209  16
                 Method interfaceMethod = clazz.getDeclaredMethod(method.getName(), (Class[]) method.getParameterTypes());
 210  14
                 ConfigAttributeDefinition interfaceAssigned =
 211  
                         (ConfigAttributeDefinition) this.methodMap.get(interfaceMethod);
 212  14
                 merge(definition, interfaceAssigned);
 213  2
             } catch (Exception e) {
 214  
                 // skip this interface
 215  14
             }
 216  
         }
 217  
 
 218  
         // Return null if empty, as per abstract superclass contract
 219  47
         if (definition.size() == 0) {
 220  4
             return null;
 221  
         } else {
 222  43
             return definition;
 223  
         }
 224  
     }
 225  
 
 226  
     private void merge(ConfigAttributeDefinition definition, ConfigAttributeDefinition toMerge) {
 227  61
         if (toMerge == null) {
 228  17
             return;
 229  
         }
 230  
 
 231  44
         Iterator attribs = toMerge.getConfigAttributes();
 232  
 
 233  117
         while (attribs.hasNext()) {
 234  73
             definition.addConfigAttribute((ConfigAttribute) attribs.next());
 235  
         }
 236  44
     }
 237  
 
 238  
     /**
 239  
      * Easier configuration of the instance, using {@link MethodDefinitionSourceMapping}.
 240  
      *
 241  
      * @param mappings {@link List} of {@link MethodDefinitionSourceMapping} objects.
 242  
      */
 243  
     public void setMappings(List mappings) {
 244  30
         Iterator it = mappings.iterator();
 245  91
         while (it.hasNext()) {
 246  64
             MethodDefinitionSourceMapping mapping = (MethodDefinitionSourceMapping) it.next();
 247  64
             ConfigAttributeDefinition configDefinition = new ConfigAttributeDefinition();
 248  
 
 249  64
             Iterator configAttributesIt = mapping.getConfigAttributes().iterator();
 250  172
             while (configAttributesIt.hasNext()) {
 251  108
                 String s = (String) configAttributesIt.next();
 252  108
                 configDefinition.addConfigAttribute(new SecurityConfig(s));
 253  108
             }
 254  
 
 255  64
             addSecureMethod(mapping.getMethodName(), configDefinition);
 256  61
         }
 257  27
     }
 258  
 }