We were faced with the unexpected lack of bitwise permission checking in spring security. We would like to confirm whether this is the expected behavior, and if so, which story is and / or justified for this.
We use the grails spring -security-acl-1.1.1 plugin that uses spring -security-acl 3.0.7.RELEASE.
The distilled scenario of what we are trying includes an object with an ACL, so aclUtilService.readAcl (obj) returns role 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 one permission, for example WRITE, and return it true. However, this does not seem to be supported. For example, for a user who has all the roles defined for the object above, the execution:
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 the output:
READ?: true WRITE?: false DELETE?: false READ-WRITE?: true
While we expect them all to return. Looking at the source, we see that the resolution is ultimately checked in AclImpl, which contains the string
if ((ace.getPermission().getMask() == p.getMask()) && ace.getSid().equals(sid)) {
This explains why only exact masks are suitable.
Changing this line only is somewhat related, and we found that in spring-security-acl 3.1 this code was reorganized so that we could define a permission policy - https://jira.spring.io/browse/SEC-1166
However, the default provisioning strategy still only validates the exact mask. So:
- Why doesn't the default strategy check bit masks by default? What is the point of CumulativePermission if it should not be checked?
- What is the preferred approach for handling bitwise permissions, are we setting the permissions incorrectly, or should just look for an implementation of bitwise permissions (in which case, this is the preferred way to do this).
Thanks for any explanation or guidance.