Handling Instance Based Security

06 / Apr / 2011 by Imran Mir 4 comments

In my current project, we were required to implement Instance Based Security. The idea was to find a clean solution separate from the main business logic of the application. We took a clue from the Spring Security Plugin to use the Annotations to do our job. All we wanted to do was to develop annotations for actions, which could help to decide what type of access verification needs to be done on a particular request, inside some Filter. So we proceeded like this:

We created an Abstract Class from which our User domain Class extended. All our Access Verifying functions would remain in this class. The class provides a single entry function (by the name evaluateExpression) to all other Access Verifying functions. This helped us to keep the Filter class clean. The Class looks like:

[java]
abstract class MyAppSecure {

public Boolean evaluateExpression(String methodName, Map params = [:]) {
this."$methodName"(params.id?.toLong())
}

// One of the many functions to be used to verify valid access
public Boolean hasUserAccessById(Long id) {
return id ? (this.id == id.toLong()) : true
}

}
[/java]

So our User domain class looks like:

[java]
class User extends MyAppSecure {

}
[/java]

Now, the idea is to:

  1. Intercept a request in the filter.
  2. Get the annotation expression placed on top of the respective Controller Action. (The expression(s) would be name of the MyAppSecure method(s) used to verify the access)
  3. Call a corresponding method in MyAppSecure to verify access
  4. Take appropriate action corresponding to a result

So let us delve into the implementation:

First step would be to enable the annotations. To do this create an interface in src/java (or you can do it in src/groovy as well):

[java]
@Documented
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MySecured {
String[] expressions();
}
[/java]

Now we are good to go for using annotations. Let us assume an action edit inside AccountController:

[java]
class AccountController{

@MySecured(expressions = ["hasUserAccessToAccount"])
def edit = {
User user = params.id ? User.get(params.id) : user
render(view: "edit", model: [user: user])
}
}

[/java]

Note: We can also give multiple expressions (to evaluate multiple access rules), separated by commas inside the list.

Now we need to implement a method by the name hasUserAccessToAccount in MyAppSecure:

[java]
abstract class MyAppSecure {

public Boolean evaluateExpression(String methodName, Map params = [:]) {
this."$methodName"(params)
}

public Boolean hasUserAccessToAccount(Map params) {
// SAMPLE LOGIC.
Integer count = Account.createCriteria().get {
projections {
count("id")
}
eq(‘id’, params.id.toLong())
eq(‘user’, this)
}
return (count > 0)
}
}
}

[/java]

We would also need some logic to get and parse annotations in MyAppSecure. We wrote something like this:

[java]
public Boolean hasAccess(Class controllerClazz, String actionName, Map params) { def field = controllerClazz.declaredFields.find {it.toString().indexOf(controllerClazz.name + ‘.’ + actionName) != -1}
// get the annotation on a Controller action (account/edit in our case)
def securedAnnotation = field.getAnnotation(AdlSecured)
return (securedAnnotation ? this.evaluateEveryExpression (params, securedAnnotation)
}

// Evaluate every expression and combine the result using AND (or we can use OR as well)
private Boolean evaluateEveryExpression(Map params, def securedAnnotation) {
Boolean hasAccess = securedAnnotation.expressions().every {String securedExpression ->
this.hasAccessForExpression(securedExpression, params)
}
return hasAccess
}

//Evaluate expression
private Boolean hasAccessForExpression(String expressionToBeEvaluated, Map params) {
return this.evaluateExpression(expressionToBeEvaluated, params)
}

[/java]

Now in you Filter you can write something like this:

[java]
mySecureFilter(controller: "*", action: "*") {
before = {
User user = someSecurityService.currentUser
if (user) {
// gets the controller class for a controllerName
Class controllerClazz = getControllerClass(controllerName)

if (!user.hasAccess(controllerClazz, actionName, params)) {
redirect(controller: ‘accessController’, action: ‘unauthorized’)
}
}
return false
}
}
[/java]

That is all that we need to do. I hope you would find this useful. Any suggestions will be welcomed.

FOUND THIS USEFUL? SHARE IT

comments (4)

  1. Bhagwat Kumar

    Great blog though trying the steps listed in the blog, I found the followings in sample code (possibly due to erroneous copy/paste 🙂 ) :

    In MySecured Interface :

    @Target([ElementType.FIELD, ElementType.METHOD, ElementType.TYPE])

    In Filter class : try this implementation for getControllerClass method :

    private Class getControllerClass(String controllerName) {
    grailsApplication.controllerClasses.find{
    controllerClass->org.apache.commons.lang.WordUtils.uncapitalize(controllerClass.name).equals(controllerName)
    }?.clazz
    }

    In hasAccess method of MyAppSecure clas :

    return (securedAnnotation ? evaluateEveryExpression(params, securedAnnotation) : true)

    Hope it helps peeps in trying the code with less effort.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *