(Sorry if this is a duplicate, but the question is very search-engine-unfriendly.)
I want to know how to evaluate Spring EL inside EL (with all the functions, variables, context, etc. passed through).
Specifically, I want to dynamically evaluate a Spring Security expression (which is just EL plus some functions and contexts) loaded from a database entity inside a hard-coded EL in #PreAuthorize.
I.e. something like #PreAuthorize("eval(argument.securityExpr)").
You can extend Springs MethodSecurityExpressionRoot (and create it in your own MethodSecurityExpressionHandler) and add an eval method which excepts a String and let the SpringExpressionParser evaluate the String. Should work...
Edit:
A little code:
public class MySpringSecurityRoot extends MethodSecurityExpressionRoot {
private MyMethodSecurityExpressionHandler handler; // to be injected in the handler
public boolean eval(String expression) {
Expression expression = handler.getExpressionParser().parseExpression(expression);
return ExpressionUtils.evaluateAsBoolean(
handler.getExpressionParser().parseExpression(expression),
handler.createEvaluationContext(authentification, methodInvocation));
}
}
your handler must be set as the default method security expression handler:
<security:global-method-security pre-post-annotations="enabled">
<security:expression-handler ref="myHandler"/>
</security:global-method-security>
now your eval function is accessible in every method security expression
BUT: You must be aware of the fact that the person who describes your security rule, can access all beans inside the current spring context! Might be a security leak.
If you use a simpleparametername discover with # to evaluate permissions you can do virtually anything you want, without enabling debug mode.
#PreAuthorize("#mySecurityService.hasPermission(#arg0)")
public String getSpecial(final String special) {
return "authorized";
}
mySecurityService can be any bean/method returning a boolean, with this wired up for arg1
public class SimpleParameterNameDiscoverer implements ParameterNameDiscoverer {
public String[] getParameterNames(Method m) {
return getParameterNames(m.getParameterTypes().length);
}
public String[] getParameterNames(Constructor c) {
return getParameterNames(c.getParameterTypes().length);
}
protected String[] getParameterNames(int length) {
String[] names = new String[length];
for (int i = 0; i < length; i++)
names[i] = "arg" + i;
return names;
}
}
and context :
<bean id="methodSecurityExpressionHandler"
class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<property name="parameterNameDiscoverer">
<bean class="your.path.SimpleParameterNameDiscoverer"/>
</property>
Related
This will work
parser.parseExpression("#configList.stream().toArray()").getValue(context)
but the following won't
parser.parseExpression("#configList.stream().map(o -> o.ruleId).collect(Collectors.toList())").getValue(context)
F.Y.I the context is constructed as follows:
Object[] args = joinPoint.getArgs();
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
String[] params = discoverer.getParameterNames(method);
EvaluationContext context = new StandardEvaluationContext();
for (int i = 0; i < params.length; i++) {
context.setVariable(params[i], args[i]);
}
Although Java can be used in SPeL expressions, SPeL itself is a separate language and does not fully support the Java language. From the documentation:
SpEL is based on a technology agnostic API allowing other expression language implementations to be integrated should the need arise.
To perform filtering and mapping operations on a List in a SPeL expression, use collection selection and collection projection respectively:
Collection selection example
// Java:
configList.stream().filter(o -> o.getRuleId() > 2).collect(Collectors.toList())
// SPeL (notice the question mark) :
"#configList.?[ruleId>2]"
Collection projection example
// Java:
configList.stream().map(o -> o.getRuleId()).collect(Collectors.toList())
// SPeL (notice the exclamation mark) :
"#configList.![ruleId]"
I have set up a small example to demonstrate it:
public class So64738543ExpressionTest {
public static class RuleItem {
private int ruleId;
public RuleItem(int ruleId) {
this.ruleId = ruleId;
}
public int getRuleId() {
return ruleId;
}
}
#Test
public void collectionProjection() {
List<RuleItem> ruleItems = Arrays.asList(new RuleItem(1), new RuleItem(2), new RuleItem(3));
EvaluationContext context = new StandardEvaluationContext(ruleItems);
Expression expression = new SpelExpressionParser().parseExpression("#root.![ruleId]");
Assert.assertEquals(Arrays.asList(1,2,3), expression.getValue(context));
}
}
[edit]
Furthermore, if a SPeL expression becomes increasingly complex, I highly recommend to move the expression to a static method and invoke it using a T operator. Don't forget to include the fully qualified package name when referring to the static method.
I'm setting up a Spring Boot (v1.2.6) web project and using Spring Security (v3.2.8). I've found the #PreAuthorize annotation so handy, but I don't know if there's a way to read Boot's properties from the SpEL in the annotation. I'm trying to do it this way:
#PreAuthorize("mysecurity.permit")
With the property declared in application.yml:
mysecurity:
permit: true
But I'm getting
Failed to evaluate expression 'mysecurity.permit'
I've made an attempt with #mysecurity.permit and ${mysecurity.permit} too, with the same result. It seems possible to declare a method in a service and access it in #service.isMySecurityPermited() way, however I would be pleased to know if I'm able to access the property directly.
The values used in an annotation must be constants. They are evaluated at compile time, and while they may be retained for use at runtime they aren't re-evaluated. So you can use an expression that's evaluated by SpEL, or you can write a helper method that is referenced within the annotation value.
If you look at the OnExpressionCondition implementation, you will notice that it gets the value passed to the annotation, which in the case linked in your comment would be something like #ConditionalOnExpression("${server.host==localhost} or ${server.port==8080} ") The annotation simply gets the text value, it has no idea what the text represents, it just knows it's a string. It's in the processing of the annotation value within OnExpressionCondition that the String value takes meaning. They take the String value and pass it to a BeanExpressionResolver for resolution.
So, in your PreAuthorize solution, which based on http://forum.spring.io/forum/spring-projects/security/100708-spel-and-spring-security-3-accessing-bean-reference-in-preauthorize also passes it to an expression processor, you should be able to use spring's expression language to reference any bean property.
I'm not in a situation to test it currently, but from that thread it seems like you could do something like
#Component
public class MyBean {
#Value("${mysecurity.permit}")
private Boolean permit;
public boolean isPermitted() { return permit; }
#PreAuthorize( "#myBean.isPermitted()" )
public blah myMethod() {
// do stuff
}
}
This maybe a generic way to evaluate expressions which i want to share with you:
#Component("AuthorizationComponent")
public final class AuthorizationComponent {
private final static Logger logger = Logger.getLogger(AuthenticationUtils.class.getName());
private static SpelExpressionParser parser;
static {
parser = new SpelExpressionParser();
}
#Autowired
private Environment environment;
public boolean evaluateExpression(final String propertyKey) {
return checkExpression(environment.getProperty(propertyKey));
}
public static boolean checkExpression(String securityExpression) {
logger.info("Checking security expression [" + securityExpression + "]...");
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
Expression exp = parser.parseExpression(securityExpression);
SecurityExpressionRoot context = new CustomMethodSecurityExpressionRoot(authentication);
boolean result = exp.getValue(context, Boolean.class);
logger.info("Check result: " + result);
return result;
}
}
And in yaml config file you can configure the path and authorization expression, something like that:
preAuthorize:
whatever:
post: hasRole('MY_ROLE') OR hasAuthority('MY_AUTHORITY')
Then you could use it like that over your method:
#PreAuthorize("#AuthorizationComponent.evaluateExpression('preAuthorize.whatevert.post')")
#RequestMapping(value = "", method = RequestMethod.POST)
public ResponseEntity<Void> addQuestion(#Valid #RequestBody BodyRestDTO bodyRestDTO){
//Code implementation
return new ResponseEntity<Void>(HttpStatus.CREATED);
}
This should work:
#Value("${mysecurity.permit}")
private Boolean permit;
Then use:
#PreAuthorize(permit)
But you need to properly set configuration file, to allow Spring access it. Read here:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
UPDATE:
Did you configure bean for a property placeholder?
For example:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:com/foo/app.properties</value>
</list>
</property>
</bean>
Is there a way to create more expressive statements in #Preauthorize blocks? Here's an example of something I find myself repeating, because the #Preauthorize is not terribly smart out of the box.
#RequestMapping(value = "{id}", method = RequestMethod.DELETE)
public void deleteGame(#PathVariable int id, #ModelAttribute User authenticatingUser) {
Game currentGame = gameService.findById(id);
if(authenticatingUser.isAdmin() || currentGame.getOwner().equals(authenticatingUser)) {
gameService.delete(gameService.findById(id));
} else {
throw new SecurityException("Only an admin, or an owner can delete a game.");
}
}
What I would prefer is something like.
#RequestMapping(value = "{id}", method = RequestMethod.DELETE)
#Preauthorize(isAdmin(authenicatingUser) OR isOwner(authenicatingUser, id)
public void deleteGame(#PathVariable int id, #ModelAttribute User authenticatingUser, #ModelAttribute currentGame ) { //I'm not sure how to add this either :(
gameService.delete(gameService.findById(id));
}
Part of the problem is that I need to make a query to the database to fetch some of this stuff to verify permissions, such as querying the database to get a copy of the game, and then comparing the owner of the game to the person making the request. I'm not really sure how all of that operates within the context of a #Preauthorize annotation processor, or how I add things to the collection of objects made available in the #Preauthorize("") value attribute.
Since #PreAuthorize evaluates SpEl-expressions, the easiest way is just to point to a bean:
#PreAuthorize("#mySecurityService.someFunction()")
MySecurityService.someFunction should have return type boolean.
Spring-security will automatically provide a variable named authentication if you want to pass the Authentication-object. You can also use any valid SpEl-expressions to access any arguments passed to your secure method, evaluate regular expressions, call static methods, etc. E.g:
#PreAuthorize("#mySecurityService.someFunction(authentication, #someParam)")
1) First you have to reimplement MethodSecurityExpressionRoot which contains extra method-specific functionality. The original Spring Security implementation is package private and hence it is not possible to just extend it. I suggest checking the source code for the given class.
public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
// copy everything from the original Spring Security MethodSecurityExpressionRoot
// add your custom methods
public boolean isAdmin() {
// do whatever you need to do, e.g. delegate to other components
// hint: you can here directly access Authentication object
// via inherited authentication field
}
public boolean isOwner(Long id) {
// do whatever you need to do, e.g. delegate to other components
}
}
2) Next you have to implement custom MethodSecurityExpressionHandler that will use the above defined CustomMethodSecurityExpressionRoot.
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
#Override
public void setReturnObject(Object returnObject, EvaluationContext ctx) {
((MethodSecurityExpressionRoot) ctx.getRootObject().getValue()).setReturnObject(returnObject);
}
#Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
MethodInvocation invocation) {
final CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
root.setThis(invocation.getThis());
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(this.trustResolver);
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
}
3) Define expression handler bean in your context, e.g. via XML you can do it as follows
<bean id="methodSecurityExpressionHandler"
class="my.package.CustomMethodSecurityExpressionHandler">
<property name="roleHierarchy" ref="roleHierarchy" />
<property name="permissionEvaluator" ref="permissionEvaluator" />
</bean>
4) Register the above defined handler
<security:global-method-security pre-post-annotations="enabled">
<security:expression-handler ref="methodSecurityExpressionHandler"/>
</security:global-method-security>
5) Then just use the defined expressions in your #PreAuthorize and/or #PostAuthorize annotations
#PreAuthorize("isAdmin() or isOwner(#id)")
public void deleteGame(#PathVariable int id, #ModelAttribute currentGame) {
// do whatever needed
}
And one more thing. It is not very common to use method level security to secure controller methods but rather to secure methods with business logic (a.k.a. your service layer methods). Then you could use something like the below.
public interface GameService {
// rest omitted
#PreAuthorize("principal.admin or #game.owner = principal.username")
public void delete(#P("game") Game game);
}
But keep in mind that this is just an example. It expects that the actual principal has isAdmin() method and that the game has getOwner() method returning username of the owner.
You could write your annotation something like:
#PreAuthorize("hasRole('ROLE_ADMIN') and hasPermission(#id, 'Game', 'DELETE')")
To get the hasPermission part working you need to implement PermissionEvaluator interface.
Then define an expression handler bean:
#Autowired
private PermissionEvaluator permissionEvaluator;
#Bean
public DefaultMethodSecurityExpressionHandler expressionHandler()
{
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
handler.setPermissionEvaluator(permissionEvaluator);
return handler;
}
And inject in your security config:
<global-method-security pre-post-annotations="enabled">
<expression-handler ref="expressionHandler" />
</global-method-security>
I would like to create a class that adds custom methods for use in spring security expression language for method-based authorization via annotations.
For example, I would like to create a custom method like 'customMethodReturningBoolean' to be used somehow like this:
#PreAuthorize("customMethodReturningBoolean()")
public void myMethodToSecure() {
// whatever
}
My question is this.
If it is possible, what class should I subclass to create my custom methods, how would I go about configuring it in the spring xml configuration files and come someone give me an example of a custom method used in this way?
None of the mentioned techniques will work anymore. It seems as though Spring has gone through great lengths to prevent users from overriding the SecurityExpressionRoot.
EDIT 11/19/14 Setup Spring to use security annotations:
<beans ... xmlns:sec="http://www.springframework.org/schema/security" ... >
...
<sec:global-method-security pre-post-annotations="enabled" />
Create a bean like this:
#Component("mySecurityService")
public class MySecurityService {
public boolean hasPermission(String key) {
return true;
}
}
Then do something like this in your jsp:
<sec:authorize access="#mySecurityService.hasPermission('special')">
<input type="button" value="Special Button" />
</sec:authorize>
Or annotate a method:
#PreAuthorize("#mySecurityService.hasPermission('special')")
public void doSpecialStuff() { ... }
Additionally, you may use Spring Expression Language in your #PreAuthorize annotations to access the current authentication as well as method arguments.
For example:
#Component("mySecurityService")
public class MySecurityService {
public boolean hasPermission(Authentication authentication, String foo) { ... }
}
Then update your #PreAuthorize to match the new method signature:
#PreAuthorize("#mySecurityService.hasPermission(authentication, #foo)")
public void doSpecialStuff(String foo) { ... }
You'll need to subclass two classes.
First, set a new method expression handler
<global-method-security>
<expression-handler ref="myMethodSecurityExpressionHandler"/>
</global-method-security>
myMethodSecurityExpressionHandler will be a subclass of DefaultMethodSecurityExpressionHandler which overrides createEvaluationContext(), setting a subclass of MethodSecurityExpressionRoot on the MethodSecurityEvaluationContext.
For example:
#Override
public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {
MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(auth, mi, parameterNameDiscoverer);
MethodSecurityExpressionRoot root = new MyMethodSecurityExpressionRoot(auth);
root.setTrustResolver(trustResolver);
root.setPermissionEvaluator(permissionEvaluator);
root.setRoleHierarchy(roleHierarchy);
ctx.setRootObject(root);
return ctx;
}
Thanks ericacm, but it does not work for a few reasons:
The properties of DefaultMethodSecurityExpressionHandler are private (reflection visibility kludges undesirable)
At least in my Eclipse, I can't resolve a MethodSecurityEvaluationContext object
The differences are that we call the existing createEvaluationContext method and then add our custom root object. Finally I just returned an StandardEvaluationContext object type since MethodSecurityEvaluationContext would not resolve in the compiler (they are both from the same interface). This is the code that I now have in production.
Make MethodSecurityExpressionHandler use our custom root:
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
// parent constructor
public CustomMethodSecurityExpressionHandler() {
super();
}
/**
* Custom override to use {#link CustomSecurityExpressionRoot}
*
* Uses a {#link MethodSecurityEvaluationContext} as the <tt>EvaluationContext</tt> implementation and
* configures it with a {#link MethodSecurityExpressionRoot} instance as the expression root object.
*/
#Override
public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {
// due to private methods, call original method, then override it's root with ours
StandardEvaluationContext ctx = (StandardEvaluationContext) super.createEvaluationContext(auth, mi);
ctx.setRootObject( new CustomSecurityExpressionRoot(auth) );
return ctx;
}
}
This replaces the default root by extending SecurityExpressionRoot. Here I've renamed hasRole to hasEntitlement:
public class CustomSecurityExpressionRoot extends SecurityExpressionRoot {
// parent constructor
public CustomSecurityExpressionRoot(Authentication a) {
super(a);
}
/**
* Pass through to hasRole preserving Entitlement method naming convention
* #param expression
* #return boolean
*/
public boolean hasEntitlement(String expression) {
return hasRole(expression);
}
}
Finally update securityContext.xml (and make sure it's referenced from your applcationContext.xml):
<!-- setup method level security using annotations -->
<security:global-method-security
jsr250-annotations="disabled"
secured-annotations="disabled"
pre-post-annotations="enabled">
<security:expression-handler ref="expressionHandler"/>
</security:global-method-security>
<!--<bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">-->
<bean id="expressionHandler" class="com.yourSite.security.CustomMethodSecurityExpressionHandler" />
Note: the #Secured annotation will not accept this override as it runs through a different validation handler. So, in the above xml I disabled them to prevent later confusion.
I am considering using Spring Security annotations for my application, with the EL (expression language) feature. For example:
#PreAuthorize("hasPermission(#contact, 'admin')")
public void deletePermission(Contact contact, Sid recipient, Permission permission);
I need the EL capability because I have built my own ACL implementation. However, to use this capability with the "#contact" type arguments, the Spring documentation says this:
You can access any of the method
arguments by name as expression
variables, provided your code has
debug information compiled in.
This begs two questions:
It is acceptable to have a
production application commercially
distributed with debug info in it?
If not, is there any way around
this?
Thanks for any guidance on this!
As a workaround you can implement a custom ParameterNameDiscoverer with your own strategy. Here is an example which produces simple numbered names (arg0, etc):
public class SimpleParameterNameDiscoverer implements
ParameterNameDiscoverer {
public String[] getParameterNames(Method m) {
return getParameterNames(m.getParameterTypes().length);
}
public String[] getParameterNames(Constructor c) {
return getParameterNames(c.getParameterTypes().length);
}
protected String[] getParameterNames(int length) {
String[] names = new String[length];
for (int i = 0; i < length; i++)
names[i] = "arg" + i;
return names;
}
}
And configuration:
<global-method-security ...>
<expression-handler ref = "methodSecurityExpressionHandler" />
</global-method-security>
<beans:bean id = "methodSecurityExpressionHandler"
class = "org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<beans:property name = "parameterNameDiscoverer">
<beans:bean class = "foo.bar.SimpleParameterNameDiscoverer" />
</beans:property>
</beans:bean>
I guess this wasn´t an option when you approached the problem the first time, but now you can do this
#PreAuthorize("hasPermission(#contact, 'admin')")
public void deletePermission(#P("contact") Contact contact, Sid recipient, Permission permission);
http://docs.spring.io/spring-security/site/docs/current/reference/html/el-access.html#access-control-using-preauthorize-and-postauthorize
I can't find the reference now, but you might be interested to know that Java 8 will include parameter names at all times, even when I believe Java 8 will include parameter names at all times, even in debug mode.