npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

rbac-engine

v1.2.0

Published

Role-based access control engine with policy-based permissions

Readme

RBAC Engine

A flexible and powerful role-based access control (RBAC) system with policy-based permissions for Node.js applications. This library provides a robust way to manage permissions across your application, inspired by AWS IAM.

Features

  • Role-Based Access Control: Assign roles to users and define permissions at the role level
  • Policy-Based Permissions: Create detailed policies using JSON format
  • Flexible Permissions: Support for wildcard patterns and conditional access
  • Time-Based Policies: Define policies with start and end dates for temporary access
  • DynamoDB Integration: Built-in support for Amazon DynamoDB
  • Extensible Architecture: Easily extend to support other database systems

Table of Contents

Installation

npm install rbac-engine

Dependencies

  • Node.js 16.0.0 or higher
  • For DynamoDB support:
    • @aws-sdk/client-dynamodb
    • @aws-sdk/lib-dynamodb

Quick Start

1. Initialize the Access Control System

import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { AccessControl, DynamoDBRepository } from "rbac-engine";

// Create a DynamoDB client
const dynamoClient = new DynamoDBClient({ region: "us-east-1" });

// Initialize the access control system with DynamoDB repository
const accessControl = new AccessControl(dynamoClient, DynamoDBRepository);

// Create necessary tables
await accessControl.init();

2. Create Roles

// Create admin role
const adminRole = await accessControl.createRole({
  id: "role-admin-123",  // Or use UUID: uuidv4()
  name: "Admin"
});

// Create editor role
const editorRole = await accessControl.createRole({
  id: "role-editor-456",
  name: "Editor"
});

3. Define Policies

import { Effect, PolicyDocument } from "rbac-engine";

// Admin policy - can do everything
const adminPolicyDocument: PolicyDocument = {
  Version: "2023-11-15",
  Statement: [
    {
      Effect: Effect.Allow,
      Action: ["*"],
      Resource: ["*"]
    }
  ]
};

// Create the policy
const adminPolicy = await accessControl.createPolicy({
  id: "policy-admin-123",
  document: adminPolicyDocument
});

// Editor policy - can only read, create and update
const editorPolicyDocument: PolicyDocument = {
  Version: "2023-11-15",
  Statement: [
    {
      Effect: Effect.Allow,
      Action: ["read", "create", "update"],
      Resource: ["document/*"]
    }
  ]
};

const editorPolicy = await accessControl.createPolicy({
  id: "policy-editor-456",
  document: editorPolicyDocument
});

Alternative: Using Builder Pattern

You can also create policies using the fluent builder pattern:

import { PolicyBuilder, StatementBuilder } from 'rbac-engine';

// Admin policy using builder pattern
const adminPolicy = await accessControl.createPolicy(
  new PolicyBuilder('policy-admin-123')
    .version('2023-11-15')
    .allow(['*'])
    .on(['*'])
);

// Editor policy with multiple statements using builder pattern
const editorPolicy = await accessControl.createPolicy(
  new PolicyBuilder('policy-editor-456')
    .version('2023-11-15')
    .statement(
      new StatementBuilder()
        .allow(['read', 'create', 'update'])
        .on(['document/*'])
    )
    .statement(
      new StatementBuilder()
        .deny(['delete'])
        .on(['document/critical/*'])
    )
);

4. Attach Policies to Roles

await accessControl.attachPolicyToRole(adminPolicy.id, adminRole.id);
await accessControl.attachPolicyToRole(editorPolicy.id, editorRole.id);

5. Create Users and Assign Roles

// Create users
const adminUser = await accessControl.createUser({
  id: "user-admin-123",
  name: "Admin User"
});

const editorUser = await accessControl.createUser({
  id: "user-editor-456",
  name: "Editor User"
});

// Assign roles to users
await accessControl.assignRoleToUser(adminUser.id, adminRole.id);
await accessControl.assignRoleToUser(editorUser.id, editorRole.id);

6. Check Permissions

// Check if admin can delete documents
const adminCanDelete = await accessControl.hasAccess(
  adminUser.id,
  "delete",
  "document/123"
);
console.log(`Admin can delete document: ${adminCanDelete}`); // true

// Check if editor can update documents
const editorCanUpdate = await accessControl.hasAccess(
  editorUser.id,
  "update",
  "document/123"
);
console.log(`Editor can update document: ${editorCanUpdate}`); // true

// Check if editor can delete documents
const editorCanDelete = await accessControl.hasAccess(
  editorUser.id,
  "delete",
  "document/123"
);
console.log(`Editor can delete document: ${editorCanDelete}`); // false

Core Concepts

Users

A User represents an individual accessing your system. Users can have roles assigned to them and policies attached directly.

export interface User {
    id: string;
    name: string;
    roles?: string[];
    policies?: string[];
}

Roles

Roles are collections of permissions that can be assigned to users. Assigning roles to users makes permission management easier as multiple users can share the same role.

export interface Role {
    id: string;
    name: string;
    policies?: string[];
}

Policies

