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

@trufflehq/perms

v1.0.3

Published

A generic permissions library.

Downloads

22

Readme

@trufflehq/perms

Introduction

This package provides a simple, yet extensible way to manage permissions in your application.

Getting started

Installation

npm install @trufflehq/perms

or

yarn add @trufflehq/perms

Basic usage

You can start evaluating permissions with minimal configuration. The following example shows how to evaluate whether or not a user has permission to view videos on a website.

import { PermissionsProcessor, permission } from "../src";

// some users in our database
const user1 = {
  id: 1,
  name: "John Doe",

  // this user has the "videos.view" permission
  perms: [
    { action: "videos.view" }
  ]
}

const user2 = {
  id: 2,
  name: "Jack Smith",

  // this user does not have the "videos.view" permission
  perms: []
}

// initialize the permissions processor
const permissionsProcessor = new PermissionsProcessor();

// check if user1 has permission to view videos
permissionsProcessor.evaluate('videos.view', user1.perms) // true

// check if user2 has permission to view videos
permissionsProcessor.evaluate('videos.view', user2.perms) // false

Advanced usage

Custom permission evaluators

You can define custom permission evaluators to handle more complex permission logic. For example, you may want to define a permission evaluator that checks if a user has access to a certain document.

You can define a custom permission evaluator with the permissionEvaluate function and then register it with the PermissionsProcessor instance.

const permissionsProcessor = new PermissionsProcessor();

// define a custom permission evaluator
const evaluator = permissionEvaluate({
  action: 'doc.read',

  // a custom function that only allows users with a permission
  // that matches the docId of the document they are trying to read
  hasPermission: (permission, context) => {
    return permission.params.docId === context.doc.docId;
  }
});

// register the custom permission evaluator
permissionsProcessor.register(evaluator);

// theoretical documents
const doc1 = { id: 1 };
const doc2 = { id: 2 };

// the perms for a theoretical user
const userPermissions = [
  {
    action: 'doc.read',

    // the params in this permission specify
    // that the user has permission to read
    // the document with id 1
    params: { docId: 1 }
  },
];

// check if the user has permission to read doc1
permissionsProcessor.evaluate(
  'doc.read',
  userPermissions,

  // we want to check if the user has permission
  // to read doc1, so we pass it into the context obj
  { doc: doc1 }
); // true

// check if the user has permission to read doc2
permissionsProcessor.evaluate('doc.read', userPermissions, { doc: doc2 }); // false

Check out the full example in examples/2-custom-eval.ts.

Setting the global fallback evaluator

You can set a global fallback evaluator that gets used if by default if a user permission fails all other permissions checks.

const permissionsProcessor = new PermissionsProcessor();
processor.globalFallback = permissionEvaluate("superAdmin");

// these are perms that a theoretical user has.
// this user will have permission regardless of
// which action we're checking for
const userPermissions = [
  permission("superAdmin"),
]

Check out the full example in examples/6-global.ts.

Inheritance

You can define permissions that inherit from other permissions. This is useful when you want to assume that a user has permission to do something if they have a higher level permission. There are two ways to define inherited permissions: PermissionEvaluate chains and PermissionEvaluate trees.

PermissionEvaluate chains

PermissionEvaluate chains are a simple way to define inherited permissions. You can define a chain of permissions that inherit from each other with the permChain function. The following example shows how to define a chain of permissions that inherit from each other.

const processor = new PermissionsProcessor();

processor.register(
  permissionEvaluateChain([ permissionEvaluate("video.view"), permissionEvaluate("video.rename"), permissionEvaluate("video.delete") ])
);

const userPermissions = [
  permission("video.rename"),
];

const testPermission = (permission: string) => {
  console.log(permission, processor.evaluate(permission, userPermissions));
};

testPermission("video.view");
testPermission("video.rename");
testPermission("video.delete");

// output:
// video.view true
// video.rename true
// video.delete false

When using the permissionEvaluateChain function, permissions are ranked ascending from left to right, with the leftmost permission being the lowest level permission, and the rightmost permission being the highest level permission. If a user has the rightmost permission, they will also have all of the permissions to the left of it.

In this example, "video.view" is the lowest level permission. If a user has the "video.view" permission, they will be able to view the video, but not rename or delete it. However, if a user has the "video.delete" permission, they will also be able to view and rename the video.

Check out the full example in examples/3-permission-chain.ts.

PermissionEvaluate trees

If you have a more complex hierarchy of permissions, you can define a PermissionEvaluate tree. A PermissionEvaluate tree is similar to a chain, but each node in the tree can have multiple children. The following example shows how you could define a tree for a document.

Basic tree

processor.register(
  permissionEvaluateTree({
    self: permissionEvaluate("doc.all"),
    children: [
      { self: permissionEvaluate("doc.read") },
      {
        self: permissionEvaluate("doc.write"),
        children: [
          { self: permissionEvaluate("doc.create") },
          { self: permissionEvaluate("doc.update") },
          { self: permissionEvaluate("doc.delete") },
        ],
      },
    ],
  })
);

In this example, the "doc.all" permission is the highest level permission. If a user has the "doc.all" permission, they will also have all of the permissions in the tree. If a user has the "doc.write" permission, they will also have the "doc.create", "doc.update", and "doc.delete" permissions.

Check out the full example in examples/4-permission-tree.ts.

One thing to keep in mind, is that permissions are evaluated from the bottom of the tree to the top. This means that your permissions tree may not look how you'd expect. examples/5-collection.ts demonstrates an example where there can be multiple documents in a collection. If a user has a particular permission for a collection, they will also have that permission for all of the documents in the collection.

The permissions tree for that example looks like this:

Collection tree

Gotchas

Duplicate PermissionEvals

You cannot register duplicate PermissionEvals. One case you might accidentally do this is if you are using chains. For example, the following code will throw an error:

const processor = new PermissionsProcessor();

processor.register(
  permissionEvaluateChain([
    permissionEvaluate("doc.read"),
    permissionEvaluate("doc.all"),
  ])
);

processor.register(
  permissionEvaluateChain([
    permissionEvaluate("doc.write"),
    permissionEvaluate("doc.all"),
  ])
);

// throws an error because "doc.all" is registered twice

In this case, you'd want to use a PermissionEvaluate tree instead.