xpolicy

Fine-grained access control policy and enforcement for modern applications

Usage no npm install needed!

<script type="module">
  import xpolicy from 'https://cdn.skypack.dev/xpolicy';
</script>

README

XPolicy

CI Coverage Status Dependencies License

Fine-grained access control policy and enforcement for modern applications.

XPolicy implements the flexible attribute-based access control model, which encompasses most access control paradigms. The wide range of rules allows you to define and enforce extremely specific policies.

Table of Contents

Quick start

# Install with NPM
npm install xpolicy

# Install with Yarn
yarn add xpolicy
const xp = require('xpolicy');
const { GreaterOrEq, Less, In, StartsWith } = xp.rules;

// Define a policy to enforce.
const policy = new xp.Policy({
  id: 1,
  description: `Allow users and creators to view, like, and comment
    on public videos if their account age is between 0 and 365`,
  effect: xp.effects.Allow,
  subject: {
    username: Any(),
    role: In(['user', 'creator']),
  },
  action: In(['view', 'like', 'comment']),
  resource: StartsWith('videos/public'),
  context: {
    accountAge: And(GreaterOrEq(0), Less(365)),
  },
});

// Create an enforcer and add the policy to it.
const enforcer = new xp.Enforcer();
enforcer.addPolicy(policy);

// Define a desired operation.
const operation = new xp.Operation({
  subject: { username: 'cat', role: 'user' },
  action: 'like',
  resource: 'videos/public/cat-montage',
  context: { accountAge: 101 },
});

// Enforcer will determine whether the operation is allowed
// according to the defined policies.
console.log(enforcer.isAllowed(operation));
// true

Policy

A policy is a set of rules that you want to enforce. You can create and add multiple policies to be enforced.

It contains the following attributes:

Attribute Description
id Required: A unique identifier of the policy.
description Optional: A helpful description of the policy.
subject Optional: The allowed entity. Can either be a rule or object.
action Optional: The allowed action. Can either be a rule or object.
resource Optional: The allowed resource. Can either be a rule or object.
context Optional: The allowed context. Can either be a rule or object.
effect Required: The result if the conditions are met.
Can either be effects.Allow or effects.Deny.

Here is an example policy:

const policy = new xp.Policy({
  id: 1,
  description: `Allow an admin to create, read, update, and delete
    any resource except for permissions if their IP is 0.0.0.0.`,
  subject: Eq('admin'),
  action: In('create', 'read', 'update', 'delete'),
  resource: And(Any(), NotEq('permissions')),
  context: { ip: Eq('0.0.0.0') },
  effect: xp.effects.Allow,
});

See the rules section for more information about composing complex rules.

Enforcer

An enforcer enforces the given policies by deciding whether a desired activity adheres to the policies. Enforcer exposes two methods:

enforcer.addPolicy(yourPolicy)

  • Adds a Policy object to the enforcer. When later authorizing an operation,

enforcer.isAllowed(yourOperation)

  • Returns a boolean of whether the Operation object is allowed based on the enforced policies.

Here is an example usage of enforcer:

const enforcer = new xp.Enforcer();
enforcer.addPolicy(yourPolicy);
enforcer.addPolicy(anotherPolicy);

enforcer.isAllowed(attemptedOperation);

Operation

An operation is an attempted activity that needs to be authorized by the rules defined by one or more policies.

To do this, you construct a new Operation object and check it by calling enforcer.isAllowed(operation). The enforcer will return a boolean value dictating whether the given operation is allowed according to the policies.

Here is an example operation:

const operation = new xp.Operation({
  subject: 'user0',
  action: 'view',
  resource: 'video',
  context: { location: 'USA' },
});

Note that all the properties are optional. However, if the policy contains a property, the corresponding property must be present on the operation, or else it will be rejected.

For example, if the policy contains subject, then the operation must also contain subject, or else it is automatically rejected.

Rules

Rules allow you to impose conditions on the attributes of a desired operation. You can use rules on the subject, action, resource, and context of an operation.

The rules are exposed in the xp.rules object, which contains all of the below rules. For ease of use, you should destructure xp.rules and expose the rules you want to use:

const { Any, Eq, In, Greater, Contains } = xp.rules;

// Now you can call the function directly:
Any();
Greater(10);

Rules can be applied directly or within an object:

// The subject itself has to equal "admin".
subject: Eq('admin');
// The subject has to be an object with a "role" attribute
// that equals "admin".
subject: {
  role: Eq('admin');
}

Both are valid syntaxes and are be enforced accordingly. The choice depends on your use case and desired fidelity of control.

Constant rules

Constant rules always result in the same outcome.

Rule Description Valid Example
Any() Always allow any data "cats"Any()
None() Always deny any data Nothing will satisfy None()

* Note that None() does not have much practical use.

Composition rules

Composition rules allow you to combine and modify other rules to form more complex rules.

Rule Description
And(a, b) Both a and b must be true.
Or(a, b) Either a or b must be true.
Not(a) a must not be true.

Relational rules

Relational rules allow you to compare pieces of data.

Rule Description Valid Example
Eq(d) Must strictly equal d 202Eq(202)
NotEq(d) Must strictly not equal d 200NotEq(300)
Greater(n) Must be a number and be greater than n 201Greater(200)
Less(n) Must be a number and be less than n 200Less(201)
GreaterOrEq(n) Must be a number and be greater than or equal to n 200GreaterOrEq(200)
LessOrEq(n) Must be a number and be less than or equal to n 200LessOrEq(200)

Array rules

Array rules allow you to compare arrays with elements and arrays with arrays.

Rule Description Valid Example
In(a) Must satisfy an element of array a "zz"In(["yy", "zz"])
NotIn(a) Must not satisfy an element of array a "ww"NotIn(["yy", "zz"])
AllIn(a) All input elements must satisfy elements of array a ["zz", "yy"]AllIn(["yy", "zz"])

These array rules can be extremely powerful—they not only compare literals to literals but also literals to rules:

In([
  'admin',
  StartsWith('user'), // You can match a rule!
  { firstName: NotEq('hacker') }, // You can match an object with a rule!
]);

In the above In rule, all the following inputs would satisfy it: admin, user123, { firstName: 'goodperson' }.

String rules

String rules allow you to match a subset of a string with another string.

Rule Description Valid Example
StartsWith(s) Must start with the value s "yolo"StartsWith("yo")
EndsWith(s) Must end with the value s "yolo"EndsWith("lo")
Contains(s) Must contain the value s "yolo"Contains("ol")