| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
| MethodDefinitionMap |
|
| 3.3333333333333335;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>*</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>*</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>*</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 | } |