diff --git a/core-tiger/src/test/java/org/springframework/security/annotation/BusinessService.java b/core-tiger/src/test/java/org/springframework/security/annotation/BusinessService.java index 110f130067..2b7724a422 100644 --- a/core-tiger/src/test/java/org/springframework/security/annotation/BusinessService.java +++ b/core-tiger/src/test/java/org/springframework/security/annotation/BusinessService.java @@ -39,8 +39,10 @@ public interface BusinessService { public void someUserMethod1(); @Secured({"ROLE_USER"}) - @RolesAllowed({"ROLE_USER"}) + @RolesAllowed({"ROLE_USER"}) public void someUserMethod2(); - + + public int someOther(String s); + public int someOther(int input); } diff --git a/core-tiger/src/test/java/org/springframework/security/annotation/BusinessServiceImpl.java b/core-tiger/src/test/java/org/springframework/security/annotation/BusinessServiceImpl.java index 02dd844a6f..79287c2858 100644 --- a/core-tiger/src/test/java/org/springframework/security/annotation/BusinessServiceImpl.java +++ b/core-tiger/src/test/java/org/springframework/security/annotation/BusinessServiceImpl.java @@ -26,7 +26,11 @@ public class BusinessServiceImpl implements BusinessService { return entity; } - public int someOther(int input) { - return input; - } + public int someOther(String s) { + return 0; + } + + public int someOther(int input) { + return input; + } } diff --git a/core-tiger/src/test/java/org/springframework/security/annotation/Jsr250BusinessServiceImpl.java b/core-tiger/src/test/java/org/springframework/security/annotation/Jsr250BusinessServiceImpl.java index 59f0c9ff7b..70f13e7a5f 100644 --- a/core-tiger/src/test/java/org/springframework/security/annotation/Jsr250BusinessServiceImpl.java +++ b/core-tiger/src/test/java/org/springframework/security/annotation/Jsr250BusinessServiceImpl.java @@ -27,7 +27,11 @@ public class Jsr250BusinessServiceImpl implements BusinessService { public void someAdminMethod() { } + public int someOther(String input) { + return 0; + } + public int someOther(int input) { - return input; - } -} \ No newline at end of file + return input; + } +} diff --git a/core-tiger/src/test/java/org/springframework/security/config/GlobalMethodSecurityBeanDefinitionParserTests.java b/core-tiger/src/test/java/org/springframework/security/config/GlobalMethodSecurityBeanDefinitionParserTests.java index 3f8c1d34e2..3b88528df9 100644 --- a/core-tiger/src/test/java/org/springframework/security/config/GlobalMethodSecurityBeanDefinitionParserTests.java +++ b/core-tiger/src/test/java/org/springframework/security/config/GlobalMethodSecurityBeanDefinitionParserTests.java @@ -31,8 +31,8 @@ public class GlobalMethodSecurityBeanDefinitionParserTests { setContext( "" + "" + - " " + - " " + + " " + + " " + "" + ConfigTestUtils.AUTH_PROVIDER_XML ); target = (BusinessService) appContext.getBean("target"); @@ -106,6 +106,21 @@ public class GlobalMethodSecurityBeanDefinitionParserTests { service.loadUserByUsername("notused"); } + @Test + public void supportsMethodArgumentsInPointcut() { + setContext( + "" + + "" + + " " + + " " + + "" + ConfigTestUtils.AUTH_PROVIDER_XML + ); + SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken("user", "password")); + target = (BusinessService) appContext.getBean("target"); + // someOther(int) should not be matched by someOther(String) + target.someOther(0); + } + @Test(expected=BeanDefinitionParsingException.class) public void duplicateElementCausesError() { setContext( @@ -116,21 +131,21 @@ public class GlobalMethodSecurityBeanDefinitionParserTests { @Test(expected=AccessDeniedException.class) public void worksWithoutTargetOrClass() { - setContext( - "" + - "" + - " " + - " " + - "" + AUTH_PROVIDER_XML - ); - + setContext( + "" + + "" + + " " + + " " + + "" + AUTH_PROVIDER_XML + ); + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test", "Password", new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_SOMEOTHERROLE")}); SecurityContextHolder.getContext().setAuthentication(token); target = (BusinessService) appContext.getBean("businessService"); target.someUserMethod1(); } - + private void setContext(String context) { appContext = new InMemoryXmlApplicationContext(context); } diff --git a/core/src/main/java/org/springframework/security/intercept/method/MapBasedMethodDefinitionSource.java b/core/src/main/java/org/springframework/security/intercept/method/MapBasedMethodDefinitionSource.java index 35f458cd94..9a03c97e58 100644 --- a/core/src/main/java/org/springframework/security/intercept/method/MapBasedMethodDefinitionSource.java +++ b/core/src/main/java/org/springframework/security/intercept/method/MapBasedMethodDefinitionSource.java @@ -34,13 +34,13 @@ import org.springframework.util.ClassUtils; /** * Stores a {@link ConfigAttributeDefinition} for a method or class signature. - * + * *

* This class is the preferred implementation of {@link MethodDefinitionSource} for XML-based * definition of method security metadata. To assist in XML-based definition, wildcard support * is provided. *

- * + * * @author Ben Alex * @version $Id$ * @since 2.0 @@ -51,9 +51,9 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini private static final Log logger = LogFactory.getLog(MapBasedMethodDefinitionSource.class); //~ Instance fields ================================================================================================ - private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); + private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); - /** Map from RegisteredMethod to ConfigAttributeDefinition */ + /** Map from RegisteredMethod to ConfigAttributeDefinition */ protected Map methodMap = new HashMap(); /** Map from RegisteredMethod to name pattern used for registration */ @@ -74,48 +74,33 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini while (iterator.hasNext()) { Map.Entry entry = (Map.Entry) iterator.next(); addSecureMethod((String)entry.getKey(), (ConfigAttributeDefinition)entry.getValue()); - } + } } - /** - * Implementation does not support class-level attributes. - */ - protected ConfigAttributeDefinition findAttributes(Class clazz) { - return null; - } - - /** - * Will walk the method inheritance tree to find the most specific declaration applicable. - */ - protected ConfigAttributeDefinition findAttributes(Method method, Class targetClass) { - return findAttributesSpecifiedAgainst(method, targetClass); - } - - private ConfigAttributeDefinition findAttributesSpecifiedAgainst(Method method, Class clazz) { - RegisteredMethod registeredMethod = new RegisteredMethod(method, clazz); - if (methodMap.containsKey(registeredMethod)) { - return (ConfigAttributeDefinition) methodMap.get(registeredMethod); - } - // Search superclass - if (clazz.getSuperclass() != null) { - return findAttributesSpecifiedAgainst(method, clazz.getSuperclass()); - } - return null; - } + /** + * Implementation does not support class-level attributes. + */ + protected ConfigAttributeDefinition findAttributes(Class clazz) { + return null; + } /** - * Add configuration attributes for a secure method. - * - * @param method the method to be secured - * @param attr required authorities associated with the method + * Will walk the method inheritance tree to find the most specific declaration applicable. */ - private void addSecureMethod(RegisteredMethod method, ConfigAttributeDefinition attr) { - Assert.notNull(method, "RegisteredMethod required"); - Assert.notNull(attr, "Configuration attribute required"); - if (logger.isInfoEnabled()) { - logger.info("Adding secure method [" + method + "] with attributes [" + attr + "]"); - } - this.methodMap.put(method, attr); + protected ConfigAttributeDefinition findAttributes(Method method, Class targetClass) { + return findAttributesSpecifiedAgainst(method, targetClass); + } + + private ConfigAttributeDefinition findAttributesSpecifiedAgainst(Method method, Class clazz) { + RegisteredMethod registeredMethod = new RegisteredMethod(method, clazz); + if (methodMap.containsKey(registeredMethod)) { + return (ConfigAttributeDefinition) methodMap.get(registeredMethod); + } + // Search superclass + if (clazz.getSuperclass() != null) { + return findAttributesSpecifiedAgainst(method, clazz.getSuperclass()); + } + return null; } /** @@ -126,7 +111,7 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini * @param attr required authorities associated with the method */ public void addSecureMethod(String name, ConfigAttributeDefinition attr) { - int lastDotIndex = name.lastIndexOf("."); + int lastDotIndex = name.lastIndexOf("."); if (lastDotIndex == -1) { throw new IllegalArgumentException("'" + name + "' is not a valid method name: format is FQN.methodName"); @@ -134,17 +119,17 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini String methodName = name.substring(lastDotIndex + 1); Assert.hasText(methodName, "Method not found for '" + name + "'"); - + String typeName = name.substring(0, lastDotIndex); Class type = ClassUtils.resolveClassName(typeName, this.beanClassLoader); - + addSecureMethod(type, methodName, attr); } /** * Add configuration attributes for a secure method. Mapped method names can end or start with * * for matching multiple methods. - * + * * @param javaType target interface or class the security configuration attribute applies to * @param mappedName mapped method name, which the javaType has declared or inherited * @param attr required authorities associated with the method @@ -192,6 +177,38 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini } } + /** + * Adds configuration attributes for a specific method, for example where the method has been + * matched using a pointcut expression. If a match already exists in the map for the method, then + * the existing match will be retained, so that if this method is called for a more general pointcut + * it will not override a more specific one which has already been added. This + */ + public void addSecureMethod(Class javaType, Method method, ConfigAttributeDefinition attr) { + RegisteredMethod key = new RegisteredMethod(method, javaType); + + if (methodMap.containsKey(key)) { + logger.debug("Method [" + method + "] is already registered with attributes [" + methodMap.get(key) + "]"); + return; + } + + methodMap.put(key, attr); + } + + /** + * Add configuration attributes for a secure method. + * + * @param method the method to be secured + * @param attr required authorities associated with the method + */ + private void addSecureMethod(RegisteredMethod method, ConfigAttributeDefinition attr) { + Assert.notNull(method, "RegisteredMethod required"); + Assert.notNull(attr, "Configuration attribute required"); + if (logger.isInfoEnabled()) { + logger.info("Adding secure method [" + method + "] with attributes [" + attr + "]"); + } + this.methodMap.put(method, attr); + } + /** * Obtains the configuration attributes explicitly defined against this bean. * @@ -254,54 +271,54 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini attributes.addAll(toMerge.getConfigAttributes()); } - public void setBeanClassLoader(ClassLoader beanClassLoader) { - Assert.notNull(beanClassLoader, "Bean class loader required"); - this.beanClassLoader = beanClassLoader; - } + public void setBeanClassLoader(ClassLoader beanClassLoader) { + Assert.notNull(beanClassLoader, "Bean class loader required"); + this.beanClassLoader = beanClassLoader; + } - /** - * @return map size (for unit tests and diagnostics) - */ - public int getMethodMapSize() { - return methodMap.size(); - } - - /** - * Stores both the Java Method as well as the Class we obtained the Method from. This is necessary because Method only - * provides us access to the declaring class. It doesn't provide a way for us to introspect which Class the Method - * was registered against. If a given Class inherits and redeclares a method (i.e. calls super();) the registered Class - * and declaring Class are the same. If a given class merely inherits but does not redeclare a method, the registered - * Class will be the Class we're invoking against and the Method will provide details of the declared class. - */ - private class RegisteredMethod { - private Method method; - private Class registeredJavaType; + /** + * @return map size (for unit tests and diagnostics) + */ + public int getMethodMapSize() { + return methodMap.size(); + } - public RegisteredMethod(Method method, Class registeredJavaType) { - Assert.notNull(method, "Method required"); - Assert.notNull(registeredJavaType, "Registered Java Type required"); - this.method = method; - this.registeredJavaType = registeredJavaType; - } + /** + * Stores both the Java Method as well as the Class we obtained the Method from. This is necessary because Method only + * provides us access to the declaring class. It doesn't provide a way for us to introspect which Class the Method + * was registered against. If a given Class inherits and redeclares a method (i.e. calls super();) the registered Class + * and declaring Class are the same. If a given class merely inherits but does not redeclare a method, the registered + * Class will be the Class we're invoking against and the Method will provide details of the declared class. + */ + private class RegisteredMethod { + private Method method; + private Class registeredJavaType; - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj != null && obj instanceof RegisteredMethod) { - RegisteredMethod rhs = (RegisteredMethod) obj; - return method.equals(rhs.method) && registeredJavaType.equals(rhs.registeredJavaType); - } - return false; - } + public RegisteredMethod(Method method, Class registeredJavaType) { + Assert.notNull(method, "Method required"); + Assert.notNull(registeredJavaType, "Registered Java Type required"); + this.method = method; + this.registeredJavaType = registeredJavaType; + } - public int hashCode() { - return method.hashCode() * registeredJavaType.hashCode(); - } + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj != null && obj instanceof RegisteredMethod) { + RegisteredMethod rhs = (RegisteredMethod) obj; + return method.equals(rhs.method) && registeredJavaType.equals(rhs.registeredJavaType); + } + return false; + } + + public int hashCode() { + return method.hashCode() * registeredJavaType.hashCode(); + } + + public String toString() { + return "RegisteredMethod[" + registeredJavaType.getName() + "; " + method + "]"; + } + } - public String toString() { - return "RegisteredMethod[" + registeredJavaType.getName() + "; " + method + "]"; - } - } - } diff --git a/core/src/main/java/org/springframework/security/intercept/method/ProtectPointcutPostProcessor.java b/core/src/main/java/org/springframework/security/intercept/method/ProtectPointcutPostProcessor.java index 773466e3e7..809090018b 100644 --- a/core/src/main/java/org/springframework/security/intercept/method/ProtectPointcutPostProcessor.java +++ b/core/src/main/java/org/springframework/security/intercept/method/ProtectPointcutPostProcessor.java @@ -21,25 +21,25 @@ import org.springframework.util.Assert; /** * Parses AspectJ pointcut expressions, registering methods that match the pointcut with a * traditional {@link MapBasedMethodDefinitionSource}. - * + * *

* This class provides a convenient way of declaring a list of pointcuts, and then * having every method of every bean defined in the Spring application context compared with * those pointcuts. Where a match is found, the matching method will be registered with the * {@link MapBasedMethodDefinitionSource}. *

- * + * *

* It is very important to understand that only the first pointcut that matches a given * method will be taken as authoritative for that method. This is why pointcuts should be provided * as a LinkedHashMap, because their order is very important. *

- * + * *

* Note also that only beans defined in the Spring application context will be examined by this - * class. + * class. *

- * + * *

* Because this class registers method security metadata with {@link MapBasedMethodDefinitionSource}, * normal Spring Security capabilities such as {@link MethodDefinitionSourceAdvisor} can be used. @@ -57,18 +57,18 @@ public final class ProtectPointcutPostProcessor implements BeanPostProcessor { private static final Log logger = LogFactory.getLog(ProtectPointcutPostProcessor.class); private Map pointcutMap = new LinkedHashMap(); /** Key: string-based pointcut, value: ConfigAttributeDefinition */ - private MapBasedMethodDefinitionSource mapBasedMethodDefinitionSource; - private PointcutParser parser; - - public ProtectPointcutPostProcessor(MapBasedMethodDefinitionSource mapBasedMethodDefinitionSource) { - Assert.notNull(mapBasedMethodDefinitionSource, "MapBasedMethodDefinitionSource to populate is required"); - this.mapBasedMethodDefinitionSource = mapBasedMethodDefinitionSource; - - // Setup AspectJ pointcut expression parser - Set supportedPrimitives = new HashSet(); - supportedPrimitives.add(PointcutPrimitive.EXECUTION); - supportedPrimitives.add(PointcutPrimitive.ARGS); - supportedPrimitives.add(PointcutPrimitive.REFERENCE); + private MapBasedMethodDefinitionSource mapBasedMethodDefinitionSource; + private PointcutParser parser; + + public ProtectPointcutPostProcessor(MapBasedMethodDefinitionSource mapBasedMethodDefinitionSource) { + Assert.notNull(mapBasedMethodDefinitionSource, "MapBasedMethodDefinitionSource to populate is required"); + this.mapBasedMethodDefinitionSource = mapBasedMethodDefinitionSource; + + // Setup AspectJ pointcut expression parser + Set supportedPrimitives = new HashSet(); + supportedPrimitives.add(PointcutPrimitive.EXECUTION); + supportedPrimitives.add(PointcutPrimitive.ARGS); + supportedPrimitives.add(PointcutPrimitive.REFERENCE); // supportedPrimitives.add(PointcutPrimitive.THIS); // supportedPrimitives.add(PointcutPrimitive.TARGET); // supportedPrimitives.add(PointcutPrimitive.WITHIN); @@ -76,79 +76,79 @@ public final class ProtectPointcutPostProcessor implements BeanPostProcessor { // supportedPrimitives.add(PointcutPrimitive.AT_WITHIN); // supportedPrimitives.add(PointcutPrimitive.AT_ARGS); // supportedPrimitives.add(PointcutPrimitive.AT_TARGET); - parser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(supportedPrimitives); - } + parser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(supportedPrimitives); + } - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - return bean; - } + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return bean; + } - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - // Obtain methods for the present bean - Method[] methods; - try { - methods = bean.getClass().getMethods(); - } catch (Exception e) { - throw new IllegalStateException(e.getMessage()); - } - - // Check to see if any of those methods are compatible with our pointcut expressions - for (int i = 0; i < methods.length; i++) { - Iterator iter = pointcutMap.keySet().iterator(); - while (iter.hasNext()) { - String ex = iter.next().toString(); - - // Parse the presented AspectJ pointcut expression - PointcutExpression expression = parser.parsePointcutExpression(ex); + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + // Obtain methods for the present bean + Method[] methods; + try { + methods = bean.getClass().getMethods(); + } catch (Exception e) { + throw new IllegalStateException(e.getMessage()); + } - // Try for the bean class directly - if (attemptMatch(bean.getClass(), methods[i], expression, beanName)) { - // We've found the first expression that matches this method, so move onto the next method now - break; // the "while" loop, not the "for" loop - } - } - } - - return bean; - } - - private boolean attemptMatch(Class targetClass, Method method, PointcutExpression expression, String beanName) { - // Determine if the presented AspectJ pointcut expression matches this method - boolean matches = expression.matchesMethodExecution(method).alwaysMatches(); - - // Handle accordingly - if (matches) { - ConfigAttributeDefinition attr = (ConfigAttributeDefinition) pointcutMap.get(expression.getPointcutExpression()); - - if (logger.isDebugEnabled()) { - logger.debug("AspectJ pointcut expression '" + expression.getPointcutExpression() + "' matches target class '" + targetClass.getName() + "' (bean ID '" + beanName + "') for method '" + method + "'; registering security configuration attribute '" + attr + "'"); - } - - mapBasedMethodDefinitionSource.addSecureMethod(targetClass, method.getName(), attr); - } - - return matches; - } - - public void setPointcutMap(Map map) { - Assert.notEmpty(map); - Iterator i = map.keySet().iterator(); - while (i.hasNext()) { - String expression = i.next().toString(); - Object value = map.get(expression); - Assert.isInstanceOf(ConfigAttributeDefinition.class, value, "Map keys must be instances of ConfigAttributeDefinition"); - addPointcut(expression, (ConfigAttributeDefinition) value); - } - } + // Check to see if any of those methods are compatible with our pointcut expressions + for (int i = 0; i < methods.length; i++) { + Iterator iter = pointcutMap.keySet().iterator(); + while (iter.hasNext()) { + String ex = iter.next().toString(); + + // Parse the presented AspectJ pointcut expression + PointcutExpression expression = parser.parsePointcutExpression(ex); + + // Try for the bean class directly + if (attemptMatch(bean.getClass(), methods[i], expression, beanName)) { + // We've found the first expression that matches this method, so move onto the next method now + break; // the "while" loop, not the "for" loop + } + } + } + + return bean; + } + + private boolean attemptMatch(Class targetClass, Method method, PointcutExpression expression, String beanName) { + // Determine if the presented AspectJ pointcut expression matches this method + boolean matches = expression.matchesMethodExecution(method).alwaysMatches(); + + // Handle accordingly + if (matches) { + ConfigAttributeDefinition attr = (ConfigAttributeDefinition) pointcutMap.get(expression.getPointcutExpression()); + + if (logger.isDebugEnabled()) { + logger.debug("AspectJ pointcut expression '" + expression.getPointcutExpression() + "' matches target class '" + targetClass.getName() + "' (bean ID '" + beanName + "') for method '" + method + "'; registering security configuration attribute '" + attr + "'"); + } + + mapBasedMethodDefinitionSource.addSecureMethod(targetClass, method, attr); + } + + return matches; + } + + public void setPointcutMap(Map map) { + Assert.notEmpty(map); + Iterator i = map.keySet().iterator(); + while (i.hasNext()) { + String expression = i.next().toString(); + Object value = map.get(expression); + Assert.isInstanceOf(ConfigAttributeDefinition.class, value, "Map keys must be instances of ConfigAttributeDefinition"); + addPointcut(expression, (ConfigAttributeDefinition) value); + } + } + + public void addPointcut(String pointcutExpression, ConfigAttributeDefinition definition) { + Assert.hasText(pointcutExpression, "An AspectJ pointcut expression is required"); + Assert.notNull(definition, "ConfigAttributeDefinition required"); + pointcutMap.put(pointcutExpression, definition); + + if (logger.isDebugEnabled()) { + logger.debug("AspectJ pointcut expression '" + pointcutExpression + "' registered for security configuration attribute '" + definition + "'"); + } + } - public void addPointcut(String pointcutExpression, ConfigAttributeDefinition definition) { - Assert.hasText(pointcutExpression, "An AspectJ pointcut expression is required"); - Assert.notNull(definition, "ConfigAttributeDefinition required"); - pointcutMap.put(pointcutExpression, definition); - - if (logger.isDebugEnabled()) { - logger.debug("AspectJ pointcut expression '" + pointcutExpression + "' registered for security configuration attribute '" + definition + "'"); - } - } - } diff --git a/core/src/test/java/org/springframework/security/config/ConfigTestUtils.java b/core/src/test/java/org/springframework/security/config/ConfigTestUtils.java index d384f0bd69..1ec01f9e9a 100644 --- a/core/src/test/java/org/springframework/security/config/ConfigTestUtils.java +++ b/core/src/test/java/org/springframework/security/config/ConfigTestUtils.java @@ -6,9 +6,8 @@ public abstract class ConfigTestUtils { " " + " " + " " + + " " + + " " + " " + " "; - - - } diff --git a/core/src/test/java/org/springframework/security/intercept/method/MapBasedMethodDefinitionSourceTests.java b/core/src/test/java/org/springframework/security/intercept/method/MapBasedMethodDefinitionSourceTests.java new file mode 100644 index 0000000000..1465a0b14c --- /dev/null +++ b/core/src/test/java/org/springframework/security/intercept/method/MapBasedMethodDefinitionSourceTests.java @@ -0,0 +1,54 @@ +package org.springframework.security.intercept.method; + +import static org.junit.Assert.*; + +import java.lang.reflect.Method; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.security.ConfigAttributeDefinition; + +/** + * Tests for {@link MapBasedMethodDefinitionSource}. + * + * @author Luke Taylor + * @since 2.0.4 + */ +public class MapBasedMethodDefinitionSourceTests { + private final ConfigAttributeDefinition ROLE_A = new ConfigAttributeDefinition("ROLE_A"); + private final ConfigAttributeDefinition ROLE_B = new ConfigAttributeDefinition("ROLE_B"); + private MapBasedMethodDefinitionSource mds; + private Method someMethodString; + private Method someMethodInteger; + + @Before + public void initialize() throws Exception { + mds = new MapBasedMethodDefinitionSource(); + someMethodString = MockService.class.getMethod("someMethod", String.class); + someMethodInteger = MockService.class.getMethod("someMethod", Integer.class); + } + + @Test + public void wildcardedMatchIsOverwrittenByMoreSpecificMatch() { + mds.addSecureMethod(MockService.class, "some*", ROLE_A); + mds.addSecureMethod(MockService.class, "someMethod*", ROLE_B); + assertEquals(ROLE_B, mds.getAttributes(someMethodInteger, MockService.class)); + } + + @Test + public void methodsWithDifferentArgumentsAreMatchedCorrectly() throws Exception { + mds.addSecureMethod(MockService.class, someMethodInteger, ROLE_A); + mds.addSecureMethod(MockService.class, someMethodString, ROLE_B); + + assertEquals(ROLE_A, mds.getAttributes(someMethodInteger, MockService.class)); + assertEquals(ROLE_B, mds.getAttributes(someMethodString, MockService.class)); + } + + private class MockService { + public void someMethod(String s) { + } + + public void someMethod(Integer i) { + } + } +}