Implementing Access Control for RBAC & ABAC in Node.js ๐Ÿ”’

Implementing Access Control for RBAC & ABAC in Node.js ๐Ÿ”’

ยท

6 min read

When creating software or web/mobile application, keeping it secure is crucial. There are two common ways to control who can access certain parts of the software: one is based on people's roles (RBAC), and the other is based on specific attributes (ABAC).

In Node.js, there's a helpful module called "AccessControl" that helps implement these control methods. But before we get into that, let's understand RBAC and ABAC simply.

What is RBAC?

RBAC stands for "Role-Based Access Control." It's a way of managing who can do what in a software system. Imagine you have a team of people working on a project, and each person has a specific role, like a manager, a writer, or an editor. RBAC assigns different roles to different people and then controls their access to certain parts of the project based on those roles. So, only the manager can access administrative features, while writers can edit content, and editors can review and approve changes. RBAC helps keep things organized and secure by giving the right permissions to the right people based on their roles.

What is ABAC?

ABAC stands for "Attribute-Based Access Control." It's another way of managing access to a software system. Instead of using predefined roles like RBAC, ABAC considers specific qualities or attributes of users and objects to control access.

Think of it like this: imagine a door with a security system that can check certain things before allowing someone to enter. In ABAC, these "certain things" are attributes. For example, the system may check if the person trying to enter is above a certain age, has a specific job title, or belongs to a certain department. Similarly, the system can check attributes of the object being accessed, like its sensitivity level or category.

With ABAC, access decisions are based on these attributes. It allows for more fine-grained control over who can do what, depending on various factors, making it very flexible and adaptable to different situations.

Implementing RBAC-ABAC in Node.js with AccessControl ๐Ÿ”’

AccessControl is a helpful Node.js module that combines the strengths of RBAC and ABAC. It handles RBAC fundamentals while also giving attention to resource and action attributes.

Installation

First off, you have to install access control either by npm or yarn

NPM: npm i accesscontrol --save
YARN: yarn add accesscontrol

Roles

In access control, "roles" act like containers for permissions. They are assigned to users based on their responsibilities. You can create and define roles by using the .grant(<role>) or .deny(<role>) methods on an AccessControl instance. We can implement that in node.js as follow:

// Import the AccessControl module
const { AccessControl } = require('accesscontrol');

// Create an instance of AccessControl
const ac = new AccessControl();

// Create a role named "reader"
ac.grant('reader');

// Roles can extend other roles, for example, "reader" extends "writer"
ac.grant('reader').extend('writer');

In this example, we created a new role named "reader" using .grant('reader'). Then, we made the "reader" role extend the "writer" role by calling .extend('writer'). This means that "reader" will inherit all the permissions granted to the "writer" role.

Actions and Action Attributes

In access control, "actions" and "action-attributes" determine what roles can do with resources. They are like a predefined list of actions based on classic CRUD (Create, Read, Update, Delete).

There are two specific action-attributes called "own" and "any" that define how roles can possess resources.

For example, imagine we have three roles: "reader," "writer," and "editor." The "editor" can perform all CRUD actions on any post. On the other hand, a "writer" can only read and update their own posts, while a "reader" can only read any post.

To define these actions and possession, you use simple methods like createOwn, readOwn, updateOwn, deleteOwn, createAny, readAny, updateAny, and deleteAny.

We can implement that in Node.js as follow

const { AccessControl } = require('accesscontrol');
const ac = new AccessControl();

// Granting permissions for 'reader' role
ac.grant('reader').readAny('post');

// Granting permissions for 'writer' role
ac.grant('writer')
  .createOwn('post')
  .deleteOwn('post')
  .readAny('post');

// Granting permissions for 'editor' role
ac.grant('editor')
  .extend('writer') // Editor inherits permissions from 'writer'
  .updateAny('post')
  .deleteAny('post');

Resources and resource-attributes

Resources and resource-attributes represent the things we want to protect, like a "post" in our example. Different roles can access these resources, but they might not have equal access to all the attributes of the resource.

To control access to specific attributes, we can use Glob notation. For example, suppose we have a "post" resource with attributes like "id," "title," and "description." We want the "editor" role to be able to read all attributes of any post, so we use .readAny('post', ['*']).

However, for the "reader" role, we don't want them to read the "id" attribute of the post, so we use .readAny('post', ['!id']).

We can implement that in Node.js using accesscontrol as follow:

const { AccessControl } = require('accesscontrol');
const ac = new AccessControl();
ac.grant('editor').readAny('post', ['*']);
ac.grant('reader').readAny('post', ['!id']);

Here, we used .readAny to specify the resource ("post") and the allowed or denied attributes using Glob notation. The "editor" can read all attributes ("['*']") of any post, while the "reader" cannot read the "id" attribute ("['!id']").

Checking permissions and filtering attributes

To check if a certain role has permission to perform a specific action on a resource, you can use the AccessControl module's .can(<role>).<action>(<resource>) method.

const permission = ac.can('reader').readAny('post');
const isGranted = permission.granted;
const allowedAttributes = permission.attributes;
const filteredData = permission.filter(data);

In this example, we used .can('reader').readAny('post') to check if the role "reader" has permission to read any "post." The result is stored in the permission variable.

To see if the permission is granted, we use permission.granted, which will be either true or false.

If permission is granted, we can get the allowed attributes using permission.attributes.

Lastly, we can use permission.filter(data) to filter the "data" object based on the allowed attributes, so only the allowed attributes will be included in the filtered data.

Define all grants at once

To define all grants at once, you can chain the methods for each role in a single sequence. This allows you to set multiple permissions for various roles and resources more efficiently.

const { AccessControl } = require('accesscontrol');
const ac = new AccessControl();

ac.grant('reader')
  .readAny('post');

ac.grant('writer')
  .createOwn('post')
  .deleteOwn('post')
  .readAny('post');

ac.grant('editor')
  .extend('writer')
  .updateAny('post')
  .deleteAny('post');

Here, we've defined all the grants for the roles "reader," "writer," and "editor" one after the other. The ac.grant method creates and starts defining permissions for a particular role, followed by the relevant actions like .readAny, .createOwn, and so on.

Conclusion

In conclusion I would say that AccessControl module in Node.js is a powerful tool to implement both Role-Based Access Control (RBAC) and Attribute-Based Access Control (ABAC). It allows you to define roles, permissions, and attributes to efficiently manage access to resources in your application. With clear and concise syntax, it provides a straightforward way to secure your software system while maintaining flexibility and granularity in access control.

I hope you write the article, for any query you can reach out to me on LinkedIn.

ย