Git Product home page Git Product logo

easy-rbac's Introduction

easy-rbac

Promise based HRBAC (Hierarchical Role Based Access Control) implementation for Node.js

NB! Important changes with v3

v3 is a rewrite of the library as such there are important changes:

  • Callbacks are no longer supported
  • Promise rejection will happen on error, otherwise boolean result will be in resolve handler
  • As of v3.2 Node >=v10.x is required

Installation

npm install easy-rbac

Test

npm test

Initialization

Require and create rbac object.

const RBAC = require('easy-rbac');
const rbac = new RBAC(opts);

Or use create function

const rbac = require('easy-rbac').create(opts);

Options

Options for RBAC can be either an object, function returning a promise or a promise

The expected configuration object example:

{
  user: { // Role name
    can: [ // list of allowed operations
      'account', 
      'post:add', 
      { 
          name: 'post:save',
          when: async (params) => params.userId === params.ownerId
      },
      'user:create',
      {
        name: 'user:*',
        when: async (params) => params.id === params.userId
      }
    ]
  },
  manager: {
    can: ['post:save', 'post:delete', 'account:*'],
    inherits: ['user']
  },
  admin: {
    can: ['rule the server'],
    inherits: ['manager']
  }
}

The roles property is required and must be an object. The keys of this object are counted to be the names of roles.

Each role must have a can property, which is an array. Elements in the array can be strings or objects.

If the element is a string then it is expected to be the name of the permitted operation.

If the element is an object:

  • It must have the name and when properties
    • name property must be a string
    • when property must be a function that returns a promise

Wildcards (v3.1+)

Each name of operation can include * character as a wildcard match. It will match anything in its stead. So something like account:* will match everything starting with account:.

Specific operations are always prioritized over wildcard operations. This means that if you have a definition like:

{
  user: {
    can: [
      'user:create',
      {
        name: 'user:*',
        when: async (params) => params.id === params.userId
      }
    ]
  }
}

Then user:create will not run the provided when operation, whereas everything else starting with user: does

Usage can(role, operation, params?)

After initialization you can use the can function of the object to check if role should have access to an operation.

The function will return a Promise that will resolve if the role can access the operation or reject if something goes wrong or the user is not allowed to access.

rbac.can('user', 'post:add')
  .then(result => {
    if (result) {
      // we are allowed access
    } else {
      // we are not allowed access
    }
  })
  .catch(err => {
    // something else went wrong - refer to err object
  });

The function accepts parameters as the third parameter, it will be used if there is a when type operation in the validation hierarchy.

rbac.can('user', 'post:save', {userId: 1, ownerId: 2})
  .then(result => {
    if (result) {
      // we are allowed access
    } else {
      // we are not allowed access
    }
  })
  .catch(err => {
    // something else went wrong - refer to err object
  });

You can also validate multiple roles at the same time, by providing an array of roles.

	rbac.can(['user', 'manager'], 'post:save', {userId: 1, ownerId: 2})
  .then(result => {
    if (result) {
      // we are allowed access
    } else {
      // we are not allowed access
    }
  })
  .catch(err => {
    // something else went wrong - refer to err object
  });

If the options of the initialization is async then it will wait for the initialization to resolve before resolving any checks.

const rbac = require('easy-rbac')
  .create(async () => opts);

rbac.can('user', 'post:add')
  .then(result => {
    if (result) {
      // we are allowed access
    } else {
      // we are not allowed access
    }
  })
  .catch(err => {
    // something else went wrong - refer to err object
  });

License

The MIT License (MIT) Copyright (c) 2015 Karl Düüna

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

easy-rbac's People

Contributors

deadalready avatar mreinstein avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

easy-rbac's Issues

Storing conditions in Database

I came from your awesome blog post! I have implemented exactly a similar RBAC in one of my projects, however, it does not include the conditional part of it.
My question to you is how can we store those conditions in Database?

I also have another one: What would you suggest if I want Porperty/Role based access control:
For eg: Suppose I have a user property as location! Now I want to give Admin set of privileges to Mr.x but only on other users belonging to his same location. So Mr. x can modify/delete users only belonging in his location.
I think it can be done using the condition part but would like to hear what you have to say.

Hope I'm clear.

Add typescript definition file

Using this module with typescript, wrote this ts.d file, but I'm not confident enough to submit to the @types repository. Thought I'd put it up here to get feedback before submitting.


declare module "easy-rbac" {

    interface when {  
        (theNumber: number) : number;
    }

    interface canObj {
        name: string;
        when: (params:any, callback:() => void) => void;
    }

    interface opts {
        [propName: string]: {
            can: (string | canObj)[],
            inherits?: string[]
        }
    }

    class RBAC {

