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 🙏

© 2024 – Pkg Stats / Ryan Hefner

permissionless.js

v1.0.9

Published

Permissionless.js is a Permissions handler for node projects, it can be used via frontend and backend services.

Downloads

169

Readme

🪪 Permissionless.js: Role-Based Access Control for Node.js Projects

NPM Downloads NPM Collaborators Static Badge NPM Type Definitions Codacy Badge

Permissionless is a powerful and extensible TypeScript library designed to manage user roles and permissions in your application. It offers features such as role inheritance, wildcard permissions, contextual checks, and dynamic configuration updates. This was first created in-house for JMServices Pro, and made open source.

Key Features

  • 👑 Role Inheritance: Define hierarchical roles where permissions are inherited from parent roles.
  • 👤 User-Specific Overrides: Grant or deny specific permissions to individual users.
  • 🔍 Contextual Permissions: Check permissions with added context (e.g., read:articles).
  • 🌟 Wildcard Matching: Use wildcards for permissions like read:* or write:docs.*.
  • Dynamic Configuration: Automatically reload changes to .permissionless.json or fetch from APIs.
  • 🚀 Caching for Performance: Built-in caching to optimize repeated permission checks.
  • 🏃 Runtime Compatibility: Works seamlessly with Bun, Deno & Node.js for maximum flexibility across different JavaScript runtimes.

Installation

Install the package using npm or yarn:

npm install permissionless.js
yarn add permissionless.js

Getting Started

1. Create a Configuration File

Define your roles, permissions, and user-specific overrides in .permissionless.json:

{
  "roles": {
    "admin": {
      "permissions": ["read:*", "write:*", "delete:*"],
      "inherits": ["editor"]
    },
    "editor": {
      "permissions": ["read:articles", "write:articles"]
    },
    "viewer": {
      "permissions": ["read:articles"]
    }
  },
  "users": {
    "123": {
      "permissions": ["read:restricted-docs"],
      "denies": ["write:articles"]
    }
  }
}

2. Initialize Permissionless

Import and initialize the Permissionless class in your application:

// CommonJS
const { Permissionless } = require('permissionless.js');
const permissions = new Permissionless();

// OR using ES Modules
import Permissionless from 'permissionless.js';
const permissions = new Permissionless();

3. Check Permissions

To check if a user has a specific permission, use the hasPermission method:

const user = { id: '123', role: 'editor', ...restOfUser };

if (permissions.hasPermission(user, 'read', 'articles')) {
  console.log('User has permission');
} else {
  console.error('User does NOT have permission');
}

Advanced Features

Role Inheritance

Define roles with inherited permissions. In the example above, admin inherits all permissions from editor. You can specify multiple roles in the inherits array.

User-Specific Overrides

Grant or deny specific permissions to individual users. For example:

"users": {
  "123": {
    "permissions": ["read:restricted-docs"], // Extra permissions for this user
    "denies": ["write:articles"]            // Explicitly deny writing articles
  }
}

Wildcard Permissions 🪪

Define generic permissions using *. For instance:

  • read:* grants read access to all resources.
  • write:articles.* grants write access to all sub-resources of articles.

Contextual Permissions

Add context to permissions checks. Example:

permissions.hasPermission(user, 'read', 'articles');
permissions.hasPermission(user, 'read', 'restricted-docs');

Dynamic Configuration Reload

Permissionless watches for changes in .permissionless.json and reloads the configuration automatically. You can also fetch the configuration from an API:

await permissions.loadConfigFromApi('https://example.com/permissions.json');

Add or Modify Roles Programmatically

You can add or modify roles and permissions dynamically:

permissions.addRole('superadmin', ['manage:users'], ['admin']);
permissions.addPermissionToRole('editor', 'edit:comments');

API Reference

constructor(configFilePath?: string)

  • Description: Initializes Permissionless with the given configuration file.
  • Default: .permissionless.json

hasPermission(user: User, permission: string, context?: string): boolean

  • Description: Checks if a user has the specified permission, optionally within a context.

getPermissionsForRole(role: string): string[]

  • Description: Retrieves all permissions for a specified role, including inherited ones.

addRole(roleName: string, permissions: string[], inherits?: string[]): void

  • Description: Adds a new role with optional inheritance from other roles.

addPermissionToRole(roleName: string, permission: string): void

  • Description: Adds a new permission to an existing role.

clearCache(): void

  • Description: Clears the internal cache of permissions.

loadConfigFromApi(apiUrl: string): Promise<void>

  • Description: Loads configuration from an external API URL.

Utility Methods

listRoles(): string[]

  • Description: Returns a list of all role names defined in the configuration.

listUsers(): string[]

  • Description: Returns a list of all user IDs defined in the configuration.

hasRole(roleName: string): boolean

  • Description: Checks if a role exists in the configuration.

checkMultiplePermissions(user: User, permissions: string[], context?: string): boolean

  • Description: Checks if a user has ALL of the specified permissions. Optionally checks within a context.

checkAnyPermission(user: User, permissions: string[], context?: string): boolean

  • Description: Checks if a user has ANY of the specified permissions. Optionally checks within a context.

Example Usage

Basic Permission Check

const user = { id: '456', role: 'viewer', ...restOfUserConfig };

if (permissions.hasPermission(user, 'read', 'articles')) {
  console.log('Viewer can read articles');
} else {
  console.error('Viewer cannot read articles');
}

Adding a New Role

permissions.addRole('moderator', ['moderate:comments'], ['viewer']);

Dynamic Permissions Update

permissions.addPermissionToRole('viewer', 'read:docs');

Advanced Wildcard and Contextual Matching

const user = { id: '789', role: 'editor' };

if (permissions.hasPermission(user, 'read', 'articles.section1')) {
  console.log('Editor can read section 1 of articles');
}

Configuration Reload

File Watcher

Permissionless automatically reloads .permissionless.json when changes are detected. No additional setup is needed for this feature.

Load Config from API

You can dynamically load configuration from an external API:

await permissions.loadConfigFromApi('https://example.com/permissions');

Performance Optimizations

  • Caching: Permissionless uses in-memory caching for permission calculations. Clear the cache when roles or permissions are updated:

    permissions.clearCache();
  • Lazy Loading: Role permissions are calculated and cached only when accessed.


Error Handling

  • Role Not Found: If a role is missing in the configuration:

    Error: Role [roleName] not found
  • Circular Inheritance: If roles inherit in a loop:

    Error: Circular inheritance detected in role: [roleName]
  • Invalid User Role: If a user has an undefined role:

    Error: Role [user.role] not found

License

Permissionless is distributed under the MIT License.


Contribution

Contributions are welcome! Feel free to open issues or submit pull requests to improve Permissionless.