/lilu-js

Primary LanguageJavaScriptMIT LicenseMIT

Image of Yaktocat

npm i @andrew_l/lilu

LiLu

Attribute-based access control with some sugar.

Permissions example

permissions:
- title: Any User
  actions: ['order.view', 'order.edit']
  rules:
  - title: 'Owned'
    operation: AND
    conditions:
    - order.owner == user.id

- title: Delete Order
  actions: ['order.delete']
  rules:
  - title: 'Owned'
    operation: AND
    conditions:
    - order.owner == user.id
  - title: 'Recently Placed'
    operation: AND
    conditions:
    - order.placedAt == {{ env.now - (1000 * 60 * 60 * 24 * 5) }}

Usage

import { Lilu } from 'lilu';

const user = {
  id: 1,
  name: 'Andrew L.',
  email: 'andrew.io.dev@gmail.com',
  role: ['ROLE_USER']
};

const order = {
  title: 'Netflix Subscription',
  owner: 1,
  placedAt: new Date(),
  items: [1, 2, 3, 4]
};

const lilu = new Lilu({
  strict: false,
  permissions: [{
    title: 'Review Order',
    actions: ['order.view'],
    rules: [{
      title: 'Owned',
      operation: 'AND',
      conditions: [
        'order.owner == user.id',
      ]
    }]
  }]
});

lilu.granted('order.view', { user, order }, function(err, result) {
  if (err) {
    console.warn('Failed to process lilu access', err);
    return;
  }

  if (result.passed) {
    console.log('Yahu!!! I have access to review order.');
  } else {
    console.log('Oops!! I don\'t have access to review order.');
  }
});

To investigate more use cases, please check examples folder.

What is strict mode?

In this mode the module makes expression validation before granted function be called. Also strict mode requires to pass all variables in context that used in rules conditions.

Condition operators

Description
> Greater than
=> Greater or equal to
< Less than
<= Less or equal to
== Equal to
!= Not equal to
in Value equals any value in the specified array

Context variables

Each time when you call granted function, you can pass the context of execution. For example we need to make sure that user has admin role before delete the order.

1 • Our case have next permission rules.

const permissions = {
  strict: false,
  permissions: [{
    title: 'Remove Order',
    actions: ['order.remove'],
    rules: [{
      title: 'Admin Access',
      operation: 'AND',
      conditions: [
        'user.roles in "ROLE_ADMIN"',
      ]
    }]
  }]
}

2 • We have next context.

const context = {
  user: {
    name: 'Andrew',
    roles: ["ROLE_USER"]
  }
};

3 • Now we can use context to check access.

const lilu = new Lilu({
  strict: false,
  permissions: permissions /* from step 1 */,
});

lilu.granted('order.remove', context /* from step 2 */, function(err, result) {
  if (err) {
    console.warn('Failed to process lilu access', err);
    return;
  }

  if (result.passed) {
    console.log('Yahu!!! I have access to delete order.');
  } else {
    console.log('Oops!! I don\'t have access to delete order.');
  }
});

Rules operation

Our rules can be checked by follow operations:

Description
AND Each rule conditions must be true
OR Any of rule conditions must be true

Rules conditions

It's pretty simple js-like expression parser inside, just to give you ability to use plain string.

Type Description Example
Literal values Support literal values like number and boolean. user.disabled == true
String Should be in double quotes. user.name == "some text"
Array Array expression should be in quotes. Also it support variables inside. user.roles in ["ROLE_USER", "ROLE_MANAGER"]
Expression You also can use native javascript expression in two curly quotes. user.createdAt > {{ Date.now() - 1000 * 60 * 60 * 24 }}