Role based access control (often abbreviated RBAC) is one of the most common forms of coarse grained access control found in software applications. RBAC is driven off of 3 main entities: roles, permissions and users (subjects).
Permissions define the specific actions or entitlements that exist within a system. For example, a simple reporting application might define permissions for each of the CRUD operations possible on reports: 'create-reports', 'view-reports', 'edit-reports' and 'delete-reports'. Permissions are coarse grained and unrelated to specific resources (i.e. the 'view-reports' permissions grants the ability to view all reports and not specific reports).
Roles allow for easy grouping of permissions by user personas. For example, in our reporting application, we might have an 'admin' role with all permissions: 'create-reports', 'view-reports', 'edit-reports' and 'delete-reports' and a 'basic-user' role with just the 'view-reports' permission. Users can then be assigned to either of these roles based on their needs within the system.
Given that permissions and roles translate naturally to capabilities and org hierarchies in most enterprises, RBAC is an especially popular access control choice for B2B and SaaS applications and is usually the first version of access control implemented in these applications. A basic RBAC implementation with a static set of roles and permissions can easily be implemented and integrated into an existing system.
At some point though, these applications can start to 'outgrow' RBAC. This typically happens when product requirements necessitate the need for more fine grained, resource-based access control policies like ABAC, ReBAC or some other custom solution.
Even though basic RBAC is relatively straightforward to implement, you can run into some common but complex challenges:
Basic RBAC works well for a static and deterministic set of roles and permissions. However, real-world applications often require additional context or dimensions on top of RBAC that can lead to something known as 'role explosion'. This concept is best explained through an example.
Let's say we have a multitenant application with 3 static roles: 'admin', 'owner' and 'viewer'. Doing basic RBAC with these 3 roles should be relatively straightforward, even in a multitenant environment. But what if we want to allow users to be a part of multiple tenants in the application? Furthermore, what if we want them to have different roles per tenant? This is where basic RBAC can start to fall apart and lead to role explosion. Let's say that our application currently has 20 tenants. How would we specify that a user (say 'user1') is an 'admin' in one tenant but not in another? The only way to achieve this in our existing RBAC system is by creating distinct roles for each role:tenant combination. So we'd end up with 20 new 'admin' roles (one per tenant) as well as 20 'owner' and 20 'viewer' roles for a grand total of 60 roles.
We can characterize this role explosion by the general expression 'a x b' where a = number of roles (3 in this case) and b = number of tenants (20 in this case). 60 roles might not seem like a lot at first but simply growing from 3 to 5 roles and 20 to 50 tenants would lead to 250 roles! Unfortunately, most RBAC implementations can't solve for role explosion natively and so moving to a more granular authorization system that supports contextual queries and/or ABAC-type policies is the optimal solution.
Another common RBAC challenge in multitenant B2B and SaaS applications (related to role explosion) is that of custom or dynamic roles. Similar to the example above, let's say we have an application with 3 built-in roles: 'admin', 'owner' and 'viewer'. Let's also say that we have 20 total tenants. One requirement that may come up is for tenants to have custom roles. For example, one of our tenants may not want one 'admin' role and instead want to split it up into 5, more granular roles.
If our RBAC implementation is dependent on a static set of roles (especially if defined directly in code), it will not be flexible enough to support custom roles per tenant. Our only option then would be to split up the role definition globally and have it apply to all tenants, which might not be acceptable. This is why it's considered a best practice to implement RBAC in a way where roles and permissions are 'data' and not 'static artifacts' defined directly in code. This approach might take longer to implement but will allow for easier expansion and changes in the future. This post goes into more detail about the pros and cons of access control as data vs. in files/code.
Most RBAC systems are implemented to answer access queries within the context of a single permission or role. For example, an application might make queries such as 'is user x a member of the admin role' or 'does user x have the create-reports permission'. These queries are easy to answer given the RBAC data model of permissions, roles and users.
Increasingly though, applications nowadays are being built to support 'multi-persona' or 'multi-context' usage. For example, a user of a SaaS application might use the app in different contexts: as an 'admin' when he/she is editing reports and as a regular 'viewer' when he/she is viewing dashboards and reports. As a result, this one user will have both the 'admin' and 'viewer' roles assigned to them and will have the corresponding permissions based on which role they are currently operating under.
For example, the user will only have the 'create-reports' permission when viewing the application as 'admin' and not when viewing the application as a 'viewer'. A basic RBAC system that doesn't take 'context' into account might always grant the user the 'create-report' permission, regardless of what role they're currently using because they've also been assigned the 'admin' role at some point. The proper way to support this would be by implementing a session or context that tracks a user's current 'active role' and restricts permissions queries based only on that context.
In general, most of the challenges described above highlight one major drawback of RBAC: a lack of granularity. While RBAC is easy to set up and implement, the main tradeoff is a lack of support for more complex and granular access control scenarios. For this reason, most companies and applications usually start with some form of RBAC before evolving or replacing it altogether with a more fine grained authorization system.
As mentioned earlier, basic RBAC with the 3 main entities (permissions, roles and users) is relatively straightforward to implement and set up. This post details a simple RBAC implementation for a web app backed by a SQL database. Again, RBAC might start simple but can easily grow in complexity as business requirements change. For this reason, it might even make sense to use a 3rd party library or service that supports RBAC as well as more granular, fine grained authz for the future.