Policies define what actions are allowed or denied on what resources. Each policy contains one or more statements that specify the permissions.

export interface Policy {
    id: string;
    document: PolicyDocument;
}

export interface PolicyDocument {
    Version: string;
    Statement: PolicyStatement[];
}

export interface PolicyStatement {
    Effect: Effect; // 'Allow' or 'Deny'
    Action: string[]; // Actions to allow/deny
    Resource: string[]; // Resources on which actions are allowed/denied
    Condition?: Record<string, any>; // Optional conditions
    StartDate?: string; // Optional ISO format date string for when the policy becomes active (UTC)
    EndDate?: string; // Optional ISO format date string for when the policy expires (UTC)
}

export enum Effect {
    Allow = 'Allow',
    Deny = 'Deny'
}

Advanced Features

Wildcard Support

You can use wildcards in both Action and Resource fields:

const policyDocument: PolicyDocument = {
  Version: "2023-11-15",
  Statement: [
    {
      Effect: Effect.Allow,
      Action: ["read*"], // Matches read, readAll, readOne, etc.
      Resource: ["document/*"] // Matches all documents
    }
  ]
};

Conditional Access

Add conditions to your policies to provide even more granular control:

const conditionalPolicy: PolicyDocument = {
  Version: "2023-11-15",
  Statement: [
    {
      Effect: Effect.Allow,
      Action: ["read"],
      Resource: ["sensitive-document/*"],
      Condition: { department: "finance" }
    }
  ]
};

// Check if user can access with a specific context
const canAccess = await accessControl.hasAccess(
  userId,
  "read",
  "sensitive-document/budget",
  { department: "finance" } // Only users with finance department can access
);

Time-Based Policies

Create policies that are only active during specific time periods by setting optional StartDate and/or EndDate fields. This is useful for temporary access grants, seasonal permissions, or scheduled policy changes.

Dates should be provided in ISO format strings and are interpreted as UTC timestamps:

const temporaryAccessPolicy: PolicyDocument = {
  Version: "2023-11-15",
  Statement: [
    {
      Effect: Effect.Allow,
      Action: ["read", "update"],
      Resource: ["project/quarterly-report"],
      StartDate: "2025-01-01T00:00:00Z", // Active from January 1st, 2025
      EndDate: "2025-03-31T23:59:59Z"    // Until March 31st, 2025
    }
  ]
};

// This policy will only grant access during Q1 2025
// Outside that date range, permissions will not be granted even if the policy is attached

You can combine time-based constraints with conditions for even more granular control:

const contractorPolicy: PolicyDocument = {
  Version: "2023-11-15",
  Statement: [
    {
      Effect: Effect.Allow,
      Action: ["read", "update"],
      Resource: ["project/*"],
      Condition: { contractorId: "C12345" },
      StartDate: "2025-01-01T00:00:00Z", // Contract start date
      EndDate: "2025-06-30T23:59:59Z"    // Contract end date
    }
  ]
};

API Reference

AccessControl

The main class for all access control operations.

Constructor

constructor(client: T, repositoryConstructor: RepositoryConstructor<T>)
  • client: Database client (e.g., DynamoDBClient)
  • repositoryConstructor: Constructor for the repository implementation (e.g., DynamoDBRepository)

Methods

// Initialization
async init(): Promise<void>

// User Management
async createUser(user: User): Promise<User>
async getUser(userId: string): Promise<User>

// Role Management
async createRole(role: Role): Promise<Role>
async getRole(roleId: string): Promise<Role>
async updateRole(role: Role): Promise<Role>
async deleteRole(roleId: string): Promise<void>

// Role Assignment
async assignRoleToUser(userId: string, roleId: string): Promise<User>
async removeRoleFromUser(userId: string, roleId: string): Promise<void>

// Policy Management
async createPolicy(policy: Policy): Promise<Policy>
async updatePolicy(policy: Policy): Promise<Policy>
async deletePolicy(policyId: string): Promise<void>

// Policy Attachment
async attachPolicyToRole(policyId: string, roleId: string): Promise<void>
async attachPolicyToUser(policyId: string, userId: string): Promise<void>
async detachPolicyFromRole(policyId: string, roleId: string): Promise<void>
async detachPolicyFromUser(policyId: string, userId: string): Promise<void>

// Policy Retrieval
async getUserPolicies(userId: string): Promise<Policy[]>
async getRolePolicies(roleId: string): Promise<Policy[]>

// Access Control
async hasAccess(userId: string, action: string, resource: string, context?: Record<string, any>): Promise<boolean>

Builder Pattern API

The RBAC Engine supports both traditional object-based policy creation and a modern builder pattern API. The builder pattern provides a fluent, intuitive way to create policies while maintaining full backward compatibility.

PolicyBuilder

Use PolicyBuilder for creating policies with a fluent API:

import { PolicyBuilder, StatementBuilder } from 'rbac-engine';

// Simple single-statement policy
const simplePolicy = new PolicyBuilder('read-documents')
  .version('2023-11-15')
  .allow(['read', 'list'])
  .on(['document/*'])
  .when({ department: 'engineering' })
  .build();

