Simple, Secure Role Based Access Control (RBAC) For REST APIs
What is RBAC and How Can You Utilize it
When it comes to managing user access to operations or resources, RBAC (Role Based Access Control) is a common approach. RBAC allows you to leverage permissions to specify what can be accessed – be it actions, or resources – while eliminating the need to manage these permissions individually. Permissions are assigned to roles, and roles can be assigned to users or user groups.
Read on for more about RBAC as a service.
Roles Bundle Permissions
Managing permissions at the user level can be daunting, especially with many users to keep track of. And as more users come online, it gets harder to stay accurate in assigning and maintaining permissions levels. At best, you’ll get a phone call from a user who can’t access a required system. At worst, unauthorized users will have access to risky operations or restricted areas, creating a massive vulnerability.
Learn all about our advanced RBAC and Governance Model in our latest post! READ MORE
Of course, there are many different models to choose from when it comes to access control, including RBAC management and DAC (Discretionary Access Control). This article is to share my perspectives on how to control RESTful applications with ease and while these principles can be applied to a variety of models, I will be using RBAC as a reference point as it is highly intuitive and widely adopted.
Read on to learn more about role based access control best practices.
Let’s start by understanding what we’re referring to when discussing “roles”. In the context of user permissions, a role does not refer to organizational structure or hierarchical job titles. A role in this case is a way to refer to a user or group of users related to their actions in a meaningful or common way. To define roles, we should look at what functions and actions users are performing.
The process of observing user activities and determining roles may feel a bit overwhelming – how do you determine which role groups and parameters to set? Reviewing users’ actions typically gives a restricted view of their activities (reading data, submitting forms, regularly used applications, etc.). With a bit more attention, we can see that there are certain actions that tend to go hand-in-hand. For example, you may find that users who read reports also update them, or that users who remove accounts also add them. Look for correlations between actions, and this is a good place to start in defining roles.
Roles will often have titles that we see commonly, such as “Editor” or “Account Administrator”. Once you have identified and defined the roles and assigned them to each user, these roles can be assigned permissions which are then applied to the users within the role group. By managing permissions within a small set of roles, administration and security becomes an easier task.
To illustrate the point, here are some illustrations:
A set of users within the system, with permissions assigned and linked without roles (managed individually):
The same set of users, with the same permissions, organized instead by role definitions:
As you can see, role based access control implementation makes permissions management much simpler!
Bundle Users with Groups
Taking it even further, a best practice is to assign roles to user groups rather than individuals.
As you begin to profile users’ actions related to roles, we will often begin to see many common threads between behaviors – users who perform the same operations, leveraging common resources. With these common actions and requirements, we can organize users into groups, and subsequently assign roles to the groups rather than individuals.
For example, we are likely to find multiple users who require an “Account Administrator” role definition, so we can establish a group called “Account Admins”, add the necessary users, and assign roles to that group.
Implementing Roles – Do’s And Dont’s
Now, let’s talk about some key things to keep in mind when establishing and implementing roles amongst users.
Never Couple Authorization Details and Actions
It is common in many systems for developers to restrict access to certain operations by specifying permission directly to the implementing method – in the code. In these cases, a role check would be added to the secured method through annotation. Here’s an example from a Spring Security based code:
public void update_order(Order order);
Again, this is very common amongst a variety of languages and frameworks. Though it is easy to implement, it leads to undesired coupling between the role itself and the implementation of the action. Imagine having dozens of methods, each annotated with hardcoded role names and definitions. Tracking effective permissions for each role will become so challenging, leading to inaccurate documentation or unknown, unmanaged permissions.
From a customer standpoint, this method of coupling makes it impossible to modify the role sets or permissions that have been defined by the developer, because these changes mean compiling and packaging the code each time (!) – not the user experience we’re going for.
How to Avoid Coupling
Here’s a better approach: first, extract the list of possible actions from the code that will be handled by an external authorization mechanism (further illustrated below). From there, we can make the code unaware of authorization details and roles, and instead simply ask if the current user (however it is retrieved) has been assigned the required permission (however it is defined) to execute a specified method.
This enables us to use generic annotations, such as:
public void update_order(Order order);
Mapping roles and granting permissions can now be done within a configuration file, which is easily accessed by customers – a much better user experience!
For example, consider this roles_config.yaml file:
In this case, the @secured wrapper can evaluate a user and determine if they have permission to execute ‘update_order’, based on the config file. For this example, the current users must be assigned the role “order_manager”, which is now both clear and easy to configure. Yet, the authorization mechanism still needs to know how to match permissions to methods within the code, and someone will need to document the available methods (i.e. view_order, change_order, etc.). This can be resolved, however, by applying the below.
Separate Concerns – Authorize Externally
Now, with a method implementation code that does not contain authorization details, we can move the entire authorization logic to a separate and independent module.
When we use a generic title (e.g., the annotation “secured”), we enable the entire authorization mechanism to be modified without any effect on the application code. As an example: we could implement “secured” as a role check, but it is also possible to use ACLs (Access Control Lists), like evaluating if the current user is listed on the order’s ACL list. Another option would be to use oauth, by asking a third-party (e.g. Facebook) whether the user has permission to perform the action.
REST is the Best
Action Extraction – Out Of The Box
I would say REST is the best (or, at least, the easiest) to suit this model. When designed properly, RESTful systems expose methods and resources through an HTTP-based RBAC API. Resources are identified by URIs, and methods are modeled using HTTP verbs (GET, POST, DELETE, etc.).
Here’s an example:
OST http://www.domain.com/bookings creates a new booking, and http://www.domain.com/orders/12345 returns details on order #12345. The extraction of actions as discussed above is ready, right out of the box!
REST services neatly model actions, as we’ve discussed, but that’s not all – this is also a good place in the request flow to evaluate authorization and authentication, as it is quite often the main system entry point. For the access control mechanism to make sense, the recommendation is to block all other access routes to the system, such as remote call mechanisms in the code, or direct access to data stores. This architecture also has the advantage of response filtering, in the cases where some of the data should not be returned to the user.
Requests are Also Access Control Tools
Because REST services process incoming requests, the information found within those requests can be used for access control decisions. Here are some details that prove useful:
- Request Origin allows blocking requests sent from unknown subnets or IP addresses.
- Headers can pass many interesting details, such as user credentials, which allow for an authorization/authentication process to take place.
- Target endpoint is indicated by the request’s URI (e.g. ‘topsecret’ in ‘http://domain.com/topsecret/’). This access can be restricted to a subset of application endpoints, pending other conditions. For example, while the ‘version’ endpoint is open to everyone, the ‘topsecret’ endpoint is only open to users who have been authorized/authenticated.
- Target method is represented by the HTTP verb (such as CONNECT), meaning it’s possible to block or pass requests based on the called method.
Putting it All Together – Using REST for Access Control
Remember the roles-to-permissions configuration file we discussed above? It had an elegant look, but required some work behind the scenes (which wasn’t depicted in our example), such as obtaining a list of all of the methods a user may call, and matching permissions individually to a specific method with that name.
But good news: there is a resolution!
REST URI’s expose all available resources, and together with HTTP verbs are able to cover all possible actions which can be performed and need to be secured. In the example below, 3 roles are configured:
- order_manager – can view, create, update and
- order_editor – can view, create and update
orders, but not delete them
- order_inspector – can only view
Through the above examples, we see how RESTful systems are a natural fit for access control. The REST service is capable of retrieving valuable information by processing incoming requests, and can hand this information over to a separate module to perform authentication and authorization tasks. If a user is authorized to perform the method as requested using the target resource, request processing may continue. Otherwise, it is here that further access is denied before reaching any internal application code.
So, we’ve covered some RBAC API permissions best practices, REST API access control, and ways to leverage this technology to your advantage for access controls and management within your environment.
In my next post, I cover how to implement this RBAC model in Cloudify, so stay tuned!