Browse Source

SEC-1562: Created SecurityExpressionHandler interface and AbstractSecurityExpressionHandler.

Luke Taylor 15 years ago
parent
commit
af56f4844d

+ 2 - 0
aspects/aspects.gradle

@@ -3,4 +3,6 @@ dependencies {
     compile project(':spring-security-core'),
             "org.springframework:spring-beans:$springVersion",
             "org.springframework:spring-context:$springVersion"
+
+    testCompile 'aopalliance:aopalliance:1.0'
 }

+ 83 - 0
core/src/main/java/org/springframework/security/access/expression/AbstractSecurityExpressionHandler.java

@@ -0,0 +1,83 @@
+package org.springframework.security.access.expression;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
+import org.springframework.security.authentication.AuthenticationTrustResolver;
+import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
+import org.springframework.security.core.Authentication;
+
+/**
+ * Base implementation of the facade which isolates Spring Security's requirements for evaluating security expressions
+ * from the implementation of the underlying expression objects.
+ *
+ * @author Luke Taylor
+ * @since 3.1
+ */
+public abstract class AbstractSecurityExpressionHandler<T> implements SecurityExpressionHandler<T>, ApplicationContextAware {
+    private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
+    private final ExpressionParser expressionParser = new SpelExpressionParser();
+    private final SecurityExpressionRootPropertyAccessor sxrpa = new SecurityExpressionRootPropertyAccessor();
+    private RoleHierarchy roleHierarchy;
+    private ApplicationContext applicationContext;
+
+    public final ExpressionParser getExpressionParser() {
+        return expressionParser;
+    }
+
+    /**
+     * Invokes the internal template methods to create {@code StandardEvaluationContext} and {@code SecurityExpressionRoot}
+     * objects. The root object will be injected with references to the application context, the {@code roleHierarchy}
+     * if set, and an {@code AuthenticationTrustResolver}.
+     *
+     * @param authentication the current authentication object
+     * @param invocation the invocation (filter, method, channel)
+     * @return the context object for use in evaluating the expression, populated with a suitable root object.
+     */
+    public final EvaluationContext createEvaluationContext(Authentication authentication, T invocation) {
+        SecurityExpressionRoot root = createSecurityExpressionRoot(authentication, invocation);
+        root.setTrustResolver(trustResolver);
+        root.setRoleHierarchy(roleHierarchy);
+        root.setApplicationContext(applicationContext);
+        StandardEvaluationContext ctx = createEvaluationContextInternal(authentication, invocation);
+        ctx.addPropertyAccessor(sxrpa);
+        ctx.setRootObject(root);
+
+        return ctx;
+    }
+
+    /**
+     * Override to create a custom instance of {@code StandardEvaluationContext}.
+     * <p>
+     * The returned object will have a {@code SecurityExpressionRootPropertyAccessor} added, allowing beans in
+     * the {@code ApplicationContext} to be accessed via expression properties.
+     *
+     * @param authentication the current authentication object
+     * @param invocation the invocation (filter, method, channel)
+     * @return A {@code StandardEvaluationContext} or potentially a custom subclass if overridden.
+     */
+    protected StandardEvaluationContext createEvaluationContextInternal(Authentication authentication, T invocation) {
+        return new StandardEvaluationContext();
+    }
+
+    /**
+     * Implement in order to create a root object of the correct type for the supported invocation type.
+     *
+     * @param authentication the current authentication object
+     * @param invocation the invocation (filter, method, channel)
+     * @return a
+     */
+    protected abstract SecurityExpressionRoot createSecurityExpressionRoot(Authentication authentication, T invocation);
+
+    public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
+        this.roleHierarchy = roleHierarchy;
+    }
+
+    public void setApplicationContext(ApplicationContext applicationContext) {
+        this.applicationContext = applicationContext;
+    }
+}

+ 25 - 0
core/src/main/java/org/springframework/security/access/expression/SecurityExpressionHandler.java

@@ -0,0 +1,25 @@
+package org.springframework.security.access.expression;
+
+import org.springframework.aop.framework.AopInfrastructureBean;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.security.core.Authentication;
+
+/**
+ * Facade which isolates Spring Security's requirements for evaluating security expressions
+ * from the implementation of the underlying expression objects
+ *
+ * @author Luke Taylor
+ * @since 3.1
+ */
+public interface SecurityExpressionHandler<T> extends AopInfrastructureBean {
+    /**
+     * @return an expression parser for the expressions used by the implementation.
+     */
+    ExpressionParser getExpressionParser();
+
+    /**
+     * Provides an evaluation context in which to evaluate security expressions for the invocation type.
+     */
+    EvaluationContext createEvaluationContext(Authentication authentication, T invocation);
+}

