9

Spring Security is great for doing role-based authorization, but it seems to fall short when it comes to rule-based authorization. Sure, there are ways to do it via SpEL, but going that route seems to lock your authorization logic inside the annotations when it would be much better to pull that logic out into a service so that multiple places can use the same logic.

There seem to be some ways to go about adding in your own SpEL expressions, but noting is particularly clear, and even those that make sense to me seem to fall short. I would think, given how flexible Groovy is, that there must be some way to not have to hard-code the dependencies together, but to have the security rules (or SpEL extensions) picked up at run-time.

While not ideal, even something as seemingly simple as defining all of the desired new rules and injecting the as mixins (ie. SecurityExpressionRoot.mixin MyRule1) would be a good start, but that doesn't seem to work.

Does anyone know of an example that does this? If not, how might I go about doing this myself?


One (simplified) example : A user can only take a particular action (ie. execute a service method) with an object if 3 of 4 fields have values over a particular threshold, but only if the object is less than 3 days old:

class MyRule {

    boolean canTakeAction(Person person, MyThing myThing) {
        int numFieldsWithValues = 0
        if (myThing.field1 != null) { numFieldsWithValues++ }
        if (myThing.field2 != null) { numFieldsWithValues++ }
        if (myThing.field3 != null) { numFieldsWithValues++ }
        if (myThing.field4 != null) { numFieldsWithValues++ }

        return (numFieldsWithValues > 3) && (ageInDays(myThing) < 3)
    }

    int ageInDays(MyThing myThing) {
        ...
    }
}

And that is one of the simpler rules.

Community
  • 1
  • 1
cdeszaq
  • 30,869
  • 25
  • 117
  • 173

3 Answers3

2

Role based authorization is the easiest but less flexible way. The contrast to this is the Spring security ACL system. ACLs are let you define exaclty who is allowed to do what on which object at runtime. On the other side this requires a much more complicated setup. There is also a grails plugin for this.

The way of using annotions with SpEL expressions is somewhere between these both alternatives. They are more flexible than simple roles and easier than ACLs. If you are looking for an introduction to method security in Grails maybe this blog post I wrote some time ago can help you.

micha
  • 47,774
  • 16
  • 73
  • 80
  • Like I said in my question, role-based authorization (which ACLs are just a more fine-grained version of) is great when that is what you need, and Spring Security is good at that. Unfortunately, my requirements are _far_ more complex than what role-based can handle easily. The SpEL rules are more what I'm after, but I need to be able to get at the same complex rules in multiple places in the application and copy-paste of SpEL rules can't be the best way to do that. – cdeszaq Dec 18 '12 at 22:49
  • Imho ACL isn't a more fine-grained role-based system. It lets you define exaclty who is allowed to perform a certain operation on an object (independent of any roles). Can you give an example of your complex rules? – micha Dec 18 '12 at 22:57
  • One example (simplified): A user can only take a particular action (ie. execute a service method) with an object if 3 of 4 fields have values over a particular threshold, but only if the object is less than 3 days old. And that is one of the simpler rules. – cdeszaq Dec 18 '12 at 23:00
  • In this case the Annotations should be the way to go. If you provide your own `SecurityExpressionHandler`/`EvaluationContext` it shouldn't be that hard to move your rules into functions defined in an extra file. Within the expression you use the functions that describe your rules, like `@PreAuthorize("isOperationXyzAllowed(#object) and isObjectValid(#object)")` – micha Dec 18 '12 at 23:29
  • 1
    I agree, that _does_ seem like the way to go, but there doesn't seem to be any clear explanation of how to do so or go about it, particularly in Grails, and that is what I'm looking for. It also seems like Groovy could make it much less painless as well, so I'm also hoping to find that. – cdeszaq Dec 18 '12 at 23:33
1

You can manage your rule in Requestmap, which provides dynamic configuration capability.

For instance, define security type as requestmap in config.groovy first:

grails.plugins.springsecurity.securityConfigType = "Requestmap"

Then you may have Requestmap domain class similar to User and Role, like this:

package com.app.auth
class Requestmap {

    String url
    String configAttribute

    static mapping = {
        cache true
    }

    static constraints = {
        url blank: false
        configAttribute blank: false
    }
}

Now since User, Role, and Reqestmap are all persisted in database, you can easily change your rules by CRUD actions in some controller without need to redeploy or restart your service. Like this:

class RequestmapController {

def springSecurityService

...

def save = { 
    def requestmapInstance = new Requestmap(params) 
    if (!requestmapInstance.save(flush: true)) { 
        render view: 'create', model: [requestmapInstance: requestmapInstance] 
        return 
    }

    springSecurityService.clearCachedRequestmaps()     //This is important: to refresh the requestmap. Without it the rules remain unchanged.
    flash.message = "${message(code: 'default.created.message', args: [message(code: 'requestmap.label', default: 'Requestmap'), requestmapInstance.id])}" 
    redirect action: show, id: requestmapInstance.id 
    } 
}

In the view layer, you can manage your menus, buttons, and other elements need authorization by using spring security tag like:

<sec:access url="foo/bar">
    <li><g:link class="list" controller="foo" action="bar">Bar action</g:link></li>
</sec:access>

Therefore, the views are also comply with the authorization rule.

Furthermore, there is a Hierarchical Roles feature which can be leveraged to reduce clutter in your request mappings.

coderLMN
  • 3,076
  • 1
  • 21
  • 26
  • As I stated in the question and mentioned to @micha, roles are not sufficient to cover my needs. I've added an example of a simplified version of one of the rules I need to be able to use to secure method access in a number of different places. If what you describe can handle that, please expand your example to show how. – cdeszaq Dec 19 '12 at 13:33
  • I think you can use [Filters](http://grails-plugins.github.com/grails-spring-security-core/docs/manual/guide/16%20Filters.html) to customize the rules specified in your example. – coderLMN Dec 19 '12 at 13:55
  • Filters can't be applied to service method calls, only to web requests. I need the security rules to be applied via Spring Security (or at least everywhere Spring Security will allow me to apply security rules using SpEL), so filters are not enough either, though I like the idea and it might work for some of the rules. – cdeszaq Dec 19 '12 at 14:00
0

From the example you posted, GContracts might be what you're looking for. You'd just have to figure out how to access the Principal when running a GContract closure, but that might be just as simple as passing it as a parameter to the contracted method.

schmolly159
  • 3,881
  • 17
  • 26
  • It looks promising, but GContracts is [known to have issues with Grails](https://github.com/andresteingress/gcontracts/wiki/GContracts-in-Spring-and-Grails-Applications) and Spring more generally. I'm not sure I would run into that particular issue, but there has to be a way to use the SpEL built into Spring Security. – cdeszaq Dec 19 '12 at 15:41