Search code examples
javaspringgrailsgroovyspring-security

Spring Security ACL Inheriting: How does it really works?


I am using Spring Security with domain Level security (ACL).

I have two domains Company and Book.

class Company {
 String name
 hasMany = [books: Book]
}

class Book {
 String title
 belongsTo = [company: Company]
}

I read that an ACL can have a Parent where it inherits its permissions from.

In a service I would have a method like this to set permission for a new book card:

@PreAuthorize("hasRole('ROLE_ADMIN') or hasPermission(#book, admin)")
void addPermission(Book book, String username, Permission permission) {
  aclUtilService.addPermission(book, username, permission)
} 

This works fine and creates a permission for a new book instance. I also have an update method which checks the permission before the book can be updated.

@PreAuthorize("hasPermission(#book, write) or hasPermission(#book, admin)")
Book update(Book book, Map params = [:]) {    
  book.properties = params
  book.save(flush:true) 
  return book
}

This works fine as long as the user has the permission for the book. But, I want to user the permission of the parent object (company) to be inherited to the book. When a user has permission to the company I want that it has also permission to all of the company's books.

I see in AclImp that there is a method setParent(). This works if you do:

AclImpl acl = aclUtilService.readAcl(book)
acl.setParent(aclUtilService.readAcl(book.company))
aclService.updateAcl(acl)

Using the code above will set the parent for book in the database.

How do I use inheriting in Spring Security ACL?


Solution

  • You could do this instead of playing with the parent ACL:

    @PreAuthorize("hasPermission(#book.company, write) or hasPermission(#book.company, admin)")
    Book update(Book book, Map params = [:]) {    
      book.properties = params
      book.save(flush:true) 
      return book
    }
    

    If you would rather inherit from the parent ACL, make sure the child's AclObjectIdentity has entriesInheriting set to true, for example using the method createAcl below:

    void createAcl(Class clazz, Long id, String sid, AclObjectIdentity parent) {
        clazz = ProxyUtils.unproxy(clazz)
        AclClass aclClass = AclClass.findOrSaveByClassName(clazz.name)
        AclSid ownerSid = AclSid.findOrSaveBySidAndPrincipal(sid, true)
        AclObjectIdentity oid = findOrCreateObjectIdentity(aclClass, ownerSid, id, parent, true)
    }
    
    AclObjectIdentity findOrCreateObjectIdentity(AclClass aclClass, AclSid ownerSid, Long id, AclObjectIdentity parent, boolean entriesInheriting) {
        assert aclClass, "aclClass is required"
        assert null != id, "id is required"
    
        AclObjectIdentity oid = AclObjectIdentity.findOrCreateByAclClassAndObjectId(aclClass, id)
        if (null == oid.id) {
            oid.entriesInheriting = entriesInheriting
            oid.owner = ownerSid
            oid.parent = parent
            oid.save(flush: true, failOnError: true)
        }
        oid
    }
    

    If the ACL is inheriting @PreAuthorize("hasPermission(#child, read)") will return true if current user has read on parent's ACL