I've been reading up on how to implement Role Based Access Control (RBAC), with the intention to implement it within a Spring based microservice API.
It appears that when implementing RBAC, you should check permissions, rather than roles. The following posts seem to agree on this point:
- Role Based Access Control (RBAC) cares about permission or roles?
- https://hackernoon.com/role-based-access-control-design-for-micro-services-dg1233079
- https://softwareengineering.stackexchange.com/questions/299729/role-vs-permission-based-access-control
- https://heap.io/blog/engineering/structure-permissions-saas-app
The selected answer in the 1st link above says "Spring security and many other prominent access control mechanisms propagate this security anti-pattern" [of checking roles instead of permissions]. Although there are only 3 upvotes so I'm not sure if this statement would be widely accepted.
So I'd like to know, how best to approach RBAC within a Spring based API, so that I check permissions rather than roles.
Take the following scenario:
A user in my app can have the Role PLAN_MANAGER
, which may have the permissions VIEW_PLAN
, CREATE_PLAN
, EDIT_PLAN
, DELETE_PLAN
. Within our app, we would allow some users to change permissions associated with roles, so for example, an admin user may edit the permissions on the PLAN_MANAGER
role to remove the DELETE_PLAN
permission.
Within spring, I would have a deletePlan
method. I want to know how to allow users to execute this method only if they have the DELETE_PLAN
permission. It looks like PreAuthorize checks are the standard way to check if a user has access to a method in spring, however it seems that hasRole, rather than hasPermision, is most commonly used with the PreAuthorize annotation.
I can't find examples/tutorials of using a PreAuthorize check that only references the permission, instead it seems to be more common to reference the role (however, the following example shows using both role and permission check https://www.dontpanicblog.co.uk/2012/08/19/protecting-service-methods-with-spring-security-annotations/).
Most examples I find seem to just do something like:
@PreAuthorize("hasRole('PLAN_MANAGER'))
But if I use that approach, and an admin user removes the DELETE_PLAN
permission from the PLAN_MANAGER
role, then we would incorrectly allow the user to execute deletePlan
.
So should I instead be using something like:
@PreAuthorize("hasPermission('DELETE_PLAN'))
So that even if the permissions associated with roles change, this check will always be valid for this particular method, and the code won't need to be changed. So new roles could be added to the app with the DELETE_PLAN permission, and we wouldn't need to add new explicit role checks in the code for those new roles.
Thanks.
Update
I've found what looks to be someone else trying to do the same thing as me here https://stackoverflow.com/a/60251931, and it looks like he's put together a repo at https://github.com/savantly-net/spring-role-permissions
There's an example shown in the readme, pasted below, which looks to be exactly what I want to achieve - so it seems I need to use hasAuthority rather than hasRole or hasPermission (hasPermission requires an object target argument, which I don't want to specify, as that would seem to go beyond RBAC into ABAC, I simply want to specify a permission/privilege, which is why hasAuthority looks appropriate):
@PreAuthorize("hasAuthority('CREATE')")
@RequestMapping("/create")
public String create() {...}
(also found an older post detailing another custom approach which looks to allow for checks on privilege/permission rather than role - https://stackoverflow.com/a/22612076/14147607)