Object based security using Spring Security ACL in Grails

19 / Nov / 2014 by Anil Agrawal 0 comments

Spring security is used to secure the URL and only authenticated user is allowed to access that. But sometimes we need to secure the java Class instances and here comes ACL to rescue us.

Let’s see why we need object based security/ACL model.

Suppose, there is relation “BankAccount” and 10 records exists for it. So as per the requirement, only authorized person should have access to his/her account i.e only BankAccount holder or some bank authorities should have access over it and not any other have.

Now, depending on the User’s role we can restrict only the URLs using Spring Security core but not the access to any record. To achieve the object based security we need Spring Security ACL. Spring Security ACL uses Spring Security Core API.

You can use Spring Security ACL in any Java web application, for configuring the same in Grails App you can follow the link .

After successful configuration there will be new tables will be created

  1. acl_class
  2. acl_entry
  3. acl_object_identity
  4. acl_sid

Note: This is tested till Grails version 2.3.7 and I am not going to explain tables that were created due to Spring Security Core api (ie: user, role, user_role etc)

Note: Spring ACL security framework provides some beans(or say grails services) that ease our task like aclService, aclUtilService, permissionEvaluator

The whole ACL functionality designed to work around above 4 tables.

  • acl_class

        Object class that need to be secured using object based security (for example Account domain discussed in above example) should have an entry in this table


aclService.createAcl(domainObject), per domain class only one entry is required in this table

  • acl_sid

        This table contains the entry for user or role to whom we want to provide permission on any object instance. We don’t need to maintain this table at all, when we add any permission to any user, entry is automatically handled by AclUtilService.

  • acl_object_identity

        For each secured instance of object there should be an entry in this table. This entry is also maintained by AclUtilService when we add a permission to any object instance.

  • acl_entry

         Whenever we add or remove a permission to/from an object instance for a user/role, the entry for each permission (read,write,create,administration) is maintained in this table.

                    aclUtilService.addPermission(domainObject, User.findByName('xyz'), BasePermission.ADMINISTRATION)

 

Once we have done with providing permission to an object, now its time to secure our operation over it

         
           @PostFilter("hasPermission(filterObject, 'administration') or hasPermission(filterObject, 'read')")
            List<Account> listAccounts(){
                Account.list()
            }

In above code we have used PostFilter annotation, that processes the returning object list, and only returns the authorized list of objects for logged in user(objects on which user have either administration or read rights) .

 

         
          @PreFilter("hasPermission(filterObject, 'read')")
            Map fetchAmountInAccounts(List<Account> accounts){
                Map map=[:];
                accounts.each{
                    map.put(it,it.amount);
                }
                map
            }

In above code only authorized objects will be provided to the function for any operation from the supplied list of objects.

We can also restrict some operation on any object using PreAuthorize annotation,

         
        @PreAuthorize("hasPermission(#account,'admin')")
            void deleteAccount(Account account, OtherInfo info){}

Here if logged-in user doesn’t have admin permission on supplied account instance, he will not be able to perform operation over it. @PreAuthorize decides if a method can be invoked or not.

We can also apply access control check after method execution using PostAuthorize annotation,

         @PostAuthorize("hasPermission(returnObject,'read')")
            Account searchAccount(FilterParams params){
                //search activity
                account
             }

above code ensures that only authorized user will be able to see the account details.

PermissionEvaluator interface provides a way to check if some user have particular permission on any object instance or not,

permissionEvaluator.hasPermission(authentication, domainObject, BasePermission.READ)

returns true/false stating if provided user have permission on given domainObject or not.

Here is a sample application to test the Spring Security ACL.

May be required:

We found an issue in Spring Security ACL grails plugin (spring-security-acl:2.0-RC1), to handle it create a class as below

 

       
            import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsHibernateUtil
            import org.springframework.security.acls.domain.ObjectIdentityImpl
            import org.springframework.security.acls.model.ObjectIdentity
            import org.springframework.security.acls.model.ObjectIdentityGenerator
            import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy

            class MyObjectIdentityRetrievalStrategy implements ObjectIdentityRetrievalStrategy, ObjectIdentityGenerator {

                ObjectIdentity getObjectIdentity(domainObject) {
                    domainObject = GrailsHibernateUtil.unwrapIfProxy(domainObject)
                    String className = domainObject.getClass().name
                    createObjectIdentity(domainObject.id, className)
                }

                ObjectIdentity createObjectIdentity(Serializable id, String type) {
                    new ObjectIdentityImpl(type, id)
                }

            }

 

And make an entry in spring\resources.groovy class inside beans closer

            objectIdentityRetrievalStrategy(MyObjectIdentityRetrievalStrategy)

Tag -

FOUND THIS USEFUL? SHARE IT

Leave a comment -