According to Spring Security 3.1 by PacktPub:
Unfortunately, the actual implementation of AclImpl
directly compares the permission specified in our SpEL expression in our [@PostFilter
] annotation, and the permission stored on the ACE in the database, without using bitwise logic. The Spring Security community is in debate about whether this is unintentional or working as intended. . .
The example in that book tries to do exactly what you're describing -- it specifies a user with a role of 3 for read/write, but the user is denied access to an object with a permission mask of 1 for read.
The solution is to write your own custom permission evaluator.
MyPermissionEvaluator.java:
public class MyPermissionEvaluator implements PermissionEvaluator {
@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object requiredPermissions) {
//some way to access your user's assigned permission mask
int permissionMask = MyUserDetails.getMask();
//the requiredPermissions object must be cast as a String, and then
//converted to an integer, even though it is an integer in the ACL table
int permissionsRequired = Integer.valueOf(requiredPermissions.toString());
//a simple bitwise OR will identify whether the user has the required permissions
return ((permissionMask | permissionsRequired) == permissionMask);
}
. . .
}
To actually use this custom permission evaluator, edit your security.xml
file:
<security:global-method-security pre-post-annotations="enabled">
<security:expression-handler ref="expressionHandler"/>
</security:global-method-security>
<bean id="espressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<property name="permissionEvaluator" ref="permissionEvaluator"/>
</bean>
<bean id="permissionEvaluator" class="my.project.package.MyPermissionEvaluator"/>
Finally, whenever a method or class requires a certain permission level:
@PreAuthorize("hasPermission(#this, '4')")
public void mySecuredMethod() { //some secured method
}
Now you can set the permission mask in the ACL table to whatever corresponds to your organizational needs (bitwise), and do the same in whatever way you identify each individual user's permissions. For example,
user site_admin_bit database_admin_bit edit_data_bit write_data_bit read_data_bit
nancy 0 1 1 0 1
Nancy thus has a permission mask of 13 (out of a possible 31) as stored in your user details implementation. If she tries to access an object with a permission requirement of edit_data
, her permissions would be checked against a mask requirement of 4, and a bitwise OR evaluation (permissionMask | permissionsRequired == permissionMask
) would evaluate to true
.
This is, in my estimation, the easiest way to implement an organization-specific bitwise permissions mask (with 32 bits to play with, which should be enough, I should think). According to the referenced book, the hasPermission
SpEL expression used in Spring annotations evaluates the user's permissions as a complete unit; if the user has a permission set at 3 for read/write, but the annotation only evaluates against read (1), the user will be denied access.