Designing Role-Based Access Control

Kamalmeet Singh
3 min readSep 20, 2023

--

Role-Based Access Control (RBAC) plays a crucial role in all applications, particularly in SaaS-based systems. Its primary purpose is to ensure that only authorized individuals can access specific features and data. If RBAC is not designed or implemented correctly, it can lead to serious consequences, such as data theft, unauthorized access to sensitive information, and even legal repercussions. Therefore, it is essential for architects and development teams to thoroughly examine access management requirements and establish the right design.

Design Option 1: User — Role

Role-based access can be implemented on a user-role basis. For instance, when a user is assigned an “admin” role, they gain access to specific functions, while a user with a “staff” role is limited to performing certain restricted operations.

Consider an example involving an Entity named “Product.” Users possessing the Admin role are granted the ability to Create and Edit the Product, whereas users with the Staff role are restricted to viewing the product. In this scenario, the authorization checks are contingent upon the assigned roles.

One can think of code implementation as

@PreAuthorize("hasRole('ROLE_ADMIN')")
@PostMapping("/create")
public ResponseEntity<String> createProduct(@RequestBody Product product) {
//logic here
return ResponseEntity.ok("Product created successfully");
}

Design Option 2: User — Role — Permission

We can delve into a more detailed approach and implement permissions on a feature or resource basis. To illustrate this, let’s revisit the example of the “Product” entity. We can establish different permissions such as “View Product,” “Create or Edit Product,” and “Delete Product.” These permissions can even be fine-tuned, for instance, to allow users to “view Products in my department only.” The advantage of this design is that roles remain separate from permissions. When creating a role, permissions can be selectively chosen and assigned to that role.

It’s crucial to emphasize that while users are assigned roles, access ultimately depends on permissions. For instance, User A might have the “Staff” role, granting them permission to “View a Product,” but it doesn’t provide permission to “Create a Product.” In the actual code, there won’t be any mention of “roles”; instead, the system will verify permissions at the access level.

@PreAuthorize("hasAuthority('CREATE_PRODUCT')")
@PostMapping("/create")
public ResponseEntity<String> createProduct(@RequestBody Product product) {
//logic here
return ResponseEntity.ok("Product created successfully");
}

Choosing the Right Option

We can clearly observe that the first option is simpler, involving only two entities. On the other hand, the second option, which incorporates permissions, offers greater flexibility but introduces added complexity.

In summary, if you have a predefined set of roles that won’t change frequently, it’s advisable to keep things straightforward and opt for option 1. However, if you want to empower end users to create new roles by combining existing permissions, option 2 is the way to go for greater control.

--

--

Kamalmeet Singh

Tech Leader - Building scalable, secured, cloud-native, state of the art software products | Mentor | Author of 3 tech books |