        constructor(opts:opts);

        can(role: string, operation: string, cb?: (err: any, can: boolean) => void): void;
        can(role: string, operation: string, params?:any): Promise<void>;

        static create(opts: opts): RBAC

    }

    export = RBAC;

}

Question: multiple roles

Thank you for the great article at https://blog.nodeswat.com/implement-access-control-in-node-js-8567e7b484d1
I have looked through the repo and tried to use this package - it looks great, however I am missing an ability to provide multiple roles for can method.

I have implemented a naive solution for this use case at https://github.com/agoldis/easy-rbac/blob/multi_roles/lib/rbac.js#L180

Would that be a suitable case for PR?

Would you to share your opinion on getting multiple roles for a user properly for RBAC.

Thank you!

Some tests fail since Q.any throws different messages

Hello,

Q changed the way it handles the error message when a Q.any does not return fulfillment value.

err.message = ("Q can't get fulfillment value from any promise, all " +
                      "promises were rejected. Last error message: " + err.message);

see commit https://github.com/kriskowal/q/pull/740/files#diff-a148bc52a39c990d02038097d8177455R1628

So the tests are not valid anymore, because err.message is not anymore equals to 'unauthorized' but instead it is 'Q can't get fulfillment value from any promise, all promises were rejected. Last error message: unauthorized'

Feature Request: Allow role:read,update split on ,

Allow a user to provide a , in the permission string and then split on the , so that you don't have to glob all permissions but instead you can add a subset. This will cut down on redundant permission string lines.

Support wildcard operations

Hi @DeadAlready, first thanks for contributing this library to the OSC!

I was wondering what your disposition is to adding support to wildcard '*' in role initialization.

Some cases worth considering:

Admin
The admin role can do anything -- maybe this is the super user, maybe this is a system operation. But rather than configuring by hand every single operation -- wildcard resource and wildcard action would be a great catch all.

{
 admin: {
    can: ['*:*']
  }
}

Resource Manager
A role that has total responsibility over some resource - maybe articles for the news page, or comments from blog, etc.

{
 editor: {
    can: ['article:*']
  }
}

Moderator
A role that has specific responsibilities over multiple resources - like flagging posts or comments as inappropriate.

{
 moderator: {
    can: ['*:flag']
  }
}

Using wildcard for resources and actions we can provide catchall's that can be reasonably expected to keep working with the addition of new resources or actions on resources without having to update all existing role configurations with new permission.

ECMAScript Exports

Thanks for the great little library -- it's very useful!

I use easy-rbac as my authz layer in an Angular 10.x web app. This has brought to my attention that this is packaged as a legacy CommonJS module (I think), which is quite difficult for tree-shakers like webpack to optimise. This causes Angular to complain about the module.

WARNING in /Users/abc/src/xyz/src/app/abc.ts depends on 'easy-rbac'. CommonJS or AMD dependencies can cause optimization bailouts.
For more info see: https://angular.io/guide/build#configuring-commonjs-dependencies

Might it be possible to re-spin this module with ECMAScript exports, as well?

Thank you for your consideration.

Not responding when checking with inherited conditional operation resulting unauthorized

It is not responding when I am checking an inherited conditional operation resulting unauthorized.

let roles = {
  user: {
    can: [{
      name: "account:view", // can view own account
      when: function(param, callback) {
         setImmediate(callback, null, false); // always false, just for test.
      }
    }]
  },
  writer: {
    can: ["post:create", {
      name: "post:update",
      when: function(param, callback) {
         setImmediate(callback, null, if(own by writer));
      }
    }],
    inherits: ["user"]
  },
  admin: {
    can: ["anything"],
    inherits: ["writer"]
  }
}

when I try to check if writer is allowed to account:view or not, it never responds when its false.
But for conditional operation resulting true it works perfect.

When I say responds, meaning Callback execution

Errors are swallowed inside easyRBAC

Good day,

I have been using your nice library for Node app and React app both are running in production at the moment. Thanks for your great job.

One thing I noticed that if a when function throw an error. The error is being swallowed and easyRBAC return false instead!
With that being siad, it is impossible to know if easyRBAC returns false because when function return false or because it throws an error.

After debugging this library I found the issue in these lines:
https://github.com/DeadAlready/easy-rbac/blob/master/lib/rbac.js#L159
https://github.com/DeadAlready/easy-rbac/blob/master/lib/rbac.js#L176
https://github.com/DeadAlready/easy-rbac/blob/master/lib/utils.js#L19
https://github.com/DeadAlready/easy-rbac/blob/master/lib/utils.js#L29

If you agree with me that easyRBAC should not swallow errors and it should just re-throw errors back to the caller. I would be happy to provide PR if you are busy.

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.