// Complex multi-statement policy
const complexPolicy = new PolicyBuilder('complex-permissions')
  .version('2023-11-15')
  .statement(
    new StatementBuilder()
      .allow(['read', 'write'])
      .on(['project/*'])
      .when({ role: 'developer' })
      .activeFrom('2025-01-01T00:00:00Z')
      .activeUntil('2025-12-31T23:59:59Z')
  )
  .statement(
    new StatementBuilder()
      .deny(['delete'])
      .on(['project/production/*'])
  )
  .build();

StatementBuilder

Create individual policy statements with the StatementBuilder:

import { StatementBuilder } from 'rbac-engine';

const statement = new StatementBuilder()
  .allow(['read', 'write'])  // or .deny(['delete'])
  .on(['resource/*'])        // Resources to apply to
  .when({ dept: 'eng' })     // Optional conditions
  .activeFrom('2025-01-01T00:00:00Z')  // Optional start date
  .activeUntil('2025-12-31T23:59:59Z') // Optional end date
  .build();

Builder Methods

PolicyBuilder Methods

  • version(version: string) - Set the policy document version
  • allow(actions: string[]) - Add an allow statement (simple mode)
  • deny(actions: string[]) - Add a deny statement (simple mode)
  • on(resources: string[]) - Set resources for simple mode statement
  • when(conditions: object) - Set conditions for simple mode statement
  • activeFrom(date: string) - Set start date for simple mode statement
  • activeUntil(date: string) - Set end date for simple mode statement
  • statement(statement: StatementBuilder) - Add a statement (complex mode)
  • addStatements(statements: StatementBuilder[]) - Add multiple statements
  • build() - Build and validate the final Policy object

StatementBuilder Methods

  • allow(actions: string[]) - Set effect to Allow with actions
  • deny(actions: string[]) - Set effect to Deny with actions
  • on(resources: string[]) - Set resources
  • when(conditions: object) - Set conditions
  • activeFrom(date: string) - Set start date
  • activeUntil(date: string) - Set end date
  • build() - Build and validate the final PolicyStatement

Integration with AccessControl

The AccessControl.createPolicy() method accepts both Policy objects and PolicyBuilder instances:

// Traditional approach (still fully supported)
const traditionalPolicy: Policy = {
  id: 'traditional-policy',
  document: {
    Version: '2023-11-15',
    Statement: [
      {
        Effect: Effect.Allow,
        Action: ['read'],
        Resource: ['document/*']
      }
    ]
  }
};

// Builder approach
const builderPolicy = new PolicyBuilder('builder-policy')
  .allow(['read'])
  .on(['document/*']);

// Both work with AccessControl
await accessControl.createPolicy(traditionalPolicy);
await accessControl.createPolicy(builderPolicy);

Validation

Builder validation occurs only when calling .build(), providing detailed error messages:

try {
  const policy = new PolicyBuilder('invalid-policy')
    .allow(['read'])
    // Missing .on() call
    .build();
} catch (error) {
  console.log(error.message); // "Invalid policy configuration"
  console.log(error.details);  // Array of specific validation errors
}

Complete Examples

The RBAC Engine comes with comprehensive examples demonstrating all features:

📋 Comprehensive Example (Recommended)

See examples/comprehensive-example.ts for a complete demonstration of all library features including:

  • Basic RBAC setup with roles, policies, and users
  • Traditional vs Builder Pattern approaches
  • Time-based policies with date constraints
  • Conditional access with context
  • Wildcard patterns and complex permissions
  • Direct policy attachment to users
  • Advanced multi-statement policies

Extending the Library

Creating Custom Repository Implementations

You can extend the library to work with other databases by implementing the IBaseRepository interface:

  1. Create a new repository class that implements the IBaseRepository interface
  2. Pass your custom repository constructor to the AccessControl constructor
import { AccessControl, IBaseRepository } from "rbac-engine";
import { Pool } from "pg";

// Example for PostgreSQL
class PostgresRepository implements IBaseRepository {
  constructor(private pool: Pool) {
    // Initialize your repository with the database connection
  }
  
  // Implement all required methods from IBaseRepository interface
  async createUser(user: User): Promise<User> {
    // PostgreSQL implementation
  }
  
  // ... implement all other required methods
}

// Then use it
const pgPool = new Pool(pgConfig);
const accessControl = new AccessControl(pgPool, PostgresRepository);

NPM Package Information

This package is available on npm and can be installed using npm or yarn:

npm install rbac-engine

The package works with Node.js 16.0.0 and above.

Best Practices

  1. Use UUIDs for IDs: Generate unique IDs for users, roles, and policies using a library like uuid
  2. Design Fine-grained Permissions: Create specific policies rather than overly broad ones
  3. Principle of Least Privilege: Give users the minimum permissions needed to perform their tasks
  4. Separate Roles by Function: Create roles based on job functions or responsibilities
  5. Audit Regularly: Periodically review role assignments and permissions

License

MIT

Author

Prudhvi Reddy Vemireddy