Skip to content

vhidvz/abacl

Repository files navigation

Attribute-Based Access Control Library

npm Coverage npm GitHub Gitter documentation Build, Test and Publish

The Attribute-Based Access-Control Library let you define five can access ability:

  • Who can? the answer is subject - Like RBAC a user can have multiple subjects.
  • How can it? the answer is action - You can define any actions you want (scoped).
  • What can? the answer is object - You can define all objects you want (scoped).
  • Where can? the answer is location - With IP and CIDR you can find the location of users.
  • When can it? the answer is time - objects availabilities with cron expression and a duration.

ABAC vs RBAC?

Question RBAC ABAC
Who can access? ✔️ With more options
How can operate? ✅ CRUD ✔️ With more options
What resource? ✅ Not Bad At All ✔️ More control on resource
Where user can do? ✔️ Supported by IP and CIDR
When user can do? ✔️ Supported by CRON
Best structure? Monolithic Apps PWA, Restful, GraphQL
Suitable for? Small and medium projects Medium and large projects

What's Scope?

  • look at carefully; scan.
  • assess or investigate something.

In this library, We scoped action, object and subject which means you can have more control over these attributes.

Note: if you want to have more control over the scoped attributes send at most three character of the first subject, action, or object for example so or sub|obj it means subject and object are in strict mode.

Quick Start Guide

installation

npm install --save abacl

Usage and Dangling

Define your user policies as a json array (so you can store it in your database):

import { Policy } from 'abacl';

enum Role {
  Admin = 'admin',
  User = 'user',
  Guest = 'guest',
  Manager = 'manager',
}

const policies: Policy<Role>[] = [
  {
    subject: Role.Admin,
    action: 'any',
    object: 'all',
  },
  {
    subject: Role.Guest,
    action: 'read',
    object: 'article:published',
  },
  {
    subject: Role.Guest,
    action: 'create:own',
    object: 'article:published',
  },
  {
    subject: Role.Manager,
    action: 'any',
    object: 'article',
  },
  {
    subject: Role.User,
    action: 'create:own',
    object: 'article',
    field: ['*', '!owner'],
    location: ['192.168.2.10', '192.168.1.0/24'],
    time: [
      {
        cron_exp: '* * 7 * * *', // from 7 AM
        duration: 9 * 60 * 60, // for 9 hours
      },
    ],
  },
  {
    subject: Role.User,
    action: 'read:own',
    object: 'article',
  },
  {
    subject: Role.User,
    action: 'read:shared',
    object: 'article',
    filter: ['*', '!owner'],
  },
  {
    subject: Role.User,
    action: 'delete:own',
    object: 'article',
  },
  {
    subject: Role.User,
    action: 'update:own',
    object: 'article',
    field: ['*', '!id', '!owner'],
  },
];

Article and User definition objects:

const user = {
  id: 1,
  subject: Role.User,
  ip: '192.168.1.100',
};

const article = {
  id: 1,
  owner: 'user1',
  title: 'title',
  content: 'content',
};

Create a new access control object, then get the permission grants:

import AccessControl from 'abacl';

// The `strict` `AccessControlOption` control the scoped functionality
// default strict value is true, you can change it on the `can` method

const ac = new AccessControl(policies, { strict: false });
const permission = await ac.can([user.subject], 'read', 'article');

// change strict mode dynamically, Example:
// const strictPermission = await ac.can([user.subject], 'read', 'article', { strict: true });

/**
 *   it('should change strict mode dynamically', () => {
 *     const ac = new AccessControl(policies, { strict: true });
 *
 *     expect(await ac.can([Role.User], 'read', 'article:published').granted).toBeFalsy();
 *
 *     // After changing strict mode
 *     expect(await ac.can([Role.User], 'read', 'article:published', { strict: false }).granted).toBeTruthy();
 *   });
 *
 * */

if (permission.granted) {
  // default scope for action and object is `any` and `all`

  if (permission.has({ action: 'read:own' })) {
    // user has read owned article objects
  }

  if (permission.has({ action: 'read:shared' })) {
    // user can access shared article objects
  }

  if (permission.has({ object: 'article:published' })) {
    // user can access shared article objects
  }

  // do something ...

  // return filtered data based on the permission
  const response = await permission.filter(article);
}

Time and location access check example:

import { AccessControl, Permission } from 'abacl';

// default `strict` value is true
const ac = new AccessControl(policies, { strict: true });

const permission = await ac.can([user.subject], 'create', 'article', {
  callable: (perm: Permission) => {
    return perm.location(user.ip) && perm.time();
  },
});

if (permission.granted) {
  const inputData = await permission.field(article);

  // the `inputData` has not `owner` property
  // do something and then return results to user
}

Related Project

Thanks a lot

accesscontrol - Role and Attribute based Access Control for Node.js

CASL is an isomorphic authorization JavaScript library which restricts what resources a given user is allowed to access.

License

MIT