We've encountered an unexpected lack of bitwise permission checking in spring security. We'd like to confirm if this is the expected behaviour and if so, what the history is and/or rationale for this.
We're using the grails plugin spring-security-acl-1.1.1 which uses spring-security-acl 3.0.7.RELEASE.
A distilled scenario of what we are trying involves an object with ACL such that aclUtilService.readAcl(obj) returns granting roles:
PrincipalSid[sampleuser]; permission: BasePermission[...........................A....=16]
GrantedAuthoritySid[ROLE_RWD]; permission: CumulativePermission[............................D.WR=11]
GrantedAuthoritySid[ROLE_RW]; permission: CumulativePermission[..............................WR=3]
GrantedAuthoritySid[ROLE_R]; permission: BasePermission[...............................R=1]
Subsequently we expect to check for a single permission like WRITE and have it return true. However it appears this is not supported. For example, for a user who has all of the roles defined on the object above, the execution of:
READ?: ${aclUtilService.hasPermission(springSecurityService.authentication, obj, BasePermission.READ)}
WRITE?: ${aclUtilService.hasPermission(springSecurityService.authentication, obj, BasePermission.WRITE)}
DELETE?: ${aclUtilService.hasPermission(springSecurityService.authentication, obj, BasePermission.DELETE)}
READ-WRITE?: ${aclUtilService.hasPermission(springSecurityService.authentication, obj, new BasePermission(BasePermission.READ.getMask() | BasePermission.WRITE.getMask()))}
Returns output:
READ?: true
WRITE?: false
DELETE?: false
READ-WRITE?: true
Whereas we would expect all of these to have returned true. Having looked at the source we can see the permission is ultimately checked in AclImpl, which contains the line
if ((ace.getPermission().getMask() == p.getMask()) && ace.getSid().equals(sid)) {
Which explains why only the exact masks are matching.
Changing only this line is somewhat involved and we have found that in spring-security-acl 3.1 this code was refactored to allow for a permission granting strategy to be defined- https://jira.spring.io/browse/SEC-1166
However the default granting strategy still only checks the exact mask. So:
- Why doesn't the default granting strategy check bitwise masks? What is the point of the CumulativePermission if this shouldn't be checked?
- What is the preferred approach for handling bitwise permissions, are we configuring the permissions incorrectly or should we just look to implement bitwise permission checking (and in which case, what is the preferred way of doing this).
Thanks for any explanation or guidance.