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

you-shall-pass

v2.0.3

Published

You shall pass is an ACL module for Javascript

Downloads

9

Readme

Build Status Coverage Status npm NpmLicense Maintainability

You Shall Pass is yet another ACL module for Javascript applications designed to be used on ES6 or Typescript projects.

With its companion modules express-you-shall-pass, and koa-you-shall-pass, it aims to be able to replace both passport and node-acl on NodeJS APIs, but can also be used client side.

It was written while designing an API to manage a restaurant chain, which is consumed by clients with different and overlapping permissions: administration, digital signage, public smartphone apps, cash registers, ...

Like many others, it allows to check if a user has permissions to access resources and perform actions.

Unlike others:

  • It does not need a storage backend to store permissions: they will come from your own persistence layer.
  • Your permissions model will be easy to unit test.
  • It can give your detailed information on why you got allowed or not to perform actions and access resources.
  • It can be extended to load arbitrary restrictions associated with the permissions.
    • Permissions are not only either "yes" or "no". You can say "yes but only for ..." without creating many different roles.
    • It comes with two examples that allow to restrict models on collections, and fields on models.
    • You can teach it to alter your database queries to enforce permissions on lists with your SQL database.

As this module is security related:

  • No production dependencies: feel free to audit the code and pin a version in your package.json.
  • Dev-dependencies are limited to compilation and test tools.
  • It is strictly typed with Typescript.
  • It is fully unit tested.

How does it work

With you-shall-pass, there are no roles and resources, all of your permissions are defined in a single "Permission Graph".

For instance for a blog API, the graph should look like this:

                                    +-----------+
                                    | is_anyone |
                                    +-----+-----+
                                          |
                                 If token |
     If author flag              is valid |            If moderator flag
     is set in the database               v            is set in the database
     +-----------+               +--------+---------+           +--------------+
     | is_author +<--------------+ is_authenticated +---------->+ is_moderator |
     +-----+-----+               +--------+---------+           +-------+------+
           |                              |                             |
           |                    If author |                             |
    Always |                    of the    |             Always          |
           |                    article   |   +-------------------------+
           v                              v   v                         v
+----------+---------+           +--------+---+-----+        +----------+---------+
| can_create_article |           | can_edit_article |        | can_delete_article |
+--------------------+           +------------------+        +--------------------+

Usage

The permission graph

Todo: write documentation

const acl = new Acl(
    // Default permission
    'is_anyone',

    // Permission graph edges
    [
        {
            explain: "User carries a basic authentication token",
            from: 'is_anyone',
            to: 'is_authenticated',
            check: async ctx => {
                // Decode basic auth token.
                const [username, password] =
                    Buffer.from(ctx.token.split(' '), 'base64').toString().split(/:/);

                // Load user **into context**.
                // It will be available for all other check functions.
                ctx.user = await getUser(username);

                // true if allowed, false otherwise
                return ctx.user && bcrypt.verify(password, ctx.user.password);
            }
        },
        {
            explain: "User is a moderator",
            from: 'is_authenticated',
            to: 'is_moderator',
            check: async ctx => ctx.user.isModerator
        },
        {
            explain: 'Moderators can edit and delete all articles'
            from: 'is_moderator',
            to: ['can_edit_article', 'can_delete_article'],
        },
        {
            explain: "Authors can edit their own articles",
            from: 'is_authenticated',
            to: 'can_edit_article',
            check: async ctx => ctx.article.createdBy == ctx.user.email
        },
        [...]
    ]
);

Checking permissions

Todo: write documentation

// Can we reach the 'can_edit_article' permission **for this article and authentication token**
const result = await acl.check('can_edit_article', {
    token: 'Basic ZWxsaW90QGUtY29ycC5jb206YW5hcmNoeV9mdHc=',
    article: {id: 1, text: "I ❤ Javascript", createdBy: '[email protected]'}
});

if (result !== null) {
    // User is allowed to edit the article (result contains the current user because it was
    // loaded during the checks).
    assert.isNotNull(result.user);
}
else {
    // User does not have the 'can_edit_article' permission in this context.
}

Customizing "Permission denied" messages

A user can be allowed to perform an action because of many reasons. In the previous example, the permission to edit a given article can be reached either by the article author or by any moderator.

The same happens when a user is denied a permission: if a user is not allowed to edit a particular article, it is both because he is not a moderator, and because he is not the article's author.

By providing all nodes in the permission graph that were checked for a particular query, and the result of each check, the .explain() method provides insight about why a permission was granted or denied.

// Ask `you-shall-pass` details about last example.
const explanation = await acl.explain('can_edit_article', {
    token: 'Basic ZWxsaW90QGUtY29ycC5jb206YW5hcmNoeV9mdHc=',
    article: {id: 1, text: "I ❤ anarchy", createdBy: '[email protected]'}
});

// We can check why we were allowed to edit the article in the previous example.
explanation
    .filter(e => e.to == 'can_edit_article' && e.checkPassed)
    .map(e => e.explain);
> ['Authenticated users can edit their own article']

// We can also check paths that the acl checker tried, but which failed to reach the requested permission.
explanation
    .filter(e => !e.checkPassed)
    .map(e => e.explain);
> ['User is a moderator']

Using restrictions

Todo: write documentation

Code structure

When dealing with complex permission graphs, if can quickly become inconvenient to handle the list of edges in a single file.

Todo: write documentation

To do

  • [ ] Have meaningful errors when data is missing in the parameters (ie: when forgetting parameters, or attaching permissions to wrong nodes).
  • [ ] Write proper documentation.
  • [ ] Cache the paths between permissions.
  • [ ] More tests with restrictions examples.
  • [ ] Tests with explain feature.
  • [ ] Express and Koa middlewares, on others repos.
  • [ ] Set-up greenkeeper to keep dev dependencies up to date.