+ 15 - 46
core/src/main/java/org/springframework/security/access/expression/method/DefaultMethodSecurityExpressionHandler.java

@@ -1,71 +1,56 @@
 package org.springframework.security.access.expression.method;
 
 import java.lang.reflect.Array;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
+import java.util.*;
 
 import org.aopalliance.intercept.MethodInvocation;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.springframework.beans.BeansException;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
 import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
 import org.springframework.core.ParameterNameDiscoverer;
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.Expression;
-import org.springframework.expression.ExpressionParser;
-import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
 import org.springframework.security.access.PermissionCacheOptimizer;
 import org.springframework.security.access.PermissionEvaluator;
+import org.springframework.security.access.expression.AbstractSecurityExpressionHandler;
 import org.springframework.security.access.expression.ExpressionUtils;
+import org.springframework.security.access.expression.SecurityExpressionRoot;
 import org.springframework.security.access.expression.SecurityExpressionRootPropertyAccessor;
-import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
-import org.springframework.security.authentication.AuthenticationTrustResolver;
-import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
 import org.springframework.security.core.Authentication;
 
 /**
- * The standard implementation of <tt>SecurityExpressionHandler</tt>.
+ * The standard implementation of {@code MethodSecurityExpressionHandler}.
  * <p>
  * A single instance should usually be shared amongst the beans that require expression support.
  *
  * @author Luke Taylor
  * @since 3.0
  */
