1
0
mirror of synced 2026-08-05 01:36:56 +00:00

SEC-1294: Enable access to beans from ApplicationContext in EL expressions.

ExpressionHandlers are now ApplicationContextAware and set the app context on the SecurityExpressionRoot. A custom PropertyAccessor resolves the properties against the root by looking them up in the app context.
This commit is contained in:
Luke Taylor
2010-03-31 22:37:22 +01:00
parent 12a6ae2ffa
commit 0521d10069
12 changed files with 170 additions and 14 deletions
@@ -4,6 +4,7 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.springframework.context.ApplicationContext;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.core.Authentication;
@@ -22,6 +23,7 @@ public abstract class SecurityExpressionRoot {
private AuthenticationTrustResolver trustResolver;
private RoleHierarchy roleHierarchy;
private Set<String> roles;
private ApplicationContext applicationContext;
/** Allows "permitAll" expression */
public final boolean permitAll = true;
@@ -92,6 +94,14 @@ public abstract class SecurityExpressionRoot {
this.roleHierarchy = roleHierarchy;
}
ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
private Set<String> getAuthoritySet() {
if (roles == null) {
roles = new HashSet<String>();
@@ -0,0 +1,38 @@
package org.springframework.security.access.expression;
import org.springframework.context.ApplicationContext;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
@SuppressWarnings("unchecked")
public final class SecurityExpressionRootPropertyAccessor implements PropertyAccessor {
public Class[] CLASSES = {SecurityExpressionRoot.class};
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
ApplicationContext ctx = ((SecurityExpressionRoot)target).getApplicationContext();
if (ctx == null) {
return false;
}
return ctx.containsBean(name);
}
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
return new TypedValue(((SecurityExpressionRoot)target).getApplicationContext().getBean(name));
}
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
return false;
}
public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException {
}
public Class[] getSpecificTargetClasses() {
return CLASSES;
}
}
@@ -9,6 +9,9 @@ import java.util.List;
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;
@@ -18,6 +21,7 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.security.access.PermissionCacheOptimizer;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.expression.ExpressionUtils;
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;
@@ -31,7 +35,7 @@ import org.springframework.security.core.Authentication;
* @author Luke Taylor
* @since 3.0
*/
public class DefaultMethodSecurityExpressionHandler implements MethodSecurityExpressionHandler {
public class DefaultMethodSecurityExpressionHandler implements MethodSecurityExpressionHandler, ApplicationContextAware {
protected final Log logger = LogFactory.getLog(getClass());
@@ -39,8 +43,10 @@ public class DefaultMethodSecurityExpressionHandler implements MethodSecurityExp
private PermissionEvaluator permissionEvaluator = new DenyAllPermissionEvaluator();
private PermissionCacheOptimizer permissionCacheOptimizer = null;
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
private final SecurityExpressionRootPropertyAccessor sxrpa = new SecurityExpressionRootPropertyAccessor();
private ExpressionParser expressionParser = new SpelExpressionParser();
private RoleHierarchy roleHierarchy;
private ApplicationContext applicationContext;
public DefaultMethodSecurityExpressionHandler() {
}
@@ -55,7 +61,9 @@ public class DefaultMethodSecurityExpressionHandler implements MethodSecurityExp
root.setTrustResolver(trustResolver);
root.setPermissionEvaluator(permissionEvaluator);
root.setRoleHierarchy(roleHierarchy);
root.setApplicationContext(applicationContext);
ctx.setRootObject(root);
ctx.addPropertyAccessor(sxrpa);
return ctx;
}
@@ -170,4 +178,8 @@ public class DefaultMethodSecurityExpressionHandler implements MethodSecurityExp
public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
this.roleHierarchy = roleHierarchy;
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
@@ -6,6 +6,8 @@ import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.spel.support.StandardEvaluationContext;
@@ -23,8 +25,9 @@ class MethodSecurityEvaluationContext extends StandardEvaluationContext {
private static Log logger = LogFactory.getLog(MethodSecurityEvaluationContext.class);
private ParameterNameDiscoverer parameterNameDiscoverer;
private final MethodInvocation mi;
private ApplicationContext appContext;
private boolean argumentsAdded;
private MethodInvocation mi;
/**
* Intended for testing. Don't use in practice as it creates a new parameter resolver
@@ -44,6 +47,7 @@ class MethodSecurityEvaluationContext extends StandardEvaluationContext {
@Override
public Object lookupVariable(String name) {
Object variable = super.lookupVariable(name);
if (variable != null) {
return variable;
}
@@ -53,7 +57,23 @@ class MethodSecurityEvaluationContext extends StandardEvaluationContext {
argumentsAdded = true;
}
return super.lookupVariable(name);
variable = super.lookupVariable(name);
if (variable != null) {
return variable;
}
if (appContext != null) {
try {
super.setVariable(name, appContext.getBean(name));
return super.lookupVariable(name);
} catch (NoSuchBeanDefinitionException e) {
logger.debug("Bean lookup for variable '" + name + "' failed");
}
}
return null;
}
public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
@@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.access.prepost.PreFilter;
@@ -43,4 +44,9 @@ public class ExpressionProtectedBusinessServiceImpl implements BusinessService {
public Object[] methodReturningAnArray(Object[] someArray) {
return someArray;
}
@PreAuthorize("#x == 'x' and number.intValue() == 1294 ")
public void methodWithBeanNamePropertyAccessExpression(String x) {
}
}