'Filtering user access in Grails' post illustration

Filtering user access in Grails

avatar

Today we’ll talk about the ways to filter user access to some controllers and actions in the Grails application. There are few solutions that could be really useful for implementing this task. So let’s look at a quick overview of them.

At first, let’s take a look on the solutions that are suggested by spring security plugin. One of them is an @Secured annotation. You may use it on the class or action level. Here is an example of using @Secured on different levels:

1
2
3
4
5
6
7
8
9
10
11
@Secured([ROLE_ADMIN, ROLE_SUPER_ADMIN])
class AdminController {
    def index = {
        render you have admin rights    
    }
    
    @Secured([ROLE_SUPER_ADMIN])
    def superAdmin = {
    render you have super admin rights
    }
}

In this example users with admin or super admin roles has access to this controller, and only super admin has access to the superAdmin action. In some cases there is a lot more convenient to specify a list of controllers and actions to which specified users has access to. This could be done by using an intercept URLs map in config.groovy file. Here is an example of using it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import grails.plugins.springsecurity.SecurityConfigType
import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils
//specifying security config type
grails.plugins.springsecurity.securityConfigType=SecurityConfigType.InterceptUrlMap
//defining the user class name
grails.plugins.springsecurity.userLookup.userDomainClassName = 'sysgears.User'
//defining authority join class name
// UserSecRole - collection that contains user - security role relation 
grails.plugins.springsecurity.userLookup.authorityJoinClassName = 'sysgears.UserSecRole'
//defining security role class name
grails.plugins.springsecurity.authority.className = 'sysgears.SecRole'
//map to secure URLs
grails.plugins.springsecurity.interceptUrlMap = [
    '/': ['IS_AUTHENTICATED_ANONYMOUSLY'],
    '/login/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
    '/register/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
    '/images/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
    '/css/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
    '/fonts/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
    '/js/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
    'admin/superAdmin/**':['ROLE_SUPER_ADMIN'],
    'admin/**':['ROLE_ADMIN', 'ROLE_SUPER_ADMIN']
]

Please make sure that you specified the right order of mapping, rights that was specified first has greater priority.
Sure this is a great solutions, but what would you do if using of security roles is not enough for you? In this case standard grails features, such as interceptors and filters could be really useful. At first let’s talk about interceptors. Interceptor is a nice tool not only to filter user access to some controllers and actions, with interceptors you may modify the data that’s being received and sent by actions, change the view that should be rendered after executing and more. There are two kinds of interceptors: beforeInterceptor and afterInterceptor. To define interceptor that will be executed before every action write the following code:

1
2
3
4
5
class SampleController {
    def beforeInterceptor = {
        //do something
    }
}

Let's look at the example, that will show how to make interceptor execute only for specified actions.

1
2
3
4
5
//interceptor will be executed for all the actions except 'register' and 'login'
//auth is an action that will be executed as interceptor
def beforeInterceptor = [action:this.&auth,except:['login', 'register']]
//interceptor will be executed only for 'superAdmin' action
def beforeInterceptor = [action:this.&auth,only:['superAdmin']]

AfterInterceptors are being executed after the action, so they could modify model and modelAndView objects and could be used as follows:

1
2
3
4
5
def afterInterceptor = { model, modelAndView ->
    if(model.changeView) {
        modelAndView.viewName = "/samplecontroller/otherview"
    }
}

If we are dealing with application that has really big amount of controllers using of interceptors is not so convenient, in this case using of filters could be a better solution. To create a filter, create a class that ends with the convention Filters in the grails-app/conf directory. Within this class define a code block called filters that contains the filter definitions. Within the filters there are three types of interceptors: before - executed before action, after - executed after action, afterView - executed after rendering the view. Here is an example of applying different filters.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class BlockFilters {
    def filters = {
       //applies filter to all the controllers 
       allTabsFilter(controller: '*', action: '*') {
           before {
               //do something
           }
       } 
 
        //applies filter to some controller
        vipFilter(controller: 'vip', action: '*') {
            before {
                //do something
            }    
        }

        //applies filter to all controllers except 'Login', 'Logout', 
        //'Register' and 'Ban'
        banFilter(controller: '(login|logout|ban|register)',
            action: '*', invert:true) {
before = {
                User user = User.findByUsername(applicationContext.
                        getBean("springSecurityService").authentication.name)
                if (user && user.isBanned()) {
                    redirect(controller: "ban", action: "index")
                    return false
                }
            }
        }    
    }
}

If you're looking for a developer or considering starting a new project,
we are always ready to help!