-public class DefaultMethodSecurityExpressionHandler implements MethodSecurityExpressionHandler, ApplicationContextAware {
+public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpressionHandler<MethodInvocation> implements MethodSecurityExpressionHandler {
 
     protected final Log logger = LogFactory.getLog(getClass());
 
     private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
     private PermissionEvaluator permissionEvaluator = new DenyAllPermissionEvaluator();
     private PermissionCacheOptimizer permissionCacheOptimizer = null;
-    private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
-    private final SecurityExpressionRootPropertyAccessor sxrpa = new SecurityExpressionRootPropertyAccessor();
-    private final ExpressionParser expressionParser = new SpelExpressionParser();
-    private RoleHierarchy roleHierarchy;
-    private ApplicationContext applicationContext;
 
     public DefaultMethodSecurityExpressionHandler() {
     }
 
     /**
-     * Uses a {@link MethodSecurityEvaluationContext} as the <tt>EvaluationContext</tt> implementation and
-     * configures it with a {@link MethodSecurityExpressionRoot} instance as the expression root object.
+     * Uses a {@link MethodSecurityEvaluationContext} as the <tt>EvaluationContext</tt> implementation.
      */
-    public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {
-        MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(auth, mi, parameterNameDiscoverer);
-        MethodSecurityExpressionRoot root = new MethodSecurityExpressionRoot(auth);
-        root.setTrustResolver(trustResolver);
+    public StandardEvaluationContext createEvaluationContextInternal(Authentication auth, MethodInvocation mi) {
+        return new MethodSecurityEvaluationContext(auth, mi, parameterNameDiscoverer);
+    }
+
+    @Override
+    protected SecurityExpressionRoot createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
+        MethodSecurityExpressionRoot root = new MethodSecurityExpressionRoot(authentication);
         root.setPermissionEvaluator(permissionEvaluator);
-        root.setRoleHierarchy(roleHierarchy);
-        root.setApplicationContext(applicationContext);
-        ctx.setRootObject(root);
-        ctx.addPropertyAccessor(sxrpa);
 
-        return ctx;
+        return root;
     }
 
     /**
@@ -151,10 +136,6 @@ public class DefaultMethodSecurityExpressionHandler implements MethodSecurityExp
         throw new IllegalArgumentException("Filter target must be a collection or array type, but was " + filterTarget);
     }
 
-    public ExpressionParser getExpressionParser() {
-        return expressionParser;
-    }
-
     public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
         this.parameterNameDiscoverer = parameterNameDiscoverer;
     }
@@ -167,19 +148,7 @@ public class DefaultMethodSecurityExpressionHandler implements MethodSecurityExp
         this.permissionCacheOptimizer = permissionCacheOptimizer;
     }
 
-    public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
-        this.trustResolver = trustResolver;
-    }
-
     public void setReturnObject(Object returnObject, EvaluationContext ctx) {
         ((MethodSecurityExpressionRoot)ctx.getRootObject().getValue()).setReturnObject(returnObject);
     }
-
-    public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
-        this.roleHierarchy = roleHierarchy;
-    }
-
-    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
-        this.applicationContext = applicationContext;
-    }
 }

+ 1 - 1
core/src/main/java/org/springframework/security/access/expression/method/ExpressionBasedPreInvocationAdvice.java

@@ -17,7 +17,7 @@ import org.springframework.security.core.Authentication;
  * Method pre-invocation handling based on expressions.
  *
  * @author Luke Taylor
- * @since
+ * @since 3.0
  */
 public class ExpressionBasedPreInvocationAdvice implements PreInvocationAuthorizationAdvice {
     private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();

+ 5 - 18
core/src/main/java/org/springframework/security/access/expression/method/MethodSecurityExpressionHandler.java

@@ -1,30 +1,17 @@
 package org.springframework.security.access.expression.method;
 
 import org.aopalliance.intercept.MethodInvocation;
-import org.springframework.aop.framework.AopInfrastructureBean;
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.Expression;
-import org.springframework.expression.ExpressionParser;
-import org.springframework.security.core.Authentication;
+import org.springframework.security.access.expression.SecurityExpressionHandler;
 
 /**
- * Facade which isolates Spring Security's requirements for evaluation method-security expressions
- * from the implementation of the underlying expression objects.
+ * Extended expression-handler facade which adds methods which are specific to securing method invocations.
  *
  * @author Luke Taylor
  * @since 3.0
  */
-public interface MethodSecurityExpressionHandler extends AopInfrastructureBean {
-    /**
-     * @return an expression parser for the expressions used by the implementation.
-     */
-    ExpressionParser getExpressionParser();
-
-    /**
-     * Provides an evaluation context in which to evaluate security expressions for a method invocation.
-     */
-    EvaluationContext createEvaluationContext(Authentication authentication, MethodInvocation mi);
-
+public interface MethodSecurityExpressionHandler extends SecurityExpressionHandler<MethodInvocation> {
     /**
      * Filters a target collection or array.
      * Only applies to method invocations.
@@ -33,7 +20,7 @@ public interface MethodSecurityExpressionHandler extends AopInfrastructureBean {
      * @param filterExpression the expression which should be used as the filter condition. If it returns false on
      *          evaluation, the object will be removed from the returned collection
      * @param ctx the current evaluation context (as created through a call to
-     *          {@link #createEvaluationContext(Authentication, MethodInvocation)}
+     *            {@link #createEvaluationContext(org.springframework.security.core.Authentication, Object)}
      * @return the filtered collection or array
      */
     Object filter(Object filterTarget, Expression filterExpression, EvaluationContext ctx);
@@ -44,7 +31,7 @@ public interface MethodSecurityExpressionHandler extends AopInfrastructureBean {
      *
      * @param returnObject the return object value
      * @param ctx the context within which the object should be set (as created through a call to
-     *          {@link #createEvaluationContext(Authentication, MethodInvocation)}
+     *            {@link #createEvaluationContext(org.springframework.security.core.Authentication, Object)}
      */
     void setReturnObject(Object returnObject, EvaluationContext ctx);
 

+ 13 - 11
taglibs/src/main/java/org/springframework/security/taglibs/authz/AuthorizeTag.java

@@ -1,8 +1,7 @@
 package org.springframework.security.taglibs.authz;
 
 import java.io.IOException;
-import java.util.Map;
-
+import java.util.*;
 import javax.servlet.FilterChain;
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
@@ -13,14 +12,15 @@ import javax.servlet.jsp.JspException;
 import javax.servlet.jsp.PageContext;
 
 import org.springframework.context.ApplicationContext;
+import org.springframework.core.GenericTypeResolver;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ParseException;
 import org.springframework.security.access.expression.ExpressionUtils;
+import org.springframework.security.access.expression.SecurityExpressionHandler;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.web.FilterInvocation;
 import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
-import org.springframework.security.web.access.expression.WebSecurityExpressionHandler;
 import org.springframework.web.context.support.WebApplicationContextUtils;
 
 /**
@@ -64,8 +64,7 @@ public class AuthorizeTag extends LegacyAuthorizeTag {
     }
 
     private int authorizeUsingAccessExpression(Authentication currentUser) throws JspException {
-        // Get web expression
-        WebSecurityExpressionHandler handler = getExpressionHandler();
+        SecurityExpressionHandler<FilterInvocation> handler = getExpressionHandler();
 
         Expression accessExpression;
         try {
@@ -105,17 +104,20 @@ public class AuthorizeTag extends LegacyAuthorizeTag {
         this.var = var;
     }
 
-    WebSecurityExpressionHandler getExpressionHandler() throws JspException {
+    SecurityExpressionHandler<FilterInvocation> getExpressionHandler() throws JspException {
         ServletContext servletContext = pageContext.getServletContext();
         ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
-        Map<String, WebSecurityExpressionHandler> expressionHdlrs = ctx.getBeansOfType(WebSecurityExpressionHandler.class);
+        Map<String, SecurityExpressionHandler> expressionHdlrs = ctx.getBeansOfType(SecurityExpressionHandler.class);
+
 
-        if (expressionHdlrs.size() == 0) {
-            throw new JspException("No visible WebSecurityExpressionHandler instance could be found in the application " +
-                    "context. There must be at least one in order to support expressions in JSP 'authorize' tags.");
+        for (SecurityExpressionHandler h : expressionHdlrs.values()) {
+            if (FilterInvocation.class.equals(GenericTypeResolver.resolveTypeArgument(h.getClass(), SecurityExpressionHandler.class))) {
+                return h;
+            }
         }
 
-        return (WebSecurityExpressionHandler) expressionHdlrs.values().toArray()[0];
+        throw new JspException("No visible SecurityExpressionHandler<FilterInvocation> instance could be found in the " +
+                "application context. There must be at least one in order to support expressions in JSP 'authorize' tags.");
     }
 
     WebInvocationPrivilegeEvaluator getPrivilegeEvaluator() throws JspException {

+ 1 - 0
taglibs/taglibs.gradle

@@ -5,6 +5,7 @@ dependencies {
             project(':spring-security-web'),
             project(':spring-security-acl'),
             "org.springframework:spring-beans:$springVersion",
+            "org.springframework:spring-aop:$springVersion",
             "org.springframework:spring-context:$springVersion",
             "org.springframework:spring-expression:$springVersion",
             "org.springframework:spring-web:$springVersion"

+ 5 - 41
web/src/main/java/org/springframework/security/web/access/expression/DefaultWebSecurityExpressionHandler.java

@@ -1,55 +1,19 @@
 package org.springframework.security.web.access.expression;
 
-import org.springframework.beans.BeansException;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
-import org.springframework.expression.EvaluationContext;
-import org.springframework.expression.ExpressionParser;
-import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.security.access.expression.AbstractSecurityExpressionHandler;
 import org.springframework.security.access.expression.SecurityExpressionRoot;
-import org.springframework.security.access.expression.SecurityExpressionRootPropertyAccessor;
-import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
-import org.springframework.security.authentication.AuthenticationTrustResolver;
-import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.web.FilterInvocation;
 
 /**
- * Facade which isolates Spring Security's requirements for evaluating web-security expressions
- * from the implementation of the underlying expression objects.
  *
  * @author Luke Taylor
  * @since 3.0
  */
-public class DefaultWebSecurityExpressionHandler implements WebSecurityExpressionHandler, ApplicationContextAware {
+public class DefaultWebSecurityExpressionHandler extends AbstractSecurityExpressionHandler<FilterInvocation> {
 
-    private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
-    private final ExpressionParser expressionParser = new SpelExpressionParser();
-    private final SecurityExpressionRootPropertyAccessor sxrpa = new SecurityExpressionRootPropertyAccessor();
-    private RoleHierarchy roleHierarchy;
-    private ApplicationContext applicationContext;
-
-    public ExpressionParser getExpressionParser() {
-        return expressionParser;
-    }
-
-    public EvaluationContext createEvaluationContext(Authentication authentication, FilterInvocation fi) {
-        SecurityExpressionRoot root = new WebSecurityExpressionRoot(authentication, fi);
-        root.setTrustResolver(trustResolver);
-        root.setRoleHierarchy(roleHierarchy);
-        root.setApplicationContext(applicationContext);
-        StandardEvaluationContext ctx = new StandardEvaluationContext(root);
-        ctx.addPropertyAccessor(sxrpa);
-
-        return ctx;
-    }
-
-    public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
-        this.roleHierarchy = roleHierarchy;
-    }
-
-    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
-        this.applicationContext = applicationContext;
+    @Override
+    protected SecurityExpressionRoot createSecurityExpressionRoot(Authentication authentication, FilterInvocation fi) {
+        return new WebSecurityExpressionRoot(authentication, fi);
     }
 }

+ 4 - 2
web/src/main/java/org/springframework/security/web/access/expression/ExpressionBasedFilterInvocationSecurityMetadataSource.java

@@ -10,12 +10,14 @@ import org.apache.commons.logging.LogFactory;
 import org.springframework.expression.ExpressionParser;
 import org.springframework.expression.ParseException;
 import org.springframework.security.access.ConfigAttribute;
+import org.springframework.security.access.expression.SecurityExpressionHandler;
+import org.springframework.security.web.FilterInvocation;
 import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
 import org.springframework.security.web.util.RequestMatcher;
 import org.springframework.util.Assert;
 
 /**
- * Expression-based <tt>FilterInvocationSecurityMetadataSource</tt>.
+ * Expression-based {@code FilterInvocationSecurityMetadataSource}.
  *
  * @author Luke Taylor
  * @since 3.0
@@ -25,7 +27,7 @@ public final class ExpressionBasedFilterInvocationSecurityMetadataSource extends
 
     public ExpressionBasedFilterInvocationSecurityMetadataSource(
             LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap,
-            WebSecurityExpressionHandler expressionHandler) {
+            SecurityExpressionHandler<FilterInvocation> expressionHandler) {
         super(processMap(requestMap, expressionHandler.getExpressionParser()));
         Assert.notNull(expressionHandler, "A non-null SecurityExpressionHandler is required");
     }

+ 3 - 2
web/src/main/java/org/springframework/security/web/access/expression/WebExpressionVoter.java

@@ -6,6 +6,7 @@ import org.springframework.expression.EvaluationContext;
 import org.springframework.security.access.AccessDecisionVoter;
 import org.springframework.security.access.ConfigAttribute;
 import org.springframework.security.access.expression.ExpressionUtils;
+import org.springframework.security.access.expression.SecurityExpressionHandler;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.web.FilterInvocation;
 
@@ -15,7 +16,7 @@ import org.springframework.security.web.FilterInvocation;
  * @since 3.0
  */
 public class WebExpressionVoter implements AccessDecisionVoter {
-    private WebSecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler();
+    private SecurityExpressionHandler<FilterInvocation> expressionHandler = new DefaultWebSecurityExpressionHandler();
 
     public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
         assert authentication != null;
@@ -52,7 +53,7 @@ public class WebExpressionVoter implements AccessDecisionVoter {
         return clazz.isAssignableFrom(FilterInvocation.class);
     }
 
-    public void setExpressionHandler(WebSecurityExpressionHandler expressionHandler) {
+    public void setExpressionHandler(SecurityExpressionHandler<FilterInvocation> expressionHandler) {
         this.expressionHandler = expressionHandler;
     }
 }

+ 3 - 14
web/src/main/java/org/springframework/security/web/access/expression/WebSecurityExpressionHandler.java

@@ -1,19 +1,8 @@
 package org.springframework.security.web.access.expression;
 
-import org.springframework.expression.EvaluationContext;
-import org.springframework.expression.ExpressionParser;
-import org.springframework.security.core.Authentication;
+import org.springframework.security.access.expression.SecurityExpressionHandler;
 import org.springframework.security.web.FilterInvocation;
 
-public interface WebSecurityExpressionHandler {
-    /**
-     * @return an expression parser for the expressions used by the implementation.
-     */
-    ExpressionParser getExpressionParser();
-
-    /**
-     * Provides an evaluation context in which to evaluate security expressions for a web invocation.
-     */
-    EvaluationContext createEvaluationContext(Authentication authentication, FilterInvocation fi);
-
+@Deprecated
+public interface WebSecurityExpressionHandler extends SecurityExpressionHandler<FilterInvocation> {
 }