Skip to main content

How to use AccessControl for RBAC and ABAC in Node.js

 System security is one of the key considerations when building software, and there are various mechanisms used in ensuring a software system is secure. The common ones are role-based access control (RBAC) and attribute-based access control (ABAC).


AccessControl, a Node.js module, can be used to implement these two access control mechanisms. Before diving into how AccessControl works, let’s briefly explain these two mechanisms and how they work.


Role-based access control (RBAC)

Role-based access control, also known as role-based security, is a mechanism that restricts system access to users using their roles and privileges and permissions.


Within an application, roles are created for various user types (e.g., writer or reader). The permission to perform certain actions or access application resources are assigned to specific roles. For instance, in a writing application, a writer can be granted the permission to create, update, read, and delete a post, whereas a reader can be restricted to only being able to read a post.


When using RBAC, there are three guiding rules:


Role assignment: A subject (i.e., a user) can exercise a permission only if the subject has selected or been assigned a role.

Role authorization: A subject’s active role must be authorized for the subject. With rule 1 above, this rule ensures that users can take on only roles for which they are authorized.

Permission authorization: A subject can exercise a permission only if the permission is authorized for the subject’s active role. With rules 1 and 2, this rule ensures that users can exercise only permissions for which they are authorized.

A user can have multiple roles and a role can have multiple permissions. RBAC also supports role hierarchy where a high-level role inherits the permissions of its sub roles. Additional constraints may be applied to place restrictive rule on the potential inheritance of permissions from another role. Some examples of constraints are:


A role cannot inherit itself

Cross-inheritance is not allowed (if the writer inherits from the reader, the reader cannot inherit from the writer)

A role cannot pre-extend a non-existing role

Attribute-based access control (ABAC)

Attribute-based access control, also known as policy-based access control for IAM, defines an access control paradigm whereby access rights are granted to users through the use of policies which combine attributes together. The policies can use any type of attributes, such as user attributes, resource attributes, object, and environment attributes.


ABAC can be used to complement RBAC in that in addition to roles and permissions, a policy can be used to define what attribute is allowed or not allowed.


Node.js RBAC-ABAC using AccessControl

AccessControl, a Node.js module, merges the best features of RBAC and ABAC. It implements RBAC basics and also focuses on resource and action attributes. For a full list of the module’s features, view the documentation.


Installation

With npm: npm i accesscontrol --save.

With Yarn: yarn add accesscontrol


Roles

Roles serve as containers for permissions. They are assigned to users depending on their responsibility. You can create and define roles simply by calling .grant(<role>) or .deny(<role>) methods on an AccessControl instance.


import { AccessControl } from 'accesscontrol';


const ac = new AccessControl();

ac.grant('reader');

Roles can extend other roles. You can extend a role by calling .extend on an existing role.


ac.grant('reader').extend('writer');

Actions and action-attributes

Actions and action-attributes represent what can be performed on resources by role(s). They are a finite fixed list based on classic CRUD. There are two action-attributes which define the possession of the resource by a role: own and any.


For example, an editor role can create, read, update or delete (CRUD) any post resource. But a writer role might only read or update its own post resource.


You can define an action and possession on a resource using: createOwn, readOwn, updateOwn, deleteOwn, createAny, readAny, updateAny, and deleteAny methods.


const ac = new AccessControl();

ac.grant('reader')

  .readAny('post')

  .grant('writer')

    .createOwn('post')             

    .deleteOwn('post')

    .readAny('post')

  .grant('editor')                   

    .extend('writer')                 

    .updateAny('post')  

    .deleteAny('post');

Resources and resource-attributes

These represent system elements that we want to protect, such as post. Multiple roles can have access to a specific resource but may not have equal access to all attributes of the resource. You can use Glob notation to define allowed or denied attributes.


For example, we have a post resource that has the following attributes: id, title, and description. All attributes of any post resource can be read by an editor role:


ac.grant('editor').readAny('post', ['*']);

But the id attribute should not be read by a reader role.


ac.grant('reader').readAny('post', ['!id']);

Checking permissions and filtering attributes

The permission granted is determined using a combination of role, action, and resource. You can add .can(<role>).<action>(<resource>) on an AccessControl instance to check for granted permissions for a specific resource and action.


const permission = ac.can('reader').readAny('post');

permission.granted;

permission.attributes;

permission.filter(data);

Defining all grants at once

You can pass the grants directly to the AccessControl constructor. It accepts either an Object:


