1
0
mirror of synced 2026-08-05 17:57:15 +00:00

SEC-1158: Decoupling of Pre/Post annotations implementation from Spring EL.

This commit is contained in:
Luke Taylor
2009-05-11 05:18:20 +00:00
parent acd7dc1f2d
commit 14c4739605
84 changed files with 2489 additions and 2028 deletions
@@ -1,4 +1,4 @@
package org.springframework.security.access.expression;
package org.springframework.security.access;
import java.io.Serializable;
@@ -11,7 +11,7 @@ import org.springframework.security.core.Authentication;
*
* @author Luke Taylor
* @version $Id$
* @since 2.5
* @since 3.0
*/
public interface PermissionEvaluator {
/**
@@ -47,9 +47,9 @@ import java.lang.annotation.Target;
@Inherited
@Documented
public @interface Secured {
/**
* Returns the list of security configuration attributes.
* (i.e. ROLE_USER, ROLE_ADMIN etc.)
/**
* Returns the list of security configuration attributes (e.g. ROLE_USER, ROLE_ADMIN).
*
* @return String[] The secure method attributes
*/
public String[] value();
@@ -33,7 +33,7 @@ import org.springframework.security.access.intercept.method.AbstractFallbackMeth
* @author Ben Alex
* @version $Id$
*/
public class SecuredMethodSecurityMetadataSource extends AbstractFallbackMethodSecurityMetadataSource {
public class SecuredAnnotationSecurityMetadataSource extends AbstractFallbackMethodSecurityMetadataSource {
protected List<ConfigAttribute> findAttributes(Class<?> clazz) {
return processAnnotation(clazz.getAnnotation(Secured.class));
@@ -4,7 +4,7 @@ import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
public class ExpressionUtils {
public final class ExpressionUtils {
public static boolean evaluateAsBoolean(Expression expr, EvaluationContext ctx) {
try {
@@ -1,4 +1,4 @@
package org.springframework.security.access.expression.support;
package org.springframework.security.access.expression;
import java.util.Set;
@@ -13,7 +13,7 @@ import org.springframework.security.core.authority.AuthorityUtils;
*
* @author Luke Taylor
* @version $Id$
* @since 2.5
* @since 3.0
*/
public abstract class SecurityExpressionRoot {
protected final Authentication authentication;
@@ -15,7 +15,7 @@ import org.springframework.util.Assert;
*
* @author Luke Taylor
* @version $Id$
* @since 2.5
* @since 3.0
*/
abstract class AbstractExpressionBasedMethodConfigAttribute implements ConfigAttribute {
private final Expression filterExpression;
@@ -1,4 +1,4 @@
package org.springframework.security.access.expression.support;
package org.springframework.security.access.expression.method;
import java.lang.reflect.Array;
import java.util.ArrayList;
@@ -14,9 +14,9 @@ import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.expression.ExpressionUtils;
import org.springframework.security.access.expression.MethodSecurityExpressionHandler;
import org.springframework.security.access.expression.PermissionEvaluator;
import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
@@ -28,7 +28,7 @@ import org.springframework.security.core.Authentication;
*
* @author Luke Taylor
* @version $Id$
* @since 2.5
* @since 3.0
*/
public class DefaultMethodSecurityExpressionHandler implements MethodSecurityExpressionHandler {
@@ -1,10 +1,10 @@
package org.springframework.security.access.expression.support;
package org.springframework.security.access.expression.method;
import java.io.Serializable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.expression.PermissionEvaluator;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
/**
@@ -13,7 +13,7 @@ import org.springframework.security.core.Authentication;
*
* @author Luke Taylor
* @version $Id$
* @since 2.5
* @since 3.0
*/
class DenyAllPermissionEvaluator implements PermissionEvaluator {
@@ -0,0 +1,58 @@
/**
*
*/
package org.springframework.security.access.expression.method;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParseException;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PostInvocationAttribute;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.access.prepost.PreFilter;
import org.springframework.security.access.prepost.PreInvocationAttribute;
import org.springframework.security.access.prepost.PrePostInvocationAttributeFactory;
/**
* {@link PrePostInvocationAttributeFactory} which interprets the annotation value as
* an expression to be evaluated at runtime.
*
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
public class ExpressionBasedAnnotationAttributeFactory implements PrePostInvocationAttributeFactory {
private ExpressionParser parser;
public ExpressionBasedAnnotationAttributeFactory(MethodSecurityExpressionHandler handler) {
parser = handler.getExpressionParser();
}
public PreInvocationAttribute createPreInvocationAttribute(PreFilter preFilter, PreAuthorize preAuthorize) {
try {
// TODO: Optimization of permitAll
Expression preAuthorizeExpression = preAuthorize == null ? parser.parseExpression("permitAll") : parser.parseExpression(preAuthorize.value());
Expression preFilterExpression = preFilter == null ? null : parser.parseExpression(preFilter.value());
String filterObject = preFilter == null ? null : preFilter.filterTarget();
return new PreInvocationExpressionAttribute(preFilterExpression, filterObject, preAuthorizeExpression);
} catch (ParseException e) {
throw new IllegalArgumentException("Failed to parse expression '" + e.getExpressionString() + "'", e);
}
}
public PostInvocationAttribute createPostInvocationAttribute(PostFilter postFilter, PostAuthorize postAuthorize) {
try {
Expression postAuthorizeExpression = postAuthorize == null ? null : parser.parseExpression(postAuthorize.value());
Expression postFilterExpression = postFilter == null ? null : parser.parseExpression(postFilter.value());
if (postFilterExpression != null || postAuthorizeExpression != null) {
return new PostInvocationExpressionAttribute(postFilterExpression, postAuthorizeExpression);
}
} catch (ParseException e) {
throw new IllegalArgumentException("Failed to parse expression '" + e.getExpressionString() + "'", e);
}
return null;
}
}
@@ -0,0 +1,61 @@
package org.springframework.security.access.expression.method;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.expression.ExpressionUtils;
import org.springframework.security.access.prepost.PostInvocationAttribute;
import org.springframework.security.access.prepost.PostInvocationAuthorizationAdvice;
import org.springframework.security.core.Authentication;
/**
*
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
public class ExpressionBasedPostInvocationAdvice implements PostInvocationAuthorizationAdvice{
protected final Log logger = LogFactory.getLog(getClass());
private MethodSecurityExpressionHandler expressionHandler;
public ExpressionBasedPostInvocationAdvice(MethodSecurityExpressionHandler expressionHandler) {
this.expressionHandler = expressionHandler;
}
public Object after(Authentication authentication, MethodInvocation mi,
PostInvocationAttribute postAttr, Object returnedObject) throws AccessDeniedException{
PostInvocationExpressionAttribute pia = (PostInvocationExpressionAttribute) postAttr;
EvaluationContext ctx = expressionHandler.createEvaluationContext(authentication, mi);
Expression postFilter = pia.getFilterExpression();
Expression postAuthorize = pia.getAuthorizeExpression();
if (postFilter != null) {
if (logger.isDebugEnabled()) {
logger.debug("Applying PostFilter expression " + postFilter);
}
if (returnedObject != null) {
returnedObject = expressionHandler.filter(returnedObject, postFilter, ctx);
} else {
if (logger.isDebugEnabled()) {
logger.debug("Return object is null, filtering will be skipped");
}
}
}
expressionHandler.setReturnObject(returnedObject, ctx);
if (postAuthorize != null && !ExpressionUtils.evaluateAsBoolean(postAuthorize, ctx)) {
if (logger.isDebugEnabled()) {
logger.debug("PostAuthorize expression rejected access");
}
throw new AccessDeniedException("Access is denied");
}
return returnedObject;
}
}
@@ -1,68 +1,47 @@
/**
*
*/
package org.springframework.security.access.expression.method;
import java.util.Collection;
import java.util.List;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.expression.ExpressionUtils;
import org.springframework.security.access.expression.MethodSecurityExpressionHandler;
import org.springframework.security.access.expression.support.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.vote.AccessDecisionVoter;
import org.springframework.security.access.prepost.PreInvocationAttribute;
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdvice;
import org.springframework.security.core.Authentication;
/**
* Voter which performs the actions for @PreFilter and @PostAuthorize annotations.
* <p>
* 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',
* to grant or deny access depending on whether the @PreAuthorize expression evaluates to 'true' or 'false',
* respectively.
*
* @author Luke Taylor
* @version $Id$
* @since 2.5
* @since
*/
public class MethodExpressionVoter implements AccessDecisionVoter {
protected final Log logger = LogFactory.getLog(getClass());
public class ExpressionBasedPreInvocationAdvice implements PreInvocationAuthorizationAdvice {
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
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, List<ConfigAttribute> attributes) {
PreInvocationExpressionAttribute mace = findMethodAccessControlExpression(attributes);
if (mace == null) {
// No expression based metadata, so abstain
return ACCESS_ABSTAIN;
}
MethodInvocation mi = (MethodInvocation)object;
public boolean before(Authentication authentication, MethodInvocation mi, PreInvocationAttribute attr) {
PreInvocationExpressionAttribute preAttr = (PreInvocationExpressionAttribute) attr;
EvaluationContext ctx = expressionHandler.createEvaluationContext(authentication, mi);
Expression preFilter = mace.getFilterExpression();
Expression preAuthorize = mace.getAuthorizeExpression();
Expression preFilter = preAttr.getFilterExpression();
Expression preAuthorize = preAttr.getAuthorizeExpression();
if (preFilter != null) {
Object filterTarget = findFilterTarget(mace.getFilterTarget(), ctx, mi);
Object filterTarget = findFilterTarget(preAttr.getFilterTarget(), ctx, mi);
expressionHandler.filter(filterTarget, preFilter, ctx);
}
if (preAuthorize == null) {
return ACCESS_GRANTED;
return true;
}
return ExpressionUtils.evaluateAsBoolean(preAuthorize, ctx) ? ACCESS_GRANTED : ACCESS_DENIED;
return ExpressionUtils.evaluateAsBoolean(preAuthorize, ctx);
}
private Object findFilterTarget(String filterTargetName, EvaluationContext ctx, MethodInvocation mi) {
@@ -94,16 +73,6 @@ public class MethodExpressionVoter implements AccessDecisionVoter {
return filterTarget;
}
private PreInvocationExpressionAttribute findMethodAccessControlExpression(List<ConfigAttribute> config) {
// Find the MethodAccessControlExpression attribute
for (ConfigAttribute attribute : config) {
if (attribute instanceof PreInvocationExpressionAttribute) {
return (PreInvocationExpressionAttribute)attribute;
}
}
return null;
}
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
this.expressionHandler = expressionHandler;
@@ -1,96 +0,0 @@
package org.springframework.security.access.expression.method;
import java.util.List;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.expression.ExpressionUtils;
import org.springframework.security.access.expression.MethodSecurityExpressionHandler;
import org.springframework.security.access.expression.support.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.intercept.AfterInvocationProvider;
import org.springframework.security.core.Authentication;
/**
* 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 MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
public Object decide(Authentication authentication, Object object, List<ConfigAttribute> config, Object returnedObject)
throws AccessDeniedException {
PostInvocationExpressionAttribute mca = findMethodAccessControlExpression(config);
if (mca == null) {
return returnedObject;
}
EvaluationContext ctx =
expressionHandler.createEvaluationContext(authentication, (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 = expressionHandler.filter(returnedObject, postFilter, ctx);
} else {
if (logger.isDebugEnabled()) {
logger.debug("Return object is null, filtering will be skipped");
}
}
}
expressionHandler.setReturnObject(returnedObject, ctx);
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 PostInvocationExpressionAttribute findMethodAccessControlExpression(List<ConfigAttribute> config) {
// Find the MethodAccessControlExpression attribute
for (ConfigAttribute attribute : config) {
if (attribute instanceof PostInvocationExpressionAttribute) {
return (PostInvocationExpressionAttribute)attribute;
}
}
return null;
}
public boolean supports(ConfigAttribute attribute) {
return attribute instanceof PostInvocationExpressionAttribute;
}
public boolean supports(Class<?> clazz) {
return clazz.isAssignableFrom(MethodInvocation.class);
}
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
this.expressionHandler = expressionHandler;
}
}
@@ -1,4 +1,4 @@
package org.springframework.security.access.expression.support;
package org.springframework.security.access.expression.method;
import java.lang.reflect.Method;
@@ -15,7 +15,7 @@ import org.springframework.util.ClassUtils;
* and when they are required.
*
* @author Luke Taylor
* @since 2.5
* @since 3.0
*/
class MethodSecurityEvaluationContext extends StandardEvaluationContext {
private ParameterNameDiscoverer parameterNameDiscoverer;
@@ -1,4 +1,4 @@
package org.springframework.security.access.expression;
package org.springframework.security.access.expression.method;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.expression.EvaluationContext;
@@ -12,7 +12,7 @@ import org.springframework.security.core.Authentication;
*
* @author Luke Taylor
* @version $Id$
* @since 2.5
* @since 3.0
*/
public interface MethodSecurityExpressionHandler {
/**
@@ -1,8 +1,9 @@
package org.springframework.security.access.expression.support;
package org.springframework.security.access.expression.method;
import java.io.Serializable;
import org.springframework.security.access.expression.PermissionEvaluator;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.core.Authentication;
@@ -11,7 +12,7 @@ import org.springframework.security.core.Authentication;
*
* @author Luke Taylor
* @version $Id$
* @since 2.5
* @since 3.0
*/
class MethodSecurityExpressionRoot extends SecurityExpressionRoot {
private PermissionEvaluator permissionEvaluator;
@@ -2,8 +2,16 @@ package org.springframework.security.access.expression.method;
import org.springframework.expression.Expression;
import org.springframework.expression.ParseException;
import org.springframework.security.access.prepost.PostInvocationAttribute;
class PostInvocationExpressionAttribute extends AbstractExpressionBasedMethodConfigAttribute {
/**
*
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
class PostInvocationExpressionAttribute extends AbstractExpressionBasedMethodConfigAttribute
implements PostInvocationAttribute {
PostInvocationExpressionAttribute(String filterExpression, String authorizeExpression)
throws ParseException {
@@ -2,8 +2,17 @@ package org.springframework.security.access.expression.method;
import org.springframework.expression.Expression;
import org.springframework.expression.ParseException;
import org.springframework.security.access.prepost.PreInvocationAttribute;
/**
*
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
class PreInvocationExpressionAttribute extends AbstractExpressionBasedMethodConfigAttribute
implements PreInvocationAttribute {
class PreInvocationExpressionAttribute extends AbstractExpressionBasedMethodConfigAttribute {
private final String filterTarget;
PreInvocationExpressionAttribute(String filterExpression, String filterTarget, String authorizeExpression)
@@ -0,0 +1,2 @@
Expression handling code to support the use of Spring-EL based expressions in @PreAuthorize, @PreFilter,
@PostAuthorizue and @PostFilter annotations. Mainly for internal framework use and liable to change.
@@ -1,5 +0,0 @@
package org.springframework.security.access.expression.support;
public class AbstractSecurityExpressionHandler {
}
@@ -1,4 +1,4 @@
package org.springframework.security.access.expression.annotation;
package org.springframework.security.access.prepost;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
@@ -12,7 +12,7 @@ import java.lang.annotation.Target;
*
* @author Luke Taylor
* @version $Id$
* @since 2.5
* @since 3.0
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@@ -1,4 +1,4 @@
package org.springframework.security.access.expression.annotation;
package org.springframework.security.access.prepost;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
@@ -12,7 +12,7 @@ import java.lang.annotation.Target;
*
* @author Luke Taylor
* @version $Id$
* @since 2.5
* @since 3.0
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@@ -0,0 +1,59 @@
package org.springframework.security.access.prepost;
import java.util.List;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.intercept.AfterInvocationProvider;
import org.springframework.security.core.Authentication;
/**
* <tt>AfterInvocationProvider</tt> which delegates to a {@link PostInvocationAuthorizationAdvice} instance
* passing it the <tt>PostInvocationAttribute</tt> created from @PostAuthorize and @PostFilter annotations.
*
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
public class PostInvocationAdviceProvider implements AfterInvocationProvider {
protected final Log logger = LogFactory.getLog(getClass());
private PostInvocationAuthorizationAdvice postAdvice;
public PostInvocationAdviceProvider(PostInvocationAuthorizationAdvice postAdvice) {
this.postAdvice = postAdvice;
}
public Object decide(Authentication authentication, Object object, List<ConfigAttribute> config, Object returnedObject)
throws AccessDeniedException {
PostInvocationAttribute pia = findPostInvocationAttribute(config);
if (pia == null) {
return returnedObject;
}
return postAdvice.after(authentication, (MethodInvocation)object, pia, returnedObject);
}
private PostInvocationAttribute findPostInvocationAttribute(List<ConfigAttribute> config) {
for (ConfigAttribute attribute : config) {
if (attribute instanceof PostInvocationAttribute) {
return (PostInvocationAttribute)attribute;
}
}
return null;
}
public boolean supports(ConfigAttribute attribute) {
return attribute instanceof PostInvocationAttribute;
}
public boolean supports(Class<?> clazz) {
return clazz.isAssignableFrom(MethodInvocation.class);
}
}
@@ -0,0 +1,16 @@
package org.springframework.security.access.prepost;
import org.springframework.security.access.ConfigAttribute;
/**
* Marker interface for attributes which are created from combined @PostFilter and @PostAuthorize annotations.
* <p>
* Consumed by a {@link PostInvocationAuthorizationAdvice}.
*
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
public interface PostInvocationAttribute extends ConfigAttribute{
}
@@ -0,0 +1,18 @@
package org.springframework.security.access.prepost;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
/**
* Performs filtering and authorization logic after a method is invoked.
*
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
public interface PostInvocationAuthorizationAdvice {
Object after(Authentication authentication, MethodInvocation mi,
PostInvocationAttribute pia, Object returnedObject) throws AccessDeniedException;
}
@@ -1,4 +1,4 @@
package org.springframework.security.access.expression.annotation;
package org.springframework.security.access.prepost;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
@@ -13,7 +13,7 @@ import java.lang.annotation.Target;
*
* @author Luke Taylor
* @version $Id$
* @since 2.5
* @since 3.0
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@@ -1,4 +1,4 @@
package org.springframework.security.access.expression.annotation;
package org.springframework.security.access.prepost;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
@@ -23,7 +23,7 @@ import java.lang.annotation.Target;
*
* @author Luke Taylor
* @version $Id$
* @since 2.5
* @since 3.0
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@@ -0,0 +1,16 @@
package org.springframework.security.access.prepost;
import org.springframework.security.access.ConfigAttribute;
/**
* Marker interface for attributes which are created from combined @PreFilter and @PreAuthorize annotations.
* <p>
* Consumed by a {@link PreInvocationAuthorizationAdvice}.
*
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
public interface PreInvocationAttribute extends ConfigAttribute{
}
@@ -0,0 +1,16 @@
package org.springframework.security.access.prepost;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.security.core.Authentication;
/**
* Performs argument filtering and authorization logic before a method is invoked.
*
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
public interface PreInvocationAuthorizationAdvice {
boolean before(Authentication authentication, MethodInvocation mi, PreInvocationAttribute preInvocationAttribute);
}
@@ -0,0 +1,69 @@
package org.springframework.security.access.prepost;
import java.util.List;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.vote.AccessDecisionVoter;
import org.springframework.security.core.Authentication;
/**
* Voter which performs the actions using a PreInvocationAuthorizationAdvice implementation
* generated from @PreFilter and @PreAuthorize annotations.
* <p>
* In practice, if these annotations are being used, they will normally contain all the necessary
* access control logic, so a voter-based system is not really necessary and a single <tt>AccessDecisionManager</tt>
* which contained the same logic would suffice. However, this class fits in readily with the traditional
* voter-based <tt>AccessDecisionManager</tt> implementations used by Spring Security.
*
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
public class PreInvocationAuthorizationAdviceVoter implements AccessDecisionVoter {
protected final Log logger = LogFactory.getLog(getClass());
private PreInvocationAuthorizationAdvice preAdvice;
public PreInvocationAuthorizationAdviceVoter(PreInvocationAuthorizationAdvice pre) {
this.preAdvice = pre;
}
public boolean supports(ConfigAttribute attribute) {
return attribute instanceof PreInvocationAuthorizationAdvice;
}
public boolean supports(Class<?> clazz) {
return clazz.isAssignableFrom(MethodInvocation.class);
}
public int vote(Authentication authentication, Object object, List<ConfigAttribute> attributes) {
// Find prefilter and preauth (or combined) attributes
// if both null, abstain
// else call advice with them
PreInvocationAttribute preAttr = findPreInvocationAttribute(attributes);
if (preAttr == null) {
// No expression based metadata, so abstain
return ACCESS_ABSTAIN;
}
boolean allowed = preAdvice.before(authentication, (MethodInvocation)object, preAttr);
return allowed ? ACCESS_GRANTED : ACCESS_DENIED;
}
private PreInvocationAttribute findPreInvocationAttribute(List<ConfigAttribute> config) {
for (ConfigAttribute attribute : config) {
if (attribute instanceof PreInvocationAttribute) {
return (PreInvocationAttribute)attribute;
}
}
return null;
}
}
@@ -1,4 +1,4 @@
package org.springframework.security.access.expression.method;
package org.springframework.security.access.prepost;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
@@ -7,22 +7,15 @@ import java.util.Collection;
import java.util.List;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParseException;
import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.expression.MethodSecurityExpressionHandler;
import org.springframework.security.access.expression.annotation.PostAuthorize;
import org.springframework.security.access.expression.annotation.PostFilter;
import org.springframework.security.access.expression.annotation.PreAuthorize;
import org.springframework.security.access.expression.annotation.PreFilter;
import org.springframework.security.access.intercept.method.AbstractMethodSecurityMetadataSource;
import org.springframework.util.ClassUtils;
/**
* <tt>MethodSecurityMetadataSource</tt> which extracts metadata from the @PreFilter and @PreAuthorize annotations
* placed on a method. The metadata is encapsulated in a {@link AbstractExpressionBasedMethodConfigAttribute} instance.
* placed on a method. This class is merely responsible for locating the relevant annotations (if any). It delegates
* the actual <tt>ConfigAttribute</tt> creation to its {@link PrePostInvocationAttributeFactory}, thus
* decoupling itself from the mechanism which will enforce the annotations' behaviour.
* <p>
* Annotations may be specified on classes or methods, and method-specific annotations will take precedence.
* If you use any annotation and do not specify a pre-authorization condition, then the method will be
@@ -31,25 +24,18 @@ import org.springframework.util.ClassUtils;
* Since we are handling multiple annotations here, it's possible that we may have to combine annotations defined in
* multiple locations for a single method - they may be defined on the method itself, or at interface or class level.
*
* @see MethodExpressionVoter
* @see PreInvocationAuthorizationAdviceVoter
*
* @author Luke Taylor
* @since 2.5
* @since 3.0
* @version $Id$
*/
public class ExpressionAnnotationMethodSecurityMetadataSource extends AbstractMethodSecurityMetadataSource {
private ExpressionParser parser;
public class PrePostAnnotationSecurityMetadataSource extends AbstractMethodSecurityMetadataSource {
public ExpressionAnnotationMethodSecurityMetadataSource() {
parser = new SpelAntlrExpressionParser();
}
private final PrePostInvocationAttributeFactory attributeFactory;
/**
* Constructor which obtains the expression parser from the {@link MethodSecurityExpressionHandler#getExpressionParser() }
* method on the supplied <tt>SecurityExpressionHandler</tt>.
*/
public ExpressionAnnotationMethodSecurityMetadataSource(MethodSecurityExpressionHandler handler) {
parser = handler.getExpressionParser();
public PrePostAnnotationSecurityMetadataSource(PrePostInvocationAttributeFactory attributeFactory) {
this.attributeFactory = attributeFactory;
}
public List<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
@@ -57,7 +43,7 @@ public class ExpressionAnnotationMethodSecurityMetadataSource extends AbstractMe
return null;
}
logger.trace("Looking for expression annotations for method '" +
logger.trace("Looking for Pre/Post annotations for method '" +
method.getName() + "' on target class '" + targetClass + "'");
PreFilter preFilter = findAnnotation(method, targetClass, PreFilter.class);
PreAuthorize preAuthorize = findAnnotation(method, targetClass, PreAuthorize.class);
@@ -71,7 +57,27 @@ public class ExpressionAnnotationMethodSecurityMetadataSource extends AbstractMe
return null;
}
return createAttributeList(preFilter, preAuthorize, postFilter, postAuthorize);
ArrayList<ConfigAttribute> attrs = new ArrayList<ConfigAttribute>();
PreInvocationAttribute pre = attributeFactory.createPreInvocationAttribute(preFilter, preAuthorize);
if (pre != null) {
attrs.add(pre);
}
PostInvocationAttribute post = attributeFactory.createPostInvocationAttribute(postFilter, postAuthorize);
if (post != null) {
attrs.add(post);
}
attrs.trimToSize();
return attrs.isEmpty() ? null : attrs;
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
/**
@@ -121,40 +127,4 @@ public class ExpressionAnnotationMethodSecurityMetadataSource extends AbstractMe
return null;
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
private List<ConfigAttribute> createAttributeList(PreFilter preFilter, PreAuthorize preAuthorize,
PostFilter postFilter, PostAuthorize postAuthorize) {
ConfigAttribute pre = null;
ConfigAttribute post = null;
// TODO: Optimization of permitAll
try {
Expression preAuthorizeExpression = preAuthorize == null ? parser.parseExpression("permitAll") : parser.parseExpression(preAuthorize.value());
Expression preFilterExpression = preFilter == null ? null : parser.parseExpression(preFilter.value());
String filterObject = preFilter == null ? null : preFilter.filterTarget();
Expression postAuthorizeExpression = postAuthorize == null ? null : parser.parseExpression(postAuthorize.value());
Expression postFilterExpression = postFilter == null ? null : parser.parseExpression(postFilter.value());
pre = new PreInvocationExpressionAttribute(preFilterExpression, filterObject, preAuthorizeExpression);
if (postFilterExpression != null || postAuthorizeExpression != null) {
post = new PostInvocationExpressionAttribute(postFilterExpression, postAuthorizeExpression);
}
} catch (ParseException e) {
throw new IllegalArgumentException("Failed to parse expression '" + e.getExpressionString() + "'", e);
}
List<ConfigAttribute> attrs = new ArrayList<ConfigAttribute>(2);
if (pre != null) {
attrs.add(pre);
}
if (post != null) {
attrs.add(post);
}
return attrs;
}
}
@@ -0,0 +1,14 @@
package org.springframework.security.access.prepost;
/**
*
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
public interface PrePostInvocationAttributeFactory {
PreInvocationAttribute createPreInvocationAttribute(PreFilter preFilter, PreAuthorize preAuthorize);
PostInvocationAttribute createPostInvocationAttribute(PostFilter postFilter, PostAuthorize postAuthorize);
}
@@ -0,0 +1,6 @@
<p>
Contains the infrastructure classes for handling the @PreAuthorize, @PreFilter, @PostAuthorize and
@PostFilter annotations.
</p>
Other than the annotations themselves, the classes should be regarded as for internal framework use and
are liable to change without notice.
@@ -13,11 +13,10 @@
* limitations under the License.
*/
package org.springframework.security.authentication.dao.salt;
package org.springframework.security.authentication.dao;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.dao.SaltSource;
import org.springframework.security.core.userdetails.UserDetails;
@@ -13,9 +13,8 @@
* limitations under the License.
*/
package org.springframework.security.authentication.dao.salt;
package org.springframework.security.authentication.dao;
import org.springframework.security.authentication.dao.SaltSource;
import org.springframework.security.core.userdetails.UserDetails;
@@ -1,5 +0,0 @@
<html>
<body>
Implementations that provide salts for more secure password encoding.
</body>
</html>
@@ -8,7 +8,7 @@ import org.springframework.context.ApplicationEvent;
*
* @author Luke Taylor
* @version $Id$
* @since 2.5
* @since 3.0
*/
public abstract class SessionCreationEvent extends ApplicationEvent {
@@ -9,7 +9,7 @@ import org.springframework.security.core.context.SecurityContext;
*
* @author Luke Taylor
* @version $Id$
* @since 2.5
* @since 3.0
*/
public abstract class SessionDestroyedEvent extends ApplicationEvent {
@@ -21,7 +21,7 @@ import javax.annotation.security.RolesAllowed;
import javax.annotation.security.PermitAll;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.expression.annotation.PreAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
/**
* @version $Id$
@@ -3,8 +3,9 @@ package org.springframework.security.access.annotation;
import java.util.ArrayList;
import java.util.List;
import org.springframework.security.access.expression.annotation.PostFilter;
import org.springframework.security.access.expression.annotation.PreFilter;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreFilter;
public class ExpressionProtectedBusinessServiceImpl implements BusinessService {
@@ -6,6 +6,7 @@ import java.util.List;
import javax.annotation.security.RolesAllowed;
import javax.annotation.security.PermitAll;
/**
*
* @author Luke Taylor
@@ -23,23 +23,23 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.annotation.SecuredMethodSecurityMetadataSource;
import org.springframework.security.access.annotation.SecuredAnnotationSecurityMetadataSource;
import org.springframework.util.StringUtils;
/**
* Tests for {@link org.springframework.security.access.annotation.SecuredMethodSecurityMetadataSource}
* Tests for {@link org.springframework.security.access.annotation.SecuredAnnotationSecurityMetadataSource}
*
* @author Mark St.Godard
* @author Joe Scalise
* @author Ben Alex
* @version $Id$
*/
public class SecuredMethodDefinitionSourceTests extends TestCase {
public class SecuredAnnotationSecurityMetadataDefinitionSourceTests extends TestCase {
//~ Instance fields ================================================================================================
private SecuredMethodSecurityMetadataSource mds = new SecuredMethodSecurityMetadataSource();;
private Log logger = LogFactory.getLog(SecuredMethodDefinitionSourceTests.class);
private SecuredAnnotationSecurityMetadataSource mds = new SecuredAnnotationSecurityMetadataSource();;
private Log logger = LogFactory.getLog(SecuredAnnotationSecurityMetadataDefinitionSourceTests.class);
//~ Methods ========================================================================================================
@@ -11,8 +11,8 @@ import java.util.List;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.Test;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.expression.method.MethodExpressionVoter;
import org.springframework.security.access.expression.method.PreInvocationExpressionAttribute;
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter;
import org.springframework.security.access.vote.AccessDecisionVoter;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.util.SimpleMethodInvocation;
@@ -20,7 +20,8 @@ import org.springframework.security.util.SimpleMethodInvocation;
@SuppressWarnings("unchecked")
public class MethodExpressionVoterTests {
private TestingAuthenticationToken joe = new TestingAuthenticationToken("joe", "joespass", "blah");
private MethodExpressionVoter am = new MethodExpressionVoter();
private PreInvocationAuthorizationAdviceVoter am =
new PreInvocationAuthorizationAdviceVoter(new ExpressionBasedPreInvocationAdvice());
@Test
public void hasRoleExpressionAllowsUserWithRole() throws Exception {
@@ -1,4 +1,4 @@
package org.springframework.security.access.expression.support;
package org.springframework.security.access.expression.method;
import static org.junit.Assert.*;
@@ -9,9 +9,9 @@ import org.junit.Test;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.expression.ExpressionUtils;
import org.springframework.security.access.expression.PermissionEvaluator;
import org.springframework.security.access.expression.support.MethodSecurityExpressionRoot;
import org.springframework.security.access.expression.method.MethodSecurityExpressionRoot;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.core.Authentication;
@@ -7,18 +7,22 @@ import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.expression.annotation.PostAuthorize;
import org.springframework.security.access.expression.annotation.PostFilter;
import org.springframework.security.access.expression.annotation.PreAuthorize;
import org.springframework.security.access.expression.annotation.PreFilter;
import org.springframework.security.access.expression.method.ExpressionAnnotationMethodSecurityMetadataSource;
import org.springframework.security.access.expression.method.PostInvocationExpressionAttribute;
import org.springframework.security.access.expression.method.PreInvocationExpressionAttribute;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.access.prepost.PreFilter;
import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource;
public class ExpressionAnnotationMethodDefinitionSourceTests {
private ExpressionAnnotationMethodSecurityMetadataSource mds = new ExpressionAnnotationMethodSecurityMetadataSource();
/**
*
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
public class PrePostAnnotationSecurityMetadataSourceTests {
private PrePostAnnotationSecurityMetadataSource mds =
new PrePostAnnotationSecurityMetadataSource(new ExpressionBasedAnnotationAttributeFactory(new DefaultMethodSecurityExpressionHandler()));
private MockMethodInvocation voidImpl1;
private MockMethodInvocation voidImpl2;
@@ -29,7 +29,6 @@ import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.authentication.dao.salt.SystemWideSaltSource;
import org.springframework.security.authentication.encoding.ShaPasswordEncoder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
@@ -19,7 +19,7 @@ import static junit.framework.Assert.assertEquals;
import org.junit.Test;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.dao.salt.ReflectionSaltSource;
import org.springframework.security.authentication.dao.ReflectionSaltSource;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
@@ -15,7 +15,7 @@
package org.springframework.security.authentication.dao.salt;
import org.springframework.security.authentication.dao.salt.SystemWideSaltSource;
import org.springframework.security.authentication.dao.SystemWideSaltSource;
import junit.framework.TestCase;
+9
View File
@@ -0,0 +1,9 @@
print authentication.name;
for authority in authentication.authorities:
print authority
print "Granting access"
allow = 1