(attributeTokens.length);
+
+ for(String token : attributeTokens) {
+ attributes.add(new SecurityConfig(token));
+ }
+
+ pointcutMap.put(expression, attributes);
}
return pointcutMap;
@@ -158,7 +178,7 @@ class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionParser {
parserContext.registerComponent(new BeanComponentDefinition(interceptor, BeanIds.METHOD_SECURITY_INTERCEPTOR));
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_SECURITY_INTERCEPTOR_POST_PROCESSOR,
- new RootBeanDefinition(MethodSecurityInterceptorPostProcessor.class));
+ new RootBeanDefinition(MethodSecurityInterceptorPostProcessor.class));
}
private void registerAdvisor(ParserContext parserContext, Object source) {
diff --git a/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java
index 2d45d7b91d..c5abdc1546 100644
--- a/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java
+++ b/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java
@@ -124,8 +124,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
String accessManagerId = element.getAttribute(ATT_ACCESS_MGR);
if (!StringUtils.hasText(accessManagerId)) {
- ConfigUtils.registerDefaultAccessManagerIfNecessary(parserContext);
- accessManagerId = BeanIds.ACCESS_MANAGER;
+ ConfigUtils.registerDefaultWebAccessManagerIfNecessary(parserContext);
+ accessManagerId = BeanIds.WEB_ACCESS_MANAGER;
}
// Register the portMapper. A default will always be created, even if no element exists.
@@ -273,8 +273,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
exceptionTranslationFilterBuilder.addPropertyValue("createSessionAllowed", new Boolean(allowSessionCreation));
if (StringUtils.hasText(accessDeniedPage)) {
- BeanDefinition accessDeniedHandler = new RootBeanDefinition(AccessDeniedHandlerImpl.class);
- accessDeniedHandler.getPropertyValues().addPropertyValue("errorPage", accessDeniedPage);
+ BeanDefinition accessDeniedHandler = new RootBeanDefinition(AccessDeniedHandlerImpl.class);
+ accessDeniedHandler.getPropertyValues().addPropertyValue("errorPage", accessDeniedPage);
exceptionTranslationFilterBuilder.addPropertyValue("accessDeniedHandler", accessDeniedHandler);
}
diff --git a/core/src/main/java/org/springframework/security/config/InterceptMethodsBeanDefinitionDecorator.java b/core/src/main/java/org/springframework/security/config/InterceptMethodsBeanDefinitionDecorator.java
index 97e5907cfe..58ad96b26b 100644
--- a/core/src/main/java/org/springframework/security/config/InterceptMethodsBeanDefinitionDecorator.java
+++ b/core/src/main/java/org/springframework/security/config/InterceptMethodsBeanDefinitionDecorator.java
@@ -30,8 +30,8 @@ public class InterceptMethodsBeanDefinitionDecorator implements BeanDefinitionDe
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
ConfigUtils.registerProviderManagerIfNecessary(parserContext);
- ConfigUtils.registerDefaultAccessManagerIfNecessary(parserContext);
-
+ ConfigUtils.registerDefaultMethodAccessManagerIfNecessary(parserContext);
+
return delegate.decorate(node, definition, parserContext);
}
}
@@ -41,8 +41,8 @@ public class InterceptMethodsBeanDefinitionDecorator implements BeanDefinitionDe
* registration.
*/
class InternalInterceptMethodsBeanDefinitionDecorator extends AbstractInterceptorDrivenBeanDefinitionDecorator {
- static final String ATT_METHOD = "method";
- static final String ATT_ACCESS = "access";
+ static final String ATT_METHOD = "method";
+ static final String ATT_ACCESS = "access";
private static final String ATT_ACCESS_MGR = "access-decision-manager-ref";
private Log logger = LogFactory.getLog(getClass());
@@ -57,40 +57,40 @@ class InternalInterceptMethodsBeanDefinitionDecorator extends AbstractIntercepto
String accessManagerId = interceptMethodsElt.getAttribute(ATT_ACCESS_MGR);
if (!StringUtils.hasText(accessManagerId)) {
- accessManagerId = BeanIds.ACCESS_MANAGER;
+ accessManagerId = BeanIds.METHOD_ACCESS_MANAGER;
}
interceptor.addPropertyValue("accessDecisionManager", new RuntimeBeanReference(accessManagerId));
interceptor.addPropertyValue("authenticationManager", new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
// Lookup parent bean information
- Element parent = (Element) node.getParentNode();
- String parentBeanClass = parent.getAttribute("class");
- String parentBeanId = parent.getAttribute("id");
+ Element parent = (Element) node.getParentNode();
+ String parentBeanClass = parent.getAttribute("class");
+ String parentBeanId = parent.getAttribute("id");
parent = null;
-
+
// Parse the included methods
List methods = DomUtils.getChildElementsByTagName(interceptMethodsElt, Elements.PROTECT);
StringBuffer sb = new StringBuffer();
-
+
for (Iterator i = methods.iterator(); i.hasNext();) {
Element protectmethodElt = (Element) i.next();
String accessConfig = protectmethodElt.getAttribute(ATT_ACCESS);
// Support inference of class names
String methodName = protectmethodElt.getAttribute(ATT_METHOD);
-
+
if (methodName.lastIndexOf(".") == -1) {
- if (parentBeanClass != null && !"".equals(parentBeanClass)) {
- methodName = parentBeanClass + "." + methodName;
- }
+ if (parentBeanClass != null && !"".equals(parentBeanClass)) {
+ methodName = parentBeanClass + "." + methodName;
+ }
}
-
+
// Rely on the default property editor for MethodSecurityInterceptor.setObjectDefinitionSource to setup the MethodDefinitionSource
sb.append(methodName + "=" + accessConfig).append("\r\n");
}
-
+
interceptor.addPropertyValue("objectDefinitionSource", sb.toString());
return interceptor.getBeanDefinition();
diff --git a/core/src/main/java/org/springframework/security/expression/ExpressionUtils.java b/core/src/main/java/org/springframework/security/expression/ExpressionUtils.java
new file mode 100644
index 0000000000..1492382785
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/expression/ExpressionUtils.java
@@ -0,0 +1,67 @@
+package org.springframework.security.expression;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.Expression;
+import org.springframework.expression.spel.standard.StandardEvaluationContext;
+
+public class ExpressionUtils {
+ public static Object doFilter(Object filterTarget, Expression filterExpression, StandardEvaluationContext ctx) {
+ SecurityExpressionRoot rootObject = (SecurityExpressionRoot) ctx.getRootContextObject();
+ Set removeList = new HashSet();
+
+ if (filterTarget instanceof Collection) {
+ for (Object filterObject : (Collection)filterTarget) {
+ rootObject.setFilterObject(filterObject);
+
+ if (!evaluateAsBoolean(filterExpression, ctx)) {
+ removeList.add(filterObject);
+ }
+ }
+
+ for(Object toRemove : removeList) {
+ ((Collection)filterTarget).remove(toRemove);
+ }
+
+ return filterTarget;
+ }
+
+ if (filterTarget.getClass().isArray()) {
+ Object[] array = (Object[])filterTarget;
+
+ for (int i = 0; i < array.length; i++) {
+ rootObject.setFilterObject(array[i]);
+
+ if (!evaluateAsBoolean(filterExpression, ctx)) {
+ removeList.add(array[i]);
+ }
+ }
+
+ Object[] filtered = (Object[]) Array.newInstance(filterTarget.getClass().getComponentType(),
+ array.length - removeList.size());
+ for (int i = 0, j = 0; i < array.length; i++) {
+ if (!removeList.contains(array[i])) {
+ filtered[j++] = array[i];
+ }
+ }
+
+ return filtered;
+ }
+
+ throw new IllegalArgumentException("Filter target must be a collection or array type, but was " + filterTarget);
+ }
+
+ public static boolean evaluateAsBoolean(Expression expr, StandardEvaluationContext ctx) {
+ try {
+ return ((Boolean) expr.getValue(ctx, Boolean.class)).booleanValue();
+ } catch (EvaluationException e) {
+ throw new IllegalArgumentException("Failed to evaluate expression", e);
+ }
+ }
+
+
+}
diff --git a/core/src/main/java/org/springframework/security/expression/MethodInvocationSecurityExpressionRoot.java b/core/src/main/java/org/springframework/security/expression/MethodInvocationSecurityExpressionRoot.java
new file mode 100644
index 0000000000..d246782362
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/expression/MethodInvocationSecurityExpressionRoot.java
@@ -0,0 +1,14 @@
+package org.springframework.security.expression;
+
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.security.Authentication;
+
+public class MethodInvocationSecurityExpressionRoot extends SecurityExpressionRoot {
+
+ MethodInvocationSecurityExpressionRoot(Authentication a, MethodInvocation mi) {
+ super(a);
+
+ mi.getArguments();
+ }
+
+}
diff --git a/core/src/main/java/org/springframework/security/expression/SecurityExpressionRoot.java b/core/src/main/java/org/springframework/security/expression/SecurityExpressionRoot.java
new file mode 100644
index 0000000000..510702ae09
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/expression/SecurityExpressionRoot.java
@@ -0,0 +1,67 @@
+package org.springframework.security.expression;
+
+import java.util.Set;
+
+import org.springframework.security.Authentication;
+import org.springframework.security.AuthenticationTrustResolver;
+import org.springframework.security.AuthenticationTrustResolverImpl;
+import org.springframework.security.util.AuthorityUtils;
+
+public class SecurityExpressionRoot {
+ private Authentication authentication;
+ private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
+ private Object filterObject;
+ private Object returnObject;
+
+ public SecurityExpressionRoot(Authentication a) {
+ this.authentication = a;
+ }
+
+ public boolean hasRole(String role) {
+ return hasAnyRole(role);
+ }
+
+ public boolean hasAnyRole(String... roles) {
+ Set roleSet = AuthorityUtils.authorityArrayToSet(authentication.getAuthorities());
+
+ for (String role : roles) {
+ if (roleSet.contains(role)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public boolean isAnonymous() {
+ return trustResolver.isAnonymous(authentication);
+ }
+
+ public boolean isRememberMe() {
+ return trustResolver.isRememberMe(authentication);
+ }
+
+ public String getName() {
+ return authentication.getName();
+ }
+
+ public boolean isFullyAuthenticated() {
+ return !trustResolver.isAnonymous(authentication) && !trustResolver.isRememberMe(authentication);
+ }
+
+ public void setFilterObject(Object filterObject) {
+ this.filterObject = filterObject;
+ }
+
+ public Object getFilterObject() {
+ return filterObject;
+ }
+
+ public void setReturnObject(Object returnObject) {
+ this.returnObject = returnObject;
+ }
+
+ public Object getReturnObject() {
+ return returnObject;
+ }
+}
diff --git a/core/src/main/java/org/springframework/security/expression/annotation/PostAuthorize.java b/core/src/main/java/org/springframework/security/expression/annotation/PostAuthorize.java
new file mode 100644
index 0000000000..3b044b1d6b
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/expression/annotation/PostAuthorize.java
@@ -0,0 +1,26 @@
+package org.springframework.security.expression.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation for specifying a method access-control expression which will be evaluated after a method has been invoked.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 2.5
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface PostAuthorize {
+ /**
+ * @return the Spring-EL expression to be evaluated after invoking the protected method
+ */
+ public String value();
+}
diff --git a/core/src/main/java/org/springframework/security/expression/annotation/PostFilter.java b/core/src/main/java/org/springframework/security/expression/annotation/PostFilter.java
new file mode 100644
index 0000000000..6672174302
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/expression/annotation/PostFilter.java
@@ -0,0 +1,26 @@
+package org.springframework.security.expression.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation for specifying a method filtering expression which will be evaluated after a method has been invoked.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 2.5
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface PostFilter {
+ /**
+ * @return the Spring-EL expression to be evaluated after invoking the protected method
+ */
+ public String value();
+}
diff --git a/core/src/main/java/org/springframework/security/expression/annotation/PreAuthorize.java b/core/src/main/java/org/springframework/security/expression/annotation/PreAuthorize.java
new file mode 100644
index 0000000000..97c78aef9c
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/expression/annotation/PreAuthorize.java
@@ -0,0 +1,27 @@
+package org.springframework.security.expression.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation for specifying a method access-control expression which will be evaluated to decide whether a
+ * method invocation is allowed or not.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 2.5
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface PreAuthorize {
+ /**
+ * @return the Spring-EL expression to be evaluated before invoking the protected method
+ */
+ String value();
+}
diff --git a/core/src/main/java/org/springframework/security/expression/annotation/PreFilter.java b/core/src/main/java/org/springframework/security/expression/annotation/PreFilter.java
new file mode 100644
index 0000000000..7f13cf8894
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/expression/annotation/PreFilter.java
@@ -0,0 +1,31 @@
+package org.springframework.security.expression.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation for specifying a method filtering expression which will be evaluated after a method has been invoked.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 2.5
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface PreFilter {
+ /**
+ * @return the Spring-EL expression to be evaluated before invoking the protected method
+ */
+ public String value();
+
+ /**
+ * @return the name of the parameter which should be filtered (must be an array or collection)
+ */
+ public String filterTarget();
+}
diff --git a/core/src/main/java/org/springframework/security/expression/support/AbstractExpressionBasedMethodConfigAttribute.java b/core/src/main/java/org/springframework/security/expression/support/AbstractExpressionBasedMethodConfigAttribute.java
new file mode 100644
index 0000000000..658f538847
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/expression/support/AbstractExpressionBasedMethodConfigAttribute.java
@@ -0,0 +1,42 @@
+package org.springframework.security.expression.support;
+
+import org.springframework.expression.Expression;
+import org.springframework.expression.ParseException;
+import org.springframework.expression.spel.SpelExpressionParser;
+import org.springframework.security.ConfigAttribute;
+import org.springframework.util.Assert;
+
+/**
+ * Contains both filtering and authorization expression meta-data for Spring-EL based access control.
+ *
+ * Base class for pre or post-invocation phases of a method invocation.
+ *
+ * Either filter or authorization expressions may be null, but not both.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 2.5
+ */
+abstract class AbstractExpressionBasedMethodConfigAttribute implements ConfigAttribute {
+ private final Expression filterExpression;
+ private final Expression authorizeExpression;
+
+ AbstractExpressionBasedMethodConfigAttribute(String filterExpression, String authorizeExpression) throws ParseException {
+ Assert.isTrue(filterExpression != null || authorizeExpression != null, "Filter and authorization Expressions cannot both be null");
+ SpelExpressionParser parser = new SpelExpressionParser();
+ this.filterExpression = filterExpression == null ? null : parser.parseExpression(filterExpression);
+ this.authorizeExpression = authorizeExpression == null ? null : parser.parseExpression(authorizeExpression);
+ }
+
+ Expression getFilterExpression() {
+ return filterExpression;
+ }
+
+ Expression getAuthorizeExpression() {
+ return authorizeExpression;
+ }
+
+ public String getAttribute() {
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/springframework/security/expression/support/ExpressionAnnotationMethodDefinitionSource.java b/core/src/main/java/org/springframework/security/expression/support/ExpressionAnnotationMethodDefinitionSource.java
new file mode 100644
index 0000000000..871cbc0236
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/expression/support/ExpressionAnnotationMethodDefinitionSource.java
@@ -0,0 +1,111 @@
+package org.springframework.security.expression.support;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.expression.ParseException;
+import org.springframework.security.ConfigAttribute;
+import org.springframework.security.ConfigAttributeDefinition;
+import org.springframework.security.config.SecurityConfigurationException;
+import org.springframework.security.expression.annotation.PostAuthorize;
+import org.springframework.security.expression.annotation.PostFilter;
+import org.springframework.security.expression.annotation.PreAuthorize;
+import org.springframework.security.expression.annotation.PreFilter;
+import org.springframework.security.intercept.method.AbstractFallbackMethodDefinitionSource;
+
+/**
+ * MethodDefinitionSource which extracts metadata from the @PreFilter and @PreAuthorize annotations
+ * placed on a method. The metadata is encapsulated in a {@link AbstractExpressionBasedMethodConfigAttribute} instance.
+ *
+ * @see MethodExpressionVoter
+ *
+ * @author Luke Taylor
+ * @since 2.5
+ * @version $Id$
+ */
+public class ExpressionAnnotationMethodDefinitionSource extends AbstractFallbackMethodDefinitionSource {
+
+ @Override
+ protected List findAttributes(Method method, Class targetClass) {
+ ConfigAttribute pre = processPreInvocationAnnotations(AnnotationUtils.findAnnotation(method, PreFilter.class),
+ AnnotationUtils.findAnnotation(method, PreAuthorize.class));
+ ConfigAttribute post = processPostInvocationAnnotations(AnnotationUtils.findAnnotation(method, PostFilter.class),
+ AnnotationUtils.findAnnotation(method, PostAuthorize.class));
+
+ if (pre == null && post == null) {
+ return null;
+ }
+
+ List attrs = new ArrayList(2);
+ if (pre != null) {
+ attrs.add(pre);
+ }
+
+ if (post != null) {
+ attrs.add(post);
+ }
+
+ return attrs;
+ }
+
+ @Override
+ protected List findAttributes(Class targetClass) {
+ ConfigAttribute pre = processPreInvocationAnnotations((PreFilter)targetClass.getAnnotation(PreFilter.class),
+ (PreAuthorize)targetClass.getAnnotation(PreAuthorize.class));
+ ConfigAttribute post = processPostInvocationAnnotations((PostFilter)targetClass.getAnnotation(PostFilter.class),
+ (PostAuthorize)targetClass.getAnnotation(PostAuthorize.class));
+
+ if (pre == null && post == null) {
+ return null;
+ }
+
+ List attrs = new ArrayList(2);
+ if (pre != null) {
+ attrs.add(pre);
+ }
+
+ if (post != null) {
+ attrs.add(post);
+ }
+
+ return attrs;
+ }
+
+ public Collection getConfigAttributeDefinitions() {
+ return null;
+ }
+
+ private ConfigAttribute processPreInvocationAnnotations(PreFilter preFilter, PreAuthorize preAuthz) {
+ if (preFilter == null && preAuthz == null) {
+ return null;
+ }
+
+ String preAuthorizeExpression = preAuthz == null ? null : preAuthz.value();
+ String preFilterExpression = preFilter == null ? null : preFilter.value();
+ String filterObject = preFilter == null ? null : preFilter.filterTarget();
+
+ try {
+ return new PreInvocationExpressionBasedMethodConfigAttribute(preFilterExpression, filterObject, preAuthorizeExpression);
+ } catch (ParseException e) {
+ throw new SecurityConfigurationException("Failed to parse expression '" + e.getExpressionString() + "'", e);
+ }
+ }
+
+ private ConfigAttribute processPostInvocationAnnotations(PostFilter postFilter, PostAuthorize postAuthz) {
+ if (postFilter == null && postAuthz == null) {
+ return null;
+ }
+
+ String postAuthorizeExpression = postAuthz == null ? null : postAuthz.value();
+ String postFilterExpression = postFilter == null ? null : postFilter.value();
+
+ try {
+ return new PostInvocationExpressionBasedMethodConfigAttribute(postFilterExpression, postAuthorizeExpression);
+ } catch (ParseException e) {
+ throw new SecurityConfigurationException("Failed to parse expression '" + e.getExpressionString() + "'", e);
+ }
+ }
+}
diff --git a/core/src/main/java/org/springframework/security/expression/support/MethodExpressionAfterInvocationProvider.java b/core/src/main/java/org/springframework/security/expression/support/MethodExpressionAfterInvocationProvider.java
new file mode 100644
index 0000000000..919a978eff
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/expression/support/MethodExpressionAfterInvocationProvider.java
@@ -0,0 +1,110 @@
+package org.springframework.security.expression.support;
+
+import java.lang.reflect.Method;
+
+import org.aopalliance.intercept.MethodInvocation;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
+import org.springframework.core.ParameterNameDiscoverer;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.Expression;
+import org.springframework.expression.spel.standard.StandardEvaluationContext;
+import org.springframework.security.AccessDeniedException;
+import org.springframework.security.Authentication;
+import org.springframework.security.ConfigAttribute;
+import org.springframework.security.ConfigAttributeDefinition;
+import org.springframework.security.afterinvocation.AfterInvocationProvider;
+import org.springframework.security.expression.ExpressionUtils;
+import org.springframework.security.expression.SecurityExpressionRoot;
+import org.springframework.util.ClassUtils;
+
+/**
+ * AfterInvocationProvider which handles the @PostAuthorize and @PostFilter annotation expressions.
+ *
+ * @author Luke Taylor
+ * @verson $Id$
+ * @since 2.5
+ */
+public class MethodExpressionAfterInvocationProvider implements AfterInvocationProvider {
+
+ protected final Log logger = LogFactory.getLog(getClass());
+
+ private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
+
+ public Object decide(Authentication authentication, Object object, ConfigAttributeDefinition config, Object returnedObject)
+ throws AccessDeniedException {
+
+ PostInvocationExpressionBasedMethodConfigAttribute mca = findMethodAccessControlExpression(config);
+
+ if (mca == null) {
+ return returnedObject;
+ }
+
+ StandardEvaluationContext ctx = new StandardEvaluationContext();
+ populateContextVariables(ctx, (MethodInvocation) object);
+ SecurityExpressionRoot expressionRoot = new SecurityExpressionRoot(authentication);
+ ctx.setRootObject(expressionRoot);
+
+ Expression postFilter = mca.getFilterExpression();
+ Expression postAuthorize = mca.getAuthorizeExpression();
+
+ if (postFilter != null) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Applying PostFilter expression " + postFilter);
+ }
+
+ if (returnedObject != null) {
+ returnedObject = ExpressionUtils.doFilter(returnedObject, postFilter, ctx);
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Return object is null, filtering will be skipped");
+ }
+ }
+ }
+
+ expressionRoot.setReturnObject(returnedObject);
+
+ if (postAuthorize != null && !ExpressionUtils.evaluateAsBoolean(postAuthorize, ctx)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("PostAuthorize expression rejected access");
+ }
+ throw new AccessDeniedException("Access is denied");
+ }
+
+ return returnedObject;
+ }
+
+ private void populateContextVariables(EvaluationContext ctx, MethodInvocation mi) {
+ Object[] args = mi.getArguments();
+ Object targetObject = mi.getThis();
+ Method method = ClassUtils.getMostSpecificMethod(mi.getMethod(), targetObject.getClass());
+ String[] paramNames = parameterNameDiscoverer.getParameterNames(method);
+
+ for(int i=0; i < args.length; i++) {
+ ctx.setVariable(paramNames[i], args[i]);
+ }
+ }
+
+ private PostInvocationExpressionBasedMethodConfigAttribute findMethodAccessControlExpression(ConfigAttributeDefinition config) {
+ // Find the MethodAccessControlExpression attribute
+ for (ConfigAttribute attribute : config.getConfigAttributes()) {
+ if (attribute instanceof PostInvocationExpressionBasedMethodConfigAttribute) {
+ return (PostInvocationExpressionBasedMethodConfigAttribute)attribute;
+ }
+ }
+
+ return null;
+ }
+
+ public boolean supports(ConfigAttribute attribute) {
+ return attribute instanceof PostInvocationExpressionBasedMethodConfigAttribute;
+ }
+
+ public boolean supports(Class clazz) {
+ return clazz.isAssignableFrom(MethodInvocation.class);
+ }
+
+
+
+}
diff --git a/core/src/main/java/org/springframework/security/expression/support/MethodExpressionVoter.java b/core/src/main/java/org/springframework/security/expression/support/MethodExpressionVoter.java
new file mode 100644
index 0000000000..19fb8bb2d4
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/expression/support/MethodExpressionVoter.java
@@ -0,0 +1,109 @@
+package org.springframework.security.expression.support;
+
+import java.lang.reflect.Method;
+
+import org.aopalliance.intercept.MethodInvocation;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
+import org.springframework.core.ParameterNameDiscoverer;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.Expression;
+import org.springframework.expression.spel.standard.StandardEvaluationContext;
+import org.springframework.security.Authentication;
+import org.springframework.security.ConfigAttribute;
+import org.springframework.security.ConfigAttributeDefinition;
+import org.springframework.security.expression.ExpressionUtils;
+import org.springframework.security.expression.SecurityExpressionRoot;
+import org.springframework.security.vote.AccessDecisionVoter;
+import org.springframework.util.ClassUtils;
+
+/**
+ * Voter which performs the actions for @PreFilter and @PostAuthorize annotations.
+ *
+ * If only a @PreFilter condition is specified, it will vote to grant access, otherwise it will vote
+ * to grant or deny access depending on whether the @PostAuthorize expression evaluates to 'true' or 'false',
+ * respectively.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 2.5
+ */
+public class MethodExpressionVoter implements AccessDecisionVoter {
+ protected final Log logger = LogFactory.getLog(getClass());
+
+ // TODO: Share this between classes
+ private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
+
+ public boolean supports(ConfigAttribute attribute) {
+ return attribute instanceof AbstractExpressionBasedMethodConfigAttribute;
+ }
+
+ public boolean supports(Class clazz) {
+ return clazz.isAssignableFrom(MethodInvocation.class);
+ }
+
+ public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
+ PreInvocationExpressionBasedMethodConfigAttribute mace = findMethodAccessControlExpression(config);
+
+ if (mace == null) {
+ // No expression based metadata, so abstain
+ return ACCESS_ABSTAIN;
+ }
+
+ StandardEvaluationContext ctx = new StandardEvaluationContext();
+ Object filterTarget =
+ populateContextVariablesAndFindFilterTarget(ctx, (MethodInvocation)object, mace.getFilterTarget());
+
+ ctx.setRootObject(new SecurityExpressionRoot(authentication));
+
+ Expression preFilter = mace.getFilterExpression();
+ Expression preAuthorize = mace.getAuthorizeExpression();
+
+ if (preFilter != null) {
+ // TODO: Allow null target if only single parameter, or single collection/array?
+ Object filtered = ExpressionUtils.doFilter(filterTarget, preFilter, ctx);
+ }
+
+ if (preAuthorize == null) {
+ return ACCESS_GRANTED;
+ }
+
+ return ExpressionUtils.evaluateAsBoolean(preAuthorize, ctx) ? ACCESS_GRANTED : ACCESS_DENIED;
+ }
+
+ private Object populateContextVariablesAndFindFilterTarget(EvaluationContext ctx, MethodInvocation mi,
+ String filterTargetName) {
+
+ Object[] args = mi.getArguments();
+ Object targetObject = mi.getThis();
+ Method method = ClassUtils.getMostSpecificMethod(mi.getMethod(), targetObject.getClass());
+ Object filterTarget = null;
+ String[] paramNames = parameterNameDiscoverer.getParameterNames(method);
+
+ for(int i=0; i < args.length; i++) {
+ ctx.setVariable(paramNames[i], args[i]);
+ if (filterTargetName != null && paramNames[i].equals(filterTargetName)) {
+ filterTarget = args[i];
+ }
+ }
+
+ if (filterTargetName != null && filterTarget == null) {
+ throw new IllegalArgumentException("No filter target argument with name " + filterTargetName +
+ " found in method: " + method.getName());
+ }
+
+ return filterTarget;
+ }
+
+ private PreInvocationExpressionBasedMethodConfigAttribute findMethodAccessControlExpression(ConfigAttributeDefinition config) {
+ // Find the MethodAccessControlExpression attribute
+ for (ConfigAttribute attribute : config.getConfigAttributes()) {
+ if (attribute instanceof AbstractExpressionBasedMethodConfigAttribute) {
+ return (PreInvocationExpressionBasedMethodConfigAttribute)attribute;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/core/src/main/java/org/springframework/security/expression/support/PostInvocationExpressionBasedMethodConfigAttribute.java b/core/src/main/java/org/springframework/security/expression/support/PostInvocationExpressionBasedMethodConfigAttribute.java
new file mode 100644
index 0000000000..52a94ac7dc
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/expression/support/PostInvocationExpressionBasedMethodConfigAttribute.java
@@ -0,0 +1,12 @@
+package org.springframework.security.expression.support;
+
+import org.springframework.expression.ParseException;
+
+class PostInvocationExpressionBasedMethodConfigAttribute extends AbstractExpressionBasedMethodConfigAttribute {
+
+ PostInvocationExpressionBasedMethodConfigAttribute(String filterExpression, String authorizeExpression)
+ throws ParseException {
+ super(filterExpression, authorizeExpression);
+ }
+
+}
diff --git a/core/src/main/java/org/springframework/security/expression/support/PreInvocationExpressionBasedMethodConfigAttribute.java b/core/src/main/java/org/springframework/security/expression/support/PreInvocationExpressionBasedMethodConfigAttribute.java
new file mode 100644
index 0000000000..1c522e1a96
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/expression/support/PreInvocationExpressionBasedMethodConfigAttribute.java
@@ -0,0 +1,18 @@
+package org.springframework.security.expression.support;
+
+import org.springframework.expression.ParseException;
+
+class PreInvocationExpressionBasedMethodConfigAttribute extends AbstractExpressionBasedMethodConfigAttribute {
+ private final String filterTarget;
+
+ PreInvocationExpressionBasedMethodConfigAttribute(String filterExpression, String filterTarget,
+ String authorizeExpression) throws ParseException {
+ super(filterExpression, authorizeExpression);
+
+ this.filterTarget = filterTarget;
+ }
+
+ String getFilterTarget() {
+ return filterTarget;
+ }
+}
diff --git a/core/src/main/java/org/springframework/security/intercept/method/AbstractFallbackMethodDefinitionSource.java b/core/src/main/java/org/springframework/security/intercept/method/AbstractFallbackMethodDefinitionSource.java
index 237617ff8f..14c9023f09 100644
--- a/core/src/main/java/org/springframework/security/intercept/method/AbstractFallbackMethodDefinitionSource.java
+++ b/core/src/main/java/org/springframework/security/intercept/method/AbstractFallbackMethodDefinitionSource.java
@@ -2,6 +2,7 @@ package org.springframework.security.intercept.method;
import java.lang.reflect.Method;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.aopalliance.intercept.MethodInvocation;
@@ -9,6 +10,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.CodeSignature;
+import org.springframework.security.ConfigAttribute;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -88,12 +90,14 @@ public abstract class AbstractFallbackMethodDefinitionSource implements MethodDe
}
else {
// We need to work it out.
- ConfigAttributeDefinition cfgAtt = computeAttributes(method, targetClass);
+ List attributes = computeAttributes(method, targetClass);
+ ConfigAttributeDefinition cfgAtt = null;
// Put it in the cache.
- if (cfgAtt == null) {
+ if (attributes == null) {
this.attributeCache.put(cacheKey, NULL_CONFIG_ATTRIBUTE);
- }
- else {
+ } else {
+ cfgAtt = new ConfigAttributeDefinition(attributes);
+
if (logger.isDebugEnabled()) {
logger.debug("Adding security method [" + cacheKey + "] with attribute [" + cfgAtt + "]");
}
@@ -110,12 +114,12 @@ public abstract class AbstractFallbackMethodDefinitionSource implements MethodDe
* @param targetClass the target class for this invocation (may be null)
* @return
*/
- private ConfigAttributeDefinition computeAttributes(Method method, Class targetClass) {
+ private List computeAttributes(Method method, Class targetClass) {
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
// First try is the method in the target class.
- ConfigAttributeDefinition attr = findAttributes(specificMethod, targetClass);
+ List attr = findAttributes(specificMethod, targetClass);
if (attr != null) {
return attr;
}
@@ -152,7 +156,7 @@ public abstract class AbstractFallbackMethodDefinitionSource implements MethodDe
* @param targetClass the target class for the invocation (may be null)
* @return the security metadata (or null if no metadata applies)
*/
- protected abstract ConfigAttributeDefinition findAttributes(Method method, Class targetClass);
+ protected abstract List findAttributes(Method method, Class targetClass);
/**
* Obtains the security metadata registered against the specified class.
@@ -166,7 +170,7 @@ public abstract class AbstractFallbackMethodDefinitionSource implements MethodDe
* @param clazz the target class for the invocation (never null)
* @return the security metadata (or null if no metadata applies)
*/
- protected abstract ConfigAttributeDefinition findAttributes(Class clazz);
+ protected abstract List findAttributes(Class clazz);
private static class DefaultCacheKey {
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 bc0a68f1d7..d9afe74907 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
@@ -27,6 +27,7 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.security.ConfigAttribute;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -54,10 +55,10 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
/** Map from RegisteredMethod to ConfigAttributeDefinition */
- protected Map methodMap = new HashMap();
+ protected Map> methodMap = new HashMap();
/** Map from RegisteredMethod to name pattern used for registration */
- private Map nameMap = new HashMap();
+ private Map nameMap = new HashMap();
//~ Methods ========================================================================================================
@@ -73,21 +74,21 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry) iterator.next();
- addSecureMethod((String)entry.getKey(), (ConfigAttributeDefinition)entry.getValue());
+ addSecureMethod((String)entry.getKey(), (List)entry.getValue());
}
}
/**
* Implementation does not support class-level attributes.
*/
- protected ConfigAttributeDefinition findAttributes(Class clazz) {
+ protected List 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) {
+ protected List findAttributes(Method method, Class targetClass) {
if (targetClass == null) {
return null;
}
@@ -95,10 +96,10 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini
return findAttributesSpecifiedAgainst(method, targetClass);
}
- private ConfigAttributeDefinition findAttributesSpecifiedAgainst(Method method, Class clazz) {
+ private List findAttributesSpecifiedAgainst(Method method, Class clazz) {
RegisteredMethod registeredMethod = new RegisteredMethod(method, clazz);
if (methodMap.containsKey(registeredMethod)) {
- return (ConfigAttributeDefinition) methodMap.get(registeredMethod);
+ return (List) methodMap.get(registeredMethod);
}
// Search superclass
if (clazz.getSuperclass() != null) {
@@ -114,7 +115,7 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini
* @param name type and method name, separated by a dot
* @param attr required authorities associated with the method
*/
- public void addSecureMethod(String name, ConfigAttributeDefinition attr) {
+ public void addSecureMethod(String name, List extends ConfigAttribute> attr) {
int lastDotIndex = name.lastIndexOf(".");
if (lastDotIndex == -1) {
@@ -138,7 +139,7 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini
* @param mappedName mapped method name, which the javaType has declared or inherited
* @param attr required authorities associated with the method
*/
- public void addSecureMethod(Class javaType, String mappedName, ConfigAttributeDefinition attr) {
+ public void addSecureMethod(Class javaType, String mappedName, List extends ConfigAttribute> attr) {
String name = javaType.getName() + '.' + mappedName;
if (logger.isDebugEnabled()) {
@@ -187,7 +188,7 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini
* 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) {
+ public void addSecureMethod(Class javaType, Method method, List extends ConfigAttribute> attr) {
RegisteredMethod key = new RegisteredMethod(method, javaType);
if (methodMap.containsKey(key)) {
@@ -204,7 +205,7 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini
* @param method the method to be secured
* @param attr required authorities associated with the method
*/
- private void addSecureMethod(RegisteredMethod method, ConfigAttributeDefinition attr) {
+ private void addSecureMethod(RegisteredMethod method, List extends ConfigAttribute> attr) {
Assert.notNull(method, "RegisteredMethod required");
Assert.notNull(attr, "Configuration attribute required");
if (logger.isInfoEnabled()) {
@@ -219,7 +220,13 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini
* @return the attributes explicitly defined against this bean
*/
public Collection getConfigAttributeDefinitions() {
- return Collections.unmodifiableCollection(methodMap.values());
+ List configAttrs = new ArrayList(methodMap.values().size());
+
+ for(List extends ConfigAttribute> attrList : methodMap.values()) {
+ configAttrs.add(new ConfigAttributeDefinition(attrList));
+ }
+
+ return configAttrs;
}
/**
diff --git a/core/src/main/java/org/springframework/security/intercept/method/MethodDefinitionAttributes.java b/core/src/main/java/org/springframework/security/intercept/method/MethodDefinitionAttributes.java
deleted file mode 100644
index cddfae5298..0000000000
--- a/core/src/main/java/org/springframework/security/intercept/method/MethodDefinitionAttributes.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.security.intercept.method;
-
-import java.lang.reflect.Method;
-import java.util.Collection;
-
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.metadata.Attributes;
-import org.springframework.security.ConfigAttribute;
-import org.springframework.security.ConfigAttributeDefinition;
-import org.springframework.util.Assert;
-
-
-/**
- * Provides {@link ConfigAttributeDefinition}s for a method signature (via the lookupAttributes method)
- * by delegating to a configured {@link Attributes} object. The latter may use Commons attributes
- * or some other approach to determine the ConfigAttributes which apply.
- *
- *
- * Note that attributes defined against parent classes (either for their methods or interfaces) are not
- * detected. The attributes must be defined against an explicit method or interface on the intercepted class.
- *
- *
- * Attributes detected that do not implement {@link ConfigAttribute} will be ignored.
- *
- * @author Cameron Braid
- * @author Ben Alex
- * @version $Id$
- */
-public class MethodDefinitionAttributes extends AbstractFallbackMethodDefinitionSource implements InitializingBean {
- //~ Instance fields ================================================================================================
-
- private Attributes attributes;
-
- //~ Methods ========================================================================================================
-
- public void afterPropertiesSet() throws Exception {
- Assert.notNull(attributes, "attributes required");
- }
-
- public Collection getConfigAttributeDefinitions() {
- return null;
- }
-
- protected ConfigAttributeDefinition findAttributes(Class clazz) {
- return ConfigAttributeDefinition.createFiltered(attributes.getAttributes(clazz));
- }
-
- protected ConfigAttributeDefinition findAttributes(Method method, Class targetClass) {
- return ConfigAttributeDefinition.createFiltered(attributes.getAttributes(method));
- }
-
- public void setAttributes(Attributes attributes) {
- Assert.notNull(attributes, "Attributes required");
- this.attributes = attributes;
- }
-}
diff --git a/core/src/main/java/org/springframework/security/intercept/method/MethodDefinitionSourceEditor.java b/core/src/main/java/org/springframework/security/intercept/method/MethodDefinitionSourceEditor.java
index 296d99b97c..15ddc8c94e 100644
--- a/core/src/main/java/org/springframework/security/intercept/method/MethodDefinitionSourceEditor.java
+++ b/core/src/main/java/org/springframework/security/intercept/method/MethodDefinitionSourceEditor.java
@@ -15,7 +15,8 @@
package org.springframework.security.intercept.method;
-import org.springframework.security.ConfigAttributeDefinition;
+import org.springframework.security.ConfigAttribute;
+import org.springframework.security.SecurityConfig;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -25,7 +26,9 @@ import org.springframework.util.StringUtils;
import java.beans.PropertyEditorSupport;
+import java.util.ArrayList;
import java.util.Iterator;
+import java.util.List;
import java.util.Properties;
import java.util.Map;
import java.util.LinkedHashMap;
@@ -65,8 +68,13 @@ public class MethodDefinitionSourceEditor extends PropertyEditorSupport {
String value = props.getProperty(name);
String[] tokens = StringUtils.commaDelimitedListToStringArray(value);
+ List attributes = new ArrayList(tokens.length);
- mappings.put(name, new ConfigAttributeDefinition(tokens));
+ for(String token : tokens) {
+ attributes.add(new SecurityConfig(token));
+ }
+
+ mappings.put(name, attributes);
}
setValue(new MapBasedMethodDefinitionSource(mappings));
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 92a9b9cc1f..6e33d870cf 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
@@ -4,6 +4,7 @@ import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -14,6 +15,7 @@ import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.PointcutPrimitive;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.security.ConfigAttribute;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.intercept.method.aopalliance.MethodDefinitionSourceAdvisor;
import org.springframework.util.Assert;
@@ -57,7 +59,7 @@ 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 Map> pointcutMap = new LinkedHashMap();
private MapBasedMethodDefinitionSource mapBasedMethodDefinitionSource;
private PointcutParser parser;
@@ -119,7 +121,7 @@ public final class ProtectPointcutPostProcessor implements BeanPostProcessor {
// Handle accordingly
if (matches) {
- ConfigAttributeDefinition attr = (ConfigAttributeDefinition) pointcutMap.get(expression.getPointcutExpression());
+ List attr = 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 + "'");
@@ -131,18 +133,17 @@ public final class ProtectPointcutPostProcessor implements BeanPostProcessor {
return matches;
}
- public void setPointcutMap(Map map) {
+ 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);
+ List value = map.get(expression);
+ addPointcut(expression, value);
}
}
- private void addPointcut(String pointcutExpression, ConfigAttributeDefinition definition) {
+ private void addPointcut(String pointcutExpression, List definition) {
Assert.hasText(pointcutExpression, "An AspectJ pointcut expression is required");
Assert.notNull(definition, "ConfigAttributeDefinition required");
pointcutExpression = replaceBooleanOperators(pointcutExpression);
diff --git a/core/src/main/java/org/springframework/security/util/InMemoryXmlApplicationContext.java b/core/src/main/java/org/springframework/security/util/InMemoryXmlApplicationContext.java
index 8f1083906a..d9df879c7a 100644
--- a/core/src/main/java/org/springframework/security/util/InMemoryXmlApplicationContext.java
+++ b/core/src/main/java/org/springframework/security/util/InMemoryXmlApplicationContext.java
@@ -13,9 +13,9 @@ public class InMemoryXmlApplicationContext extends AbstractXmlApplicationContext
" xmlns:b='http://www.springframework.org/schema/beans'\n" +
" xmlns:aop='http://www.springframework.org/schema/aop'\n" +
" xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'\n" +
- " xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n" +
- "http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n" +
- "http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.2.xsd'>\n";
+ " xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\n" +
+ "http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd\n" +
+ "http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.5.xsd'>\n";
private static final String BEANS_CLOSE = "\n";
Resource inMemoryXml;
diff --git a/core/src/main/resources/META-INF/spring.schemas b/core/src/main/resources/META-INF/spring.schemas
index 5bf71855e0..9b0c7c8952 100644
--- a/core/src/main/resources/META-INF/spring.schemas
+++ b/core/src/main/resources/META-INF/spring.schemas
@@ -1,5 +1,6 @@
-http\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-2.0.4.xsd
+http\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-2.5.xsd
http\://www.springframework.org/schema/security/spring-security-2.0.xsd=org/springframework/security/config/spring-security-2.0.xsd
http\://www.springframework.org/schema/security/spring-security-2.0.1.xsd=org/springframework/security/config/spring-security-2.0.1.xsd
http\://www.springframework.org/schema/security/spring-security-2.0.2.xsd=org/springframework/security/config/spring-security-2.0.2.xsd
http\://www.springframework.org/schema/security/spring-security-2.0.4.xsd=org/springframework/security/config/spring-security-2.0.4.xsd
+http\://www.springframework.org/schema/security/spring-security-2.5.xsd=org/springframework/security/config/spring-security-2.5.xsd
diff --git a/core/src/test/java/org/springframework/security/annotation/BusinessService.java b/core/src/test/java/org/springframework/security/annotation/BusinessService.java
index 2b7724a422..af2ecb2541 100644
--- a/core/src/test/java/org/springframework/security/annotation/BusinessService.java
+++ b/core/src/test/java/org/springframework/security/annotation/BusinessService.java
@@ -15,9 +15,13 @@
package org.springframework.security.annotation;
+import java.util.List;
+
import javax.annotation.security.RolesAllowed;
import javax.annotation.security.PermitAll;
+import org.springframework.security.expression.annotation.PreAuthorize;
+
/**
* @version $Id$
*/
@@ -28,6 +32,7 @@ public interface BusinessService {
@Secured({"ROLE_ADMIN"})
@RolesAllowed({"ROLE_ADMIN"})
+ @PreAuthorize("hasRole('ROLE_ADMIN')")
public void someAdminMethod();
@Secured({"ROLE_USER", "ROLE_ADMIN"})
@@ -45,4 +50,11 @@ public interface BusinessService {
public int someOther(String s);
public int someOther(int input);
+
+ public List methodReturningAList(List someList);
+
+ public Object[] methodReturningAnArray(Object[] someArray);
+
+ public List methodReturningAList(String userName, String extraParam);
+
}
diff --git a/core/src/test/java/org/springframework/security/annotation/BusinessServiceImpl.java b/core/src/test/java/org/springframework/security/annotation/BusinessServiceImpl.java
index 79287c2858..9d79e5a8b3 100644
--- a/core/src/test/java/org/springframework/security/annotation/BusinessServiceImpl.java
+++ b/core/src/test/java/org/springframework/security/annotation/BusinessServiceImpl.java
@@ -1,5 +1,8 @@
package org.springframework.security.annotation;
+import java.util.ArrayList;
+import java.util.List;
+
/**
*
* @author Joe Scalise
@@ -33,4 +36,17 @@ public class BusinessServiceImpl implements BusinessService {
public int someOther(int input) {
return input;
}
+
+ public List methodReturningAList(List someList) {
+ return someList;
+ }
+
+ public List methodReturningAList(String userName, String arg2) {
+ return new ArrayList();
+ }
+
+ public Object[] methodReturningAnArray(Object[] someArray) {
+ return null;
+ }
+
}
diff --git a/core/src/test/java/org/springframework/security/annotation/Jsr250BusinessServiceImpl.java b/core/src/test/java/org/springframework/security/annotation/Jsr250BusinessServiceImpl.java
index 70f13e7a5f..d79a77ffc4 100644
--- a/core/src/test/java/org/springframework/security/annotation/Jsr250BusinessServiceImpl.java
+++ b/core/src/test/java/org/springframework/security/annotation/Jsr250BusinessServiceImpl.java
@@ -1,5 +1,8 @@
package org.springframework.security.annotation;
+import java.util.ArrayList;
+import java.util.List;
+
import javax.annotation.security.RolesAllowed;
import javax.annotation.security.PermitAll;
@@ -34,4 +37,17 @@ public class Jsr250BusinessServiceImpl implements BusinessService {
public int someOther(int input) {
return input;
}
+
+ public List methodReturningAList(List someList) {
+ return someList;
+ }
+
+ public List methodReturningAList(String userName, String arg2) {
+ return new ArrayList();
+ }
+
+ public Object[] methodReturningAnArray(Object[] someArray) {
+ return null;
+ }
+
}
diff --git a/core/src/test/java/org/springframework/security/annotation/Jsr250MethodDefinitionSourceTests.java b/core/src/test/java/org/springframework/security/annotation/Jsr250MethodDefinitionSourceTests.java
index 56762dd1fc..c78666472b 100644
--- a/core/src/test/java/org/springframework/security/annotation/Jsr250MethodDefinitionSourceTests.java
+++ b/core/src/test/java/org/springframework/security/annotation/Jsr250MethodDefinitionSourceTests.java
@@ -2,6 +2,8 @@ package org.springframework.security.annotation;
import static org.junit.Assert.assertEquals;
+import java.util.List;
+
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
@@ -9,6 +11,7 @@ import javax.annotation.security.RolesAllowed;
import junit.framework.Assert;
import org.junit.Test;
+import org.springframework.security.ConfigAttribute;
import org.springframework.security.ConfigAttributeDefinition;
/**
@@ -17,56 +20,56 @@ import org.springframework.security.ConfigAttributeDefinition;
* @version $Id$
*/
public class Jsr250MethodDefinitionSourceTests {
- Jsr250MethodDefinitionSource mds = new Jsr250MethodDefinitionSource();
+ Jsr250MethodDefinitionSource mds = new Jsr250MethodDefinitionSource();
A a = new A();
UserAllowedClass userAllowed = new UserAllowedClass();
DenyAllClass denyAll = new DenyAllClass();
@Test
public void methodWithRolesAllowedHasCorrectAttribute() throws Exception {
- ConfigAttributeDefinition accessAttributes = mds.findAttributes(a.getClass().getMethod("adminMethod"), null);
- assertEquals(1, accessAttributes.getConfigAttributes().size());
- assertEquals("ADMIN", accessAttributes.getConfigAttributes().iterator().next().toString());
+ List accessAttributes = mds.findAttributes(a.getClass().getMethod("adminMethod"), null);
+ assertEquals(1, accessAttributes.size());
+ assertEquals("ADMIN", accessAttributes.get(0).toString());
}
@Test
public void permitAllMethodHasPermitAllAttribute() throws Exception {
- ConfigAttributeDefinition accessAttributes = mds.findAttributes(a.getClass().getMethod("permitAllMethod"), null);
- assertEquals(1, accessAttributes.getConfigAttributes().size());
- assertEquals("javax.annotation.security.PermitAll", accessAttributes.getConfigAttributes().iterator().next().toString());
+ List accessAttributes = mds.findAttributes(a.getClass().getMethod("permitAllMethod"), null);
+ assertEquals(1, accessAttributes.size());
+ assertEquals("javax.annotation.security.PermitAll", accessAttributes.get(0).toString());
}
@Test
public void noRoleMethodHasDenyAllAttributeWithDenyAllClass() throws Exception {
- ConfigAttributeDefinition accessAttributes = mds.findAttributes(denyAll.getClass());
- assertEquals(1, accessAttributes.getConfigAttributes().size());
- assertEquals("javax.annotation.security.DenyAll", accessAttributes.getConfigAttributes().iterator().next().toString());
+ List accessAttributes = mds.findAttributes(denyAll.getClass());
+ assertEquals(1, accessAttributes.size());
+ assertEquals("javax.annotation.security.DenyAll", accessAttributes.get(0).toString());
}
@Test
public void adminMethodHasAdminAttributeWithDenyAllClass() throws Exception {
- ConfigAttributeDefinition accessAttributes = mds.findAttributes(denyAll.getClass().getMethod("adminMethod"), null);
- assertEquals(1, accessAttributes.getConfigAttributes().size());
- assertEquals("ADMIN", accessAttributes.getConfigAttributes().iterator().next().toString());
+ List accessAttributes = mds.findAttributes(denyAll.getClass().getMethod("adminMethod"), null);
+ assertEquals(1, accessAttributes.size());
+ assertEquals("ADMIN", accessAttributes.get(0).toString());
}
@Test
public void noRoleMethodHasNoAttributes() throws Exception {
- ConfigAttributeDefinition accessAttributes = mds.findAttributes(a.getClass().getMethod("noRoleMethod"), null);
+ List accessAttributes = mds.findAttributes(a.getClass().getMethod("noRoleMethod"), null);
Assert.assertNull(accessAttributes);
}
-
+
@Test
public void classRoleIsAppliedToNoRoleMethod() throws Exception {
- ConfigAttributeDefinition accessAttributes = mds.findAttributes(userAllowed.getClass().getMethod("noRoleMethod"), null);
+ List accessAttributes = mds.findAttributes(userAllowed.getClass().getMethod("noRoleMethod"), null);
Assert.assertNull(accessAttributes);
}
@Test
public void methodRoleOverridesClassRole() throws Exception {
- ConfigAttributeDefinition accessAttributes = mds.findAttributes(userAllowed.getClass().getMethod("adminMethod"), null);
- assertEquals(1, accessAttributes.getConfigAttributes().size());
- assertEquals("ADMIN", accessAttributes.getConfigAttributes().iterator().next().toString());
+ List accessAttributes = mds.findAttributes(userAllowed.getClass().getMethod("adminMethod"), null);
+ assertEquals(1, accessAttributes.size());
+ assertEquals("ADMIN", accessAttributes.get(0).toString());
}
//~ Inner Classes ======================================================================================================
@@ -87,7 +90,7 @@ public class Jsr250MethodDefinitionSourceTests {
public void noRoleMethod() {}
@RolesAllowed("ADMIN")
- public void adminMethod() {}
+ public void adminMethod() {}
}
@DenyAll
@@ -96,7 +99,7 @@ public class Jsr250MethodDefinitionSourceTests {
public void noRoleMethod() {}
@RolesAllowed("ADMIN")
- public void adminMethod() {}
+ public void adminMethod() {}
}
diff --git a/core/src/test/java/org/springframework/security/annotation/SecuredMethodDefinitionSourceTests.java b/core/src/test/java/org/springframework/security/annotation/SecuredMethodDefinitionSourceTests.java
index 1bb761ea6f..78ba303baa 100644
--- a/core/src/test/java/org/springframework/security/annotation/SecuredMethodDefinitionSourceTests.java
+++ b/core/src/test/java/org/springframework/security/annotation/SecuredMethodDefinitionSourceTests.java
@@ -15,11 +15,13 @@
package org.springframework.security.annotation;
import java.lang.reflect.Method;
+import java.util.List;
import junit.framework.TestCase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.springframework.security.ConfigAttribute;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.SecurityConfig;
import org.springframework.util.StringUtils;
@@ -50,22 +52,19 @@ public class SecuredMethodDefinitionSourceTests extends TestCase {
fail("Should be a superMethod called 'someUserMethod3' on class!");
}
- ConfigAttributeDefinition attrs = this.mds.findAttributes(method, DepartmentServiceImpl.class);
+ List attrs = mds.findAttributes(method, DepartmentServiceImpl.class);
assertNotNull(attrs);
if (logger.isDebugEnabled()) {
- logger.debug("attrs: " + StringUtils.collectionToCommaDelimitedString(attrs.getConfigAttributes()));
+ logger.debug("attrs: " + StringUtils.collectionToCommaDelimitedString(attrs));
}
// expect 1 attribute
- assertTrue("Did not find 1 attribute", attrs.getConfigAttributes().size() == 1);
+ assertTrue("Did not find 1 attribute", attrs.size() == 1);
// should have 1 SecurityConfig
- for (Object obj : attrs.getConfigAttributes()) {
- assertTrue(obj instanceof SecurityConfig);
-
- SecurityConfig sc = (SecurityConfig) obj;
+ for (ConfigAttribute sc : attrs) {
assertEquals("Found an incorrect role", "ROLE_ADMIN", sc.getAttribute());
}
@@ -77,37 +76,35 @@ public class SecuredMethodDefinitionSourceTests extends TestCase {
fail("Should be a superMethod called 'someUserMethod3' on class!");
}
- ConfigAttributeDefinition superAttrs = this.mds.findAttributes(superMethod, DepartmentServiceImpl.class);
+ List superAttrs = this.mds.findAttributes(superMethod, DepartmentServiceImpl.class);
assertNotNull(superAttrs);
if (logger.isDebugEnabled()) {
- logger.debug("superAttrs: " + StringUtils.collectionToCommaDelimitedString(superAttrs.getConfigAttributes()));
+ logger.debug("superAttrs: " + StringUtils.collectionToCommaDelimitedString(superAttrs));
}
// This part of the test relates to SEC-274
// expect 1 attribute
- assertTrue("Did not find 1 attribute", superAttrs.getConfigAttributes().size() == 1);
+ assertEquals("Did not find 1 attribute", 1, superAttrs.size());
// should have 1 SecurityConfig
- for (Object obj : superAttrs.getConfigAttributes()) {
- assertTrue(obj instanceof SecurityConfig);
- SecurityConfig sc = (SecurityConfig) obj;
+ for (ConfigAttribute sc : superAttrs) {
assertEquals("Found an incorrect role", "ROLE_ADMIN", sc.getAttribute());
}
}
public void testGetAttributesClass() {
- ConfigAttributeDefinition attrs = this.mds.findAttributes(BusinessService.class);
+ List attrs = this.mds.findAttributes(BusinessService.class);
assertNotNull(attrs);
// expect 1 annotation
- assertTrue(attrs.getConfigAttributes().size() == 1);
+ assertEquals(1, attrs.size());
// should have 1 SecurityConfig
- SecurityConfig sc = (SecurityConfig) attrs.getConfigAttributes().iterator().next();
+ SecurityConfig sc = ((SecurityConfig) attrs.get(0));
- assertTrue(sc.getAttribute().equals("ROLE_USER"));
+ assertEquals("ROLE_USER", sc.getAttribute());
}
public void testGetAttributesMethod() {
@@ -119,21 +116,19 @@ public class SecuredMethodDefinitionSourceTests extends TestCase {
fail("Should be a method called 'someUserAndAdminMethod' on class!");
}
- ConfigAttributeDefinition attrs = this.mds.findAttributes(method, BusinessService.class);
+ List attrs = this.mds.findAttributes(method, BusinessService.class);
assertNotNull(attrs);
// expect 2 attributes
- assertTrue(attrs.getConfigAttributes().size() == 2);
+ assertEquals(2, attrs.size());
boolean user = false;
boolean admin = false;
// should have 2 SecurityConfigs
- for (Object obj : attrs.getConfigAttributes()) {
- assertTrue(obj instanceof SecurityConfig);
-
- SecurityConfig sc = (SecurityConfig) obj;
+ for (ConfigAttribute sc : attrs) {
+ assertTrue(sc instanceof SecurityConfig);
if (sc.getAttribute().equals("ROLE_USER")) {
user = true;
@@ -145,5 +140,5 @@ public class SecuredMethodDefinitionSourceTests extends TestCase {
// expect to have ROLE_USER and ROLE_ADMIN
assertTrue(user && admin);
}
-
+
}
diff --git a/core/src/test/java/org/springframework/security/config/CustomAfterInvocationProviderBeanDefinitionDecoratorTests.java b/core/src/test/java/org/springframework/security/config/CustomAfterInvocationProviderBeanDefinitionDecoratorTests.java
index 73d3c6c274..33222274a4 100644
--- a/core/src/test/java/org/springframework/security/config/CustomAfterInvocationProviderBeanDefinitionDecoratorTests.java
+++ b/core/src/test/java/org/springframework/security/config/CustomAfterInvocationProviderBeanDefinitionDecoratorTests.java
@@ -33,8 +33,8 @@ public class CustomAfterInvocationProviderBeanDefinitionDecoratorTests {
MethodSecurityInterceptor msi = (MethodSecurityInterceptor) appContext.getBean(BeanIds.METHOD_SECURITY_INTERCEPTOR);
AfterInvocationProviderManager apm = (AfterInvocationProviderManager) msi.getAfterInvocationManager();
assertNotNull(apm);
- assertEquals(1, apm.getProviders().size());
- assertTrue(apm.getProviders().get(0) instanceof MockAfterInvocationProvider);
+ assertEquals(2, apm.getProviders().size());
+ assertTrue(apm.getProviders().get(1) instanceof MockAfterInvocationProvider);
}
private void setContext(String context) {
diff --git a/core/src/test/java/org/springframework/security/config/GlobalMethodSecurityBeanDefinitionParserTests.java b/core/src/test/java/org/springframework/security/config/GlobalMethodSecurityBeanDefinitionParserTests.java
index 4b2c63767e..c55ec7c8e7 100644
--- a/core/src/test/java/org/springframework/security/config/GlobalMethodSecurityBeanDefinitionParserTests.java
+++ b/core/src/test/java/org/springframework/security/config/GlobalMethodSecurityBeanDefinitionParserTests.java
@@ -3,6 +3,9 @@ package org.springframework.security.config;
import static org.junit.Assert.*;
import static org.springframework.security.config.ConfigTestUtils.*;
+import java.util.ArrayList;
+import java.util.List;
+
import org.junit.After;
import org.junit.Test;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
@@ -180,6 +183,50 @@ public class GlobalMethodSecurityBeanDefinitionParserTests {
target.someUserMethod1();
}
+ @Test(expected=AccessDeniedException.class)
+ public void accessIsDeniedForHasRoleExpression() {
+ setContext(
+ "" +
+ "" +
+ AUTH_PROVIDER_XML);
+ SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken("bob","bobspassword"));
+ target = (BusinessService) appContext.getBean("target");
+ target.someAdminMethod();
+ }
+
+ @Test
+ public void preAndPostFilterAnnotationsWorkWithLists() {
+ setContext(
+ "" +
+ "" +
+ AUTH_PROVIDER_XML);
+ SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken("bob","bobspassword"));
+ target = (BusinessService) appContext.getBean("target");
+ List arg = new ArrayList();
+ arg.add("joe");
+ arg.add("bob");
+ arg.add("sam");
+ List result = target.methodReturningAList(arg);
+ // Expression is (filterObject == name or filterObject == 'sam'), so "joe" should be gone after pre-filter
+ // PostFilter should remove sam from the return object
+ assertEquals(1, result.size());
+ assertEquals("bob", result.get(0));
+ }
+
+ @Test
+ public void preAndPostFilterAnnotationsWorkWithArrays() {
+ setContext(
+ "" +
+ "" +
+ AUTH_PROVIDER_XML);
+ SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken("bob","bobspassword"));
+ target = (BusinessService) appContext.getBean("target");
+ Object[] arg = new String[] {"joe", "bob", "sam"};
+ Object[] result = target.methodReturningAnArray(arg);
+ assertEquals(1, result.length);
+ assertEquals("bob", result[0]);
+ }
+
private void setContext(String context) {
appContext = new InMemoryXmlApplicationContext(context);
}
diff --git a/core/src/test/java/org/springframework/security/expression/support/MethodExpressionVoterTests.java b/core/src/test/java/org/springframework/security/expression/support/MethodExpressionVoterTests.java
new file mode 100644
index 0000000000..7d0c15947d
--- /dev/null
+++ b/core/src/test/java/org/springframework/security/expression/support/MethodExpressionVoterTests.java
@@ -0,0 +1,71 @@
+package org.springframework.security.expression.support;
+
+import static org.junit.Assert.assertEquals;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.aopalliance.intercept.MethodInvocation;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.security.ConfigAttributeDefinition;
+import org.springframework.security.annotation.ExpressionProtectedBusinessServiceImpl;
+import org.springframework.security.expression.support.AbstractExpressionBasedMethodConfigAttribute;
+import org.springframework.security.expression.support.MethodExpressionVoter;
+import org.springframework.security.providers.TestingAuthenticationToken;
+import org.springframework.security.util.SimpleMethodInvocation;
+import org.springframework.security.vote.AccessDecisionVoter;
+
+public class MethodExpressionVoterTests {
+ private TestingAuthenticationToken joe = new TestingAuthenticationToken("joe", "joespass", "blah");
+ private MethodInvocation miStringArgs;
+ private MethodInvocation miListArg;
+ private List listArg;
+
+ @Before
+ public void setUp() throws Exception {
+ Method m = ExpressionProtectedBusinessServiceImpl.class.getMethod("methodReturningAList",
+ String.class, String.class);
+ miStringArgs = new SimpleMethodInvocation(new Object(), m, new String[] {"joe", "arg2Value"});
+ m = ExpressionProtectedBusinessServiceImpl.class.getMethod("methodReturningAList", List.class);
+ listArg = new ArrayList(Arrays.asList("joe", "bob"));
+ miListArg = new SimpleMethodInvocation(new Object(), m, new Object[] {listArg});
+ }
+
+ @Test
+ public void hasRoleExpressionAllowsUserWithRole() throws Exception {
+ MethodExpressionVoter am = new MethodExpressionVoter();
+ ConfigAttributeDefinition cad = new ConfigAttributeDefinition(new PreInvocationExpressionBasedMethodConfigAttribute(null, null, "hasRole('blah')"));
+
+ assertEquals(AccessDecisionVoter.ACCESS_GRANTED, am.vote(joe, miStringArgs, cad));
+ }
+
+ @Test
+ public void hasRoleExpressionDeniesUserWithoutRole() throws Exception {
+ MethodExpressionVoter am = new MethodExpressionVoter();
+ ConfigAttributeDefinition cad = new ConfigAttributeDefinition(new PreInvocationExpressionBasedMethodConfigAttribute(null, null, "hasRole('joedoesnt')"));
+
+ assertEquals(AccessDecisionVoter.ACCESS_DENIED, am.vote(joe, miStringArgs, cad));
+ }
+
+ @Test
+ public void matchingArgAgainstAuthenticationNameIsSuccessful() throws Exception {
+ MethodExpressionVoter am = new MethodExpressionVoter();
+ ConfigAttributeDefinition cad = new ConfigAttributeDefinition(new PreInvocationExpressionBasedMethodConfigAttribute(null, null, "(#userName == name) and (name == 'joe')"));
+
+ assertEquals(AccessDecisionVoter.ACCESS_GRANTED, am.vote(joe, miStringArgs, cad));
+ }
+
+ @Test
+ public void accessIsGrantedIfNoPreAuthorizeAttributeIsUsed() throws Exception {
+ MethodExpressionVoter am = new MethodExpressionVoter();
+ ConfigAttributeDefinition cad = new ConfigAttributeDefinition(new PreInvocationExpressionBasedMethodConfigAttribute("(name == 'jim')", "someList", null));
+
+ assertEquals(AccessDecisionVoter.ACCESS_GRANTED, am.vote(joe, miListArg, cad));
+ // All objects should have been removed, because the expression is always false
+ assertEquals(0, listArg.size());
+ }
+
+}
diff --git a/core/src/test/java/org/springframework/security/intercept/method/AbstractMethodDefinitionSourceTests.java b/core/src/test/java/org/springframework/security/intercept/method/AbstractMethodDefinitionSourceTests.java
deleted file mode 100644
index f4635d6b14..0000000000
--- a/core/src/test/java/org/springframework/security/intercept/method/AbstractMethodDefinitionSourceTests.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.security.intercept.method;
-
-import junit.framework.TestCase;
-
-import org.springframework.security.util.SimpleMethodInvocation;
-
-import org.aopalliance.intercept.MethodInvocation;
-
-
-/**
- * Tests {@link AbstractMethodDefinitionSource} and associated {@link ConfigAttributeDefinition}.
- *
- * @author Ben Alex
- * @version $Id$
- */
-public class AbstractMethodDefinitionSourceTests extends TestCase {
- //~ Constructors ===================================================================================================
-
- public AbstractMethodDefinitionSourceTests() {
- super();
- }
-
- public AbstractMethodDefinitionSourceTests(String arg0) {
- super(arg0);
- }
-
- //~ Methods ========================================================================================================
-
- public static void main(String[] args) {
- junit.textui.TestRunner.run(AbstractMethodDefinitionSourceTests.class);
- }
-
- public final void setUp() throws Exception {
- super.setUp();
- }
-
- public void testDoesNotSupportAnotherObject() {
- MockMethodDefinitionSource mds = new MockMethodDefinitionSource(false, true);
- assertFalse(mds.supports(String.class));
- }
-
- public void testGetAttributesForANonMethodInvocation() {
- MockMethodDefinitionSource mds = new MockMethodDefinitionSource(false, true);
-
- try {
- mds.getAttributes(new String());
- fail("Should have thrown IllegalArgumentException");
- } catch (IllegalArgumentException expected) {
- assertTrue(true);
- }
- }
-
- public void testGetAttributesForANullObject() {
- MockMethodDefinitionSource mds = new MockMethodDefinitionSource(false, true);
-
- try {
- mds.getAttributes(null);
- fail("Should have thrown IllegalArgumentException");
- } catch (IllegalArgumentException expected) {
- assertTrue(true);
- }
- }
-
- public void testGetAttributesForMethodInvocation() {
- MockMethodDefinitionSource mds = new MockMethodDefinitionSource(false, true);
-
- try {
- mds.getAttributes(new SimpleMethodInvocation());
- fail("Should have thrown UnsupportedOperationException");
- } catch (UnsupportedOperationException expected) {
- assertTrue(true);
- }
- }
-
- public void testSupportsMethodInvocation() {
- MockMethodDefinitionSource mds = new MockMethodDefinitionSource(false, true);
- assertTrue(mds.supports(MethodInvocation.class));
- }
-}
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
index 1465a0b14c..53d32acc1b 100644
--- a/core/src/test/java/org/springframework/security/intercept/method/MapBasedMethodDefinitionSourceTests.java
+++ b/core/src/test/java/org/springframework/security/intercept/method/MapBasedMethodDefinitionSourceTests.java
@@ -3,10 +3,13 @@ package org.springframework.security.intercept.method;
import static org.junit.Assert.*;
import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
import org.junit.Before;
import org.junit.Test;
-import org.springframework.security.ConfigAttributeDefinition;
+import org.springframework.security.ConfigAttribute;
+import org.springframework.security.SecurityConfig;
/**
* Tests for {@link MapBasedMethodDefinitionSource}.
@@ -15,8 +18,8 @@ import org.springframework.security.ConfigAttributeDefinition;
* @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 final List extends ConfigAttribute> ROLE_A = Arrays.asList(new SecurityConfig("ROLE_A"));
+ private final List extends ConfigAttribute> ROLE_B = Arrays.asList(new SecurityConfig("ROLE_B"));
private MapBasedMethodDefinitionSource mds;
private Method someMethodString;
private Method someMethodInteger;
@@ -32,7 +35,7 @@ public class MapBasedMethodDefinitionSourceTests {
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));
+ assertEquals(ROLE_B, mds.getAttributes(someMethodInteger, MockService.class).getConfigAttributes());
}
@Test
@@ -40,8 +43,8 @@ public class MapBasedMethodDefinitionSourceTests {
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));
+ assertEquals(ROLE_A, mds.getAttributes(someMethodInteger, MockService.class).getConfigAttributes());
+ assertEquals(ROLE_B, mds.getAttributes(someMethodString, MockService.class).getConfigAttributes());
}
private class MockService {
diff --git a/core/src/test/java/org/springframework/security/intercept/method/MethodDefinitionAttributesTests.java b/core/src/test/java/org/springframework/security/intercept/method/MethodDefinitionAttributesTests.java
deleted file mode 100644
index e693c20ebd..0000000000
--- a/core/src/test/java/org/springframework/security/intercept/method/MethodDefinitionAttributesTests.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.security.intercept.method;
-
-import java.lang.reflect.Method;
-
-import junit.framework.Assert;
-
-import org.junit.Test;
-import org.springframework.security.ConfigAttributeDefinition;
-import org.springframework.security.ITargetObject;
-
-
-/**
- * Tests {@link MethodDefinitionAttributes}.
- *
- * @author Cameron Braid
- * @author Ben Alex
- * @version $Id$
- */
-public class MethodDefinitionAttributesTests {
-
- private MethodDefinitionAttributes build() {
- MethodDefinitionAttributes mda = new MethodDefinitionAttributes();
- mda.setAttributes(new MockAttributes());
- return mda;
- }
-
- @Test
- public void testMethodsReturned() throws Exception {
- Class clazz = ITargetObject.class;
- Method method = clazz.getMethod("countLength", new Class[] {String.class});
- ConfigAttributeDefinition result = build().findAttributes(method, ITargetObject.class);
- Assert.assertEquals(1, result.getConfigAttributes().size());
- }
-
- @Test
- public void testClassesReturned() throws Exception {
- Class clazz = ITargetObject.class;
- ConfigAttributeDefinition result = build().findAttributes(ITargetObject.class);
- Assert.assertEquals(1, result.getConfigAttributes().size());
- }
-
-}
diff --git a/core/src/test/java/org/springframework/security/intercept/method/MockMethodDefinitionSource.java b/core/src/test/java/org/springframework/security/intercept/method/MockMethodDefinitionSource.java
index 232dabc42a..f90c943d73 100644
--- a/core/src/test/java/org/springframework/security/intercept/method/MockMethodDefinitionSource.java
+++ b/core/src/test/java/org/springframework/security/intercept/method/MockMethodDefinitionSource.java
@@ -15,6 +15,8 @@
package org.springframework.security.intercept.method;
+import org.aopalliance.intercept.MethodInvocation;
+import org.aspectj.lang.JoinPoint;
import org.springframework.security.ConfigAttributeDefinition;
import java.lang.reflect.Method;
@@ -29,7 +31,7 @@ import java.util.Collection;
* @author Ben Alex
* @version $Id$
*/
-public class MockMethodDefinitionSource extends AbstractMethodDefinitionSource {
+public class MockMethodDefinitionSource implements MethodDefinitionSource {
//~ Instance fields ================================================================================================
private List list;
@@ -65,14 +67,19 @@ public class MockMethodDefinitionSource extends AbstractMethodDefinitionSource {
return list;
} else {
return null;
- }
+ }
}
- protected ConfigAttributeDefinition lookupAttributes(Method method) {
+ public ConfigAttributeDefinition getAttributes(Object object) throws IllegalArgumentException {
throw new UnsupportedOperationException("mock method not implemented");
}
- public ConfigAttributeDefinition getAttributes(Method method, Class targetClass) {
+ public ConfigAttributeDefinition getAttributes(Method method, Class targetClass) {
throw new UnsupportedOperationException("mock method not implemented");
- }
+ }
+
+ public boolean supports(Class clazz) {
+ return (MethodInvocation.class.isAssignableFrom(clazz) || JoinPoint.class.isAssignableFrom(clazz));
+ }
+
}
diff --git a/pom.xml b/pom.xml
index 6caa3fda3b..121e910f44 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,6 +17,7 @@
acl
taglibs
itest
+ spring-el
Spring Security
@@ -76,11 +77,16 @@
-
- spring-external
- Spring Portfolio Release Repository
- http://s3.amazonaws.com/maven.springframework.org/external
-
+
+ com.springsource.repository.bundles.release
+ SpringSource Enterprise Bundle Repository - SpringSource Bundle Releases
+ http://repository.springsource.com/maven/bundles/release
+
+
+ com.springsource.repository.bundles.external
+ SpringSource Enterprise Bundle Repository - External Bundle Releases
+ http://repository.springsource.com/maven/bundles/external
+
@@ -634,14 +640,13 @@