let grantObjects = {

reader: {

        post: {

            'read:any': ['*', '!id]

        }

    },

    writer: {

        post: {

            'create:own': ['*'],

            'read:any': ['*'],

            'update:own': ['*'],

            'delete:own': ['*']

        }

    },

    editor: {

        post: {

            'create:any': ['*'],

            'read:any': ['*'],

            'update:any': ['*'],

            'delete:any': ['*']

        }

    }

}


const ac = new AccessControl(grantsObject);

Or an array:


let grantArray = [

  { role: 'reader', resource: 'post', action: 'read:any', attributes: '*, !id' },

  { role: 'writer', resource: 'post', action: 'read:any', attributes: '*' },

  { role: 'writer', resource: 'post', action: 'create:own', attributes: '*' },

  { role: 'writer', resource: 'post', action: 'update:own', attributes: '*' },

  { role: 'writer', resource: 'post', action: 'delete:own', attributes: '*' },

  { role: 'editor', resource: 'post', action: 'read:any', attributes: '*' },

  { role: 'editor', resource: 'post', action: 'create:any', attributes: '*' },

  { role: 'editor', resource: 'post', action: 'update:any', attributes: '*' },

  { role: 'editor', resource: 'post', action: 'delete:any', attributes: '*' },

]


const ac = new AccessControl(grantArray);

Example with Express.js

const ac = new AccessControl(grants);


router.get('/posts/:title', function (req, res, next) {

    const permission = ac.can(req.user.role).readAny('post');

    if (permission.granted) {

        Video.find(req.params.title, function (err, data) {

            if (err || !data) return res.status(404).end();

            res.json(permission.filter(data));

        });

    } else {

        res.status(403).end();

    }

});

Conclusion

We have shown how we can use AccessControl for authorization in a server-side application. We can also use it to authorize routes and UI elements in a client-side application, which can be done by using the same grant object for both the server and the client. AccessControl is one only library for implementing access control in Node.js. Node-casbin and CASL are also Node.js libraries for implementing access control.

Comments

Popular posts from this blog

How to use Ngx-Charts in Angular ?

Charts helps us to visualize large amount of data in an easy to understand and interactive way. This helps businesses to grow more by taking important decisions from the data. For example, e-commerce can have charts or reports for product sales, with various categories like product type, year, etc. In angular, we have various charting libraries to create charts.  Ngx-charts  is one of them. Check out the list of  best angular chart libraries .  In this article, we will see data visualization with ngx-charts and how to use ngx-charts in angular application ? We will see, How to install ngx-charts in angular ? Create a vertical bar chart Create a pie chart, advanced pie chart and pie chart grid Introduction ngx-charts  is an open-source and declarative charting framework for angular2+. It is maintained by  Swimlane . It is using Angular to render and animate the SVG elements with all of its binding and speed goodness and uses d3 for the excellent math functio...

Understand Angular’s forRoot and forChild

  forRoot   /   forChild   is a pattern for singleton services that most of us know from routing. Routing is actually the main use case for it and as it is not commonly used outside of it, I wouldn’t be surprised if most Angular developers haven’t given it a second thought. However, as the official Angular documentation puts it: “Understanding how  forRoot()  works to make sure a service is a singleton will inform your development at a deeper level.” So let’s go. Providers & Injectors Angular comes with a dependency injection (DI) mechanism. When a component depends on a service, you don’t manually create an instance of the service. You  inject  the service and the dependency injection system takes care of providing an instance. import { Component, OnInit } from '@angular/core'; import { TestService } from 'src/app/services/test.service'; @Component({ selector: 'app-test', templateUrl: './test.component.html', styleUrls: ['./test.compon...

How to solve Puppeteer TimeoutError: Navigation timeout of 30000 ms exceeded

During the automation of multiple tasks on my job and personal projects, i decided to move on  Puppeteer  instead of the old school PhantomJS. One of the most usual problems with pages that contain a lot of content, because of the ads, images etc. is the load time, an exception is thrown (specifically the TimeoutError) after a page takes more than 30000ms (30 seconds) to load totally. To solve this problem, you will have 2 options, either to increase this timeout in the configuration or remove it at all. Personally, i prefer to remove the limit as i know that the pages that i work with will end up loading someday. In this article, i'll explain you briefly 2 ways to bypass this limitation. A. Globally on the tab The option that i prefer, as i browse multiple pages in the same tab, is to remove the timeout limit on the tab that i use to browse. For example, to remove the limit you should add: await page . setDefaultNavigationTimeout ( 0 ) ;  COPY SNIPPET The setDefaultNav...