Grails custom security evaluator
grails, spring-security
Solution
You should be able to do this with spring security and grails without much trouble.
I used the following 2 ways in the past for similar tasks. Both require the spring security ACL plugin which provides the `@PreAuthorize` and `@PostAuthorize` annotations.
Custom PermissionEvaluator
You can use the `hasPermission()` methods within security annotations and create a custom `PermissionEvaluator`. Within code this looks like this:
@PreAuthorize("hasPermission(#myObject, 'update')")
public void updateSomething(myObject) {
..
}
The `hasPermission()` calls are routed to a `PermissionEvaluator` by spring security. To write your own implementation you have to implement the `PermissionEvaluator` interface:
class MyPermissionEvaluator implements PermissionEvaluator {
@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
// your custom logic..
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
// your custom logic
}
}
To register your `PermissionEvaluator` you have to override the bean named `expressionHandler`. You do this by adding the following lines in `conf/spring/resources.groovy`:
beans = {
expressionHandler(MyExpressionHandler) {
parameterNameDiscoverer = ref('parameterNameDiscoverer')
permissionEvaluator = ref('myPermissionEvaluator') // your PermissionEvaluator
roleHierarchy = ref('roleHierarchy')
trustResolver = ref('authenticationTrustResolver')
}
myPermissionEvaluator(MyPermissionEvaluator)
}
Within `resources.groovy` you can define beans like you would do in `applicationContext.xml` when using spring. The above lines create a bean of type `MyPermissionEvaluator` with the bean name `myPermissionEvaluator`. Spring securities `expressionHandler` bean is overridden with a bean of type `MyExpressionHandler`. The other dependencies are copied from the configuration file of the spring security ACL plugin.
Service calls in security annotations
If the design of the `hasPermission()` methods does not achive all you requirements you can use simple service calls instead. The `@PostAuthorize` and `@PreAuthorize` annotations use SPEL to evaluate the expression. Within SPEL you can use the `@` symbol to access beans. For example:
@PreAuthorize("@securityService.canAccess(#myObject)")
public void doSomething(myObject) {
..
}
This calls the `canAccess` method of the bean named `securityService` and passes the method argument to it.
To use this approach you have to register a BeanResolver on the EvaluationContext. To do this you have to override the DefaultMethodSecurityExpressionHandler which is configured by the spring security ACL plugin.
This can look like the following:
class MyExpressionHandler extends DefaultMethodSecurityExpressionHandler {
BeanResolver beanResolver
@Override
public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation mi) {
StandardEvaluationContext ctx = (StandardEvaluationContext) super.createEvaluationContext(auth, mi)
ctx.setBeanResolver(beanResolver) // set BeanResolver here
return ctx;
}
}
`BeanResolver` is a simple interface that resolves a bean name to a bean instance:
class GrailsBeanResolver implements BeanResolver {
GrailsApplication grailsApplication
@Override
public Object resolve(EvaluationContext evaluationContext, String beanName) throws AccessException {
return grailsApplication.mainContext.getBean(beanName)
}
}
And finally add the beans to `resources.groovy`:
expressionHandler(MyExpressionHandler) {
parameterNameDiscoverer = ref('parameterNameDiscoverer')
permissionEvaluator = ref('permissionEvaluator')
roleHierarchy = ref('roleHierarchy')
trustResolver = ref('authenticationTrustResolver')
beanResolver = ref('beanResolver') // this is your BeanResolver
}
// This is the service called within security expressions
// If you place your service in the grails service folder you can skip this line
securityService(MySecurityService)
// this is your BeanResolver
beanResolver(GrailsBeanResolver) {
grailsApplication = ref('grailsApplication')
}
Update (2013-10-22): Recently I wrote a blog post about exactly this which provides some additional information.
Problem
I'm working on an app that has to do object level security checks, and the checks will be made by a service because it will need to make REST calls to a separate application. Because of this I'm not able to use Spring Security roles or ACLs because none of this information will be stored locally in the app. I'm trying to find an elegant way to handle this, and here are two options I can think of: 1) Create a custom annotation that will check permissions 2) Extend a Spring security annotation permission check (possibly with Permission Evaluator?) that lets me write the logic for checking access For #1 I've created a custom annotation and am using filters to read the annotation and check access, although this seems to be more brittle and will only give me protection for controller actions, and it would be nice to also secure other services as well. I've found bits an pieces of this information, but nothing complete. THIS talks about customizing ACL but only for a new permission, not controlling the logic THIS talks about using SpEL, but I'd like to have checks before a method runs, to make sure that no effect takes place that would be unauthorized. THIS appears to be the closest to what I want to do, but is specific to Spring Security and not Grails - my biggest challenge is converting the information in applicationContext.xml into resources.groovy Thanks in advance for any suggestions or advice you may have!