Giter VIP home page Giter VIP logo

accesscontrol's Introduction

AccessControl.js

Build Status Coverage Status Dependencies Known Vulnerabilities Maintained
npm Release Downloads/mo. License TypeScript Documentation
© 2019, Onur Yıldırım (@onury).


Role and Attribute based Access Control for Node.js

Many RBAC (Role-Based Access Control) implementations differ, but the basics is widely adopted since it simulates real life role (job) assignments. But while data is getting more and more complex; you need to define policies on resources, subjects or even environments. This is called ABAC (Attribute-Based Access Control).

With the idea of merging the best features of the two (see this NIST paper); this library implements RBAC basics and also focuses on resource and action attributes.

Install Examples Roles Actions Resources Permissions More F.A.Q. API Reference

Core Features

  • Chainable, friendly API.
    e.g. ac.can(role).create(resource)
  • Role hierarchical inheritance.
  • Define grants at once (e.g. from database result) or one by one.
  • Grant/deny permissions by attributes defined by glob notation (with nested object support).
  • Ability to filter data (model) instance by allowed attributes.
  • Ability to control access on own or any resources.
  • Ability to lock underlying grants model.
  • No silent errors.
  • Fast. (Grants are stored in memory, no database queries.)
  • Brutally tested.
  • TypeScript support.

In order to build on more solid foundations, this library (v1.5.0+) is completely re-written in TypeScript.

Installation

with npm: npm i accesscontrol --save
with yarn: yarn add accesscontrol

Guide

const AccessControl = require('accesscontrol');
// or:
// import { AccessControl } from 'accesscontrol';

Basic Example

Define roles and grants one by one.

const ac = new AccessControl();
ac.grant('user')                    // define new or modify existing role. also takes an array.
    .createOwn('video')             // equivalent to .createOwn('video', ['*'])
    .deleteOwn('video')
    .readAny('video')
  .grant('admin')                   // switch to another role without breaking the chain
    .extend('user')                 // inherit role capabilities. also takes an array
    .updateAny('video', ['title'])  // explicitly defined attributes
    .deleteAny('video');

const permission = ac.can('user').createOwn('video');
console.log(permission.granted);    // —> true
console.log(permission.attributes); // —> ['*'] (all attributes)

permission = ac.can('admin').updateAny('video');
console.log(permission.granted);    // —> true
console.log(permission.attributes); // —> ['title']

Express.js Example

Check role permissions for the requested resource and action, if granted; respond with filtered attributes.

const ac = new AccessControl(grants);
// ...
router.get('/videos/:title', function (req, res, next) {
    const permission = ac.can(req.user.role).readAny('video');
    if (permission.granted) {
        Video.find(req.params.title, function (err, data) {
            if (err || !data) return res.status(404).end();
            // filter data by permission attributes and send.
            res.json(permission.filter(data));
        });
    } else {
        // resource is forbidden for this user/role
        res.status(403).end();
    }
});

Roles

You can create/define roles simply by calling .grant(<role>) or .deny(<role>) methods on an AccessControl instance.

  • Roles can extend other roles.
// user role inherits viewer role permissions
ac.grant('user').extend('viewer');
// admin role inherits both user and editor role permissions
ac.grant('admin').extend(['user', 'editor']);
// both admin and superadmin roles inherit moderator permissions
ac.grant(['admin', 'superadmin']).extend('moderator');
  • Inheritance is done by reference, so you can grant resource permissions before or after extending a role.
// case #1
ac.grant('admin').extend('user') // assuming user role already exists
  .grant('user').createOwn('video');

// case #2
ac.grant('user').createOwn('video')
  .grant('admin').extend('user');

// below results the same for both cases
const permission = ac.can('admin').createOwn('video');
console.log(permission.granted); // true

Notes on inheritance:

  • A role cannot extend itself.
  • Cross-inheritance is not allowed.
    e.g. ac.grant('user').extend('admin').grant('admin').extend('user') will throw.
  • A role cannot (pre)extend a non-existing role. In other words, you should first create the base role. e.g. ac.grant('baseRole').grant('role').extend('baseRole')

Actions and Action-Attributes

CRUD operations are the actions you can perform on a resource. There are two action-attributes which define the possession of the resource: own and any.

For example, an admin role can create, read, update or delete (CRUD) any account resource. But a user role might only read or update its own account resource.

Action Possession
Create
Read
Update
Delete
Own The C|R|U|D action is (or not) to be performed on own resource(s) of the current subject.
Any The C|R|U|D action is (or not) to be performed on any resource(s); including own.
ac.grant('role').readOwn('resource');
ac.deny('role').deleteAny('resource');

Note that own requires you to also check for the actual possession. See this for more.

Resources and Resource-Attributes

Multiple roles can have access to a specific resource. But depending on the context, you may need to limit the contents of the resource for specific roles.

This is possible by resource attributes. You can use Glob notation to define allowed or denied attributes.

For example, we have a video resource that has the following attributes: id, title and runtime. All attributes of any video resource can be read by an admin role:

ac.grant('admin').readAny('video', ['*']);
// equivalent to:
// ac.grant('admin').readAny('video');

But the id attribute should not be read by a user role.

ac.grant('user').readOwn('video', ['*', '!id']);
// equivalent to:
// ac.grant('user').readOwn('video', ['title', 'runtime']);

You can also use nested objects (attributes).

ac.grant('user').readOwn('account', ['*', '!record.id']);

Checking Permissions and Filtering Attributes

You can call .can(<role>).<action>(<resource>) on an AccessControl instance to check for granted permissions for a specific resource and action.

const permission = ac.can('user').readOwn('account');
permission.granted;       // true
permission.attributes;    // ['*', '!record.id']
permission.filter(data);  // filtered data (without record.id)

See express.js example.

Defining All Grants at Once

You can pass the grants directly to the AccessControl constructor. It accepts either an Object:

// This is actually how the grants are maintained internally.
let grantsObject = {
    admin: {
        video: {
            'create:any': ['*', '!views'],
            'read:any': ['*'],
            'update:any': ['*', '!views'],
            'delete:any': ['*']
        }
    },
    user: {
        video: {
            'create:own': ['*', '!rating', '!views'],
            'read:own': ['*'],
            'update:own': ['*', '!rating', '!views'],
            'delete:own': ['*']
        }
    }
};
const ac = new AccessControl(grantsObject);

... or an Array (useful when fetched from a database):

// grant list fetched from DB (to be converted to a valid grants object, internally)
let grantList = [
    { role: 'admin', resource: 'video', action: 'create:any', attributes: '*, !views' },
    { role: 'admin', resource: 'video', action: 'read:any', attributes: '*' },
    { role: 'admin', resource: 'video', action: 'update:any', attributes: '*, !views' },
    { role: 'admin', resource: 'video', action: 'delete:any', attributes: '*' },

    { role: 'user', resource: 'video', action: 'create:own', attributes: '*, !rating, !views' },
    { role: 'user', resource: 'video', action: 'read:any', attributes: '*' },
    { role: 'user', resource: 'video', action: 'update:own', attributes: '*, !rating, !views' },
    { role: 'user', resource: 'video', action: 'delete:own', attributes: '*' }
];
const ac = new AccessControl(grantList);

You can set grants any time...

const ac = new AccessControl();
ac.setGrants(grantsObject);
console.log(ac.getGrants());

...unless you lock it:

ac.lock().setGrants({}); // throws after locked

Documentation

You can read the full API reference with lots of details, features and examples.
And more at the F.A.Q. section.

Change-Log

See CHANGELOG.

Contributing

Clone original project:

git clone https://github.com/onury/accesscontrol.git

Install dependencies:

npm install

Add tests to relevant file under /test directory and run:

npm run build && npm run cover

Use included tslint.json and editorconfig for style and linting.
Travis build should pass, coverage should not degrade.

License

MIT.

accesscontrol's People

Contributors

aljaxus avatar onury 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  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

accesscontrol's Issues

Give unique User rights for unique "resource"

Hey everyone, your project looks very nice.
But I have a special question: we have something like container-entities for collect sub-entities. A has differend B's and B's have different C's, everything 1:n.
We want to grant access to a special User for a special entitiy, but its not the owner.
Is this possible?
Thank you!
Dominik

Expect Object from getGrants

describe('grantList', async () => {

  var grantList = [
    { role: 'admin', resource: 'video', action: 'create:any', attributes: ['*'] }
  ];

  it('should initialize ac from grantList', async () => {
    var ac = new AccessControl(grantList)
    let grants = ac.getGrants()
    console.log(grants)
    })
})

Outputs:

[ { role: 'admin',
resource: 'video',
action: 'create:any',
attributes: [ '*' ] } ]

I am expecting an object, like in the tests:

https://github.com/onury/accesscontrol/blob/master/lib/AccessControl.js#L130

When i mix my approach:

describe('grantList', async () => {
    var grantList = [
        { role: 'admin', resource: 'video', action: 'create:any', attributes: ['*'] }
    ];

    it('should initialize ac from grantList', async () => {
        var ac = new AccessControl(grantList)
        ac
        .grant('user')
        .readAny(['profile', 'video'], ['', '!id', '!password'])
        .createOwn(['profile', 'video'])
        .deleteOwn(['video']);
        // logging underlying grants model
        console.log(ac.getGrants());
    })
})

I get this mixd output:

[ { role: 'admin',
    resource: 'video',
    action: 'create:any',
    attributes: [ '*' ] },
  user: { profile: { 'read:any': [Array], 'create:own': [Array] },
    video: 
     { 'read:any': [Array],
       'create:own': [Array],
       'delete:own': [Array] } } ]

I guess I am expecting the types to be the same.

Really, I'd love to export as a grantList or an Object, depending on the situation.

Groups based permission.

Hi and congratulations, the library looks really promising and well done.
I have a question for a specific use case I was asked to implement and I'm just checking out the right solution:

An application with groups, similar to the facebook ones.
A user with given permissions can create groups, say GroupA and GroupB, and assign different group based permissions .
E.g. UserA is assigned to GroupA with the "admin" role and assigned to GroupB with the "guest" role.
Is this doable with this library and if so, can you please give any suggestions ?
Thanks.

Plans on saving access control in a database

Are there any plans on saving the access control lists in a database?
Do I understand correctly that all access control infos are lost if i stop the application it's implemented in?

Filtering Data

Hello,

Very nice project. However I am having issues filtering data based on attributes i set in my grants.

// grants
{ role: 'admin', resource: 'account', action: 'read:any', attributes: '*, !name' },

account is the name of table that gets fetched from mongodb

// route handler

exports.getAll = function (req, res, next) {
 
 const { user } = res.locals;
 const crudPermission = ac.can(user.role).readAny('account');

if (crudPermission.granted) {
   console.log(crudPermission.attributes)
   Account.find()
     .populate('user')
     .then(accounts => {
       console.log(accounts)
       let filtered = crudPermission.filter({accounts})
       res.send(filtered)
     })
     .catch(err => {
       next(err);
     })

 }
}

the response is still returning the name attribute. Not sure what I am doing wrong here? Any help would be much appreciated.

Chain Queries

I'm working on a project using AccessControl, it would be nice if there was a way of chaining queries like so :

ac.can('user').createOwn(xx).readAny(yy)

If not possible what is the best way to do it besides a && condition.

Thanks

Client Side Filter

I'm in a situation where I have the security enforced server side, but I'd like to be able to pass a user's grants down to the client to alter the way the page renders.

Could we add a method to export grants in a way that can be consumed client side or create a client module that can read from an exported object and use it?

Edit: upon reading more I see this uses the 'notation' module which can be used client side for a specific attribute list. however It would be useful to have the ability to export a json object of all permissions and attributes granted to a specific user. I created a very crude one below of what I'm thinking.

let role = '<your role>'
let grants = {};

// Add grants to the u object
for (let verb of ['create', 'read', 'update', 'delete']) {
  for (let possession of ['Own', 'Any']) {
    grants[verb + possession] = {};
    for (let resource of ac.getResources()) {
      let permission = ac.can(role)[verb + possession](resource);
      if (permission.granted) {
        grants[verb + possession][resource] = permission.attributes;
      }
    }
  }
}

Grandparent inheritance?

Accesscontrol seems to support extending permissions from a parent to child but does not seem to support inheriting permissions more than one level up i.e. grandparent -> parent -> child

e.g. viewer -> ops -> admin where viewer has read, ops read/update and admin read/update/delete

accessControl.grant('viewer').readAny('devices');
accessControl.grant('ops').extend('viewer').updateAny('devices');
accessControl.grant('admin').extend('ops').deleteAny('devices');
console.log(`accessControl.getGrants()=${JSON.stringify(accessControl.getGrants())}`);
assert(accessControl.can('ops').readAny('devices').granted);
assert('admin can reay any devices=' + accessControl.can('admin').readAny('devices').granted);

Correct or have I set up my grants incorrectly?

custom actions and user-based access control

Hi, @onury!
First of all thank you for such a great lib with a clear api!

I have a several questions:

  • How can I control access to features which are build on top of my resources.
    It's very clear for me about classic CRUD, but I have a lot of extra features/action which role can do with a resource such as: export resources, import resources, bulk update resources, comment etc. For example: Admin can view any resource, bulk edit resource, export resource into csv and comment on resource. But user can only view any resource

  • It is possible to control access to a particular resources? I know that Policies in ABAC can do this trick, specify something like:

{"own": ["resource.creator_id=user.id"], "sharedToMe": ["resource.id in (sharedIds)"] }

Thank you!

Spec for AccessControl

Hey there, came across this today, and was blown away by the quality of the docs and care put into it. We (our MSFT open source eng team) have been looking into using an access system like this, and your project could be the right answer. One question: I know it says it's a merging of some traditional RBAC schemes and other access schemes, based on the cited NIST paper, but does this have/follow formal specs? If it's a brand new construct that combines ideas of two/more schemes, would you be willing to codify it with a spec that goes a bit beyond API docs?

Dynamic Actions and Possession Groups

Own and Any are equivalent to "user" and "world" in the unix ABAC, missing the "group".

Currently ownership verification is left out as responsibility of the application, which works fine for static ownership.

But context-based dynamic ownerships require the ability to create new possession levels programatically.

For example, a resource (say MP3 file) purchased by one user need to be allowed update access to immediate family members (so that his family members can add meta data), "play" permission to friends, allow "share" permission for both. Another user may want to allow all permissions to family and friends same. In all cases, the seller should have the permission to "update" the file (say, improved version auto-upgraded).

Now this "family members" group and "friends" group memberships is not static and dynamically determined from the resource owner (who purchased it). And the access permission preferences for both groups are not same for each resource or each user. These groups cannot be implemented as Role since membership varies based on context. Not possible to hardcode the ownership verification since the access permission preferences vary for each user, and new groups may be created on the fly with new permissions (and may further change over time).

This kind of access requires the ability to define Permission Groups and Actions dynamically. For example, CRUD may not be enough and developer should be able to define more (such as "share", "like", "email", "print", "copy", "backup", yada yada). Own and Any are not enough (too wide blanket), developers should be able to create dynamic groups and be allowed to specify permissions for each group.

Determining the group membership could be left to the developer, while the access permissions for those groups could be tracked by this library automatically.

Checking of posession based on role?

I'm trying to check for possession for the following scenario;

Roles:

  • User
  • PrimaryContact
  • Admin

In the system there are Contacts and Accounts. Contacts are linked to Accounts by AccountID. Some Accounts have a parent Account, linked by ID.

I would like to set grants for the following:

  • User role may read own Contacts,

  • PrimaryContacts may read any Contact that belongs to all Accounts

  • Admin may read any Contact.

So for a User reading his own Contact, the isOwn check is something like

const isOwn = contact.accountID === user.accountID

The users with the PrimaryContact and Admin role have no other special attributes as a 'normal' user (besides having different role assigned on the user in the database) so the only IsOwn check I can think of is directly checking for a certain role. Is this the way to go?

permission.filter(...) -> exception value is not a function

Hi there!
First, thanks for an amazing project. Like it a lot and started using in my pet project.
Second, I faced the following error when trying to apply permission to filter output based on attribute rules. See title. According to Notation.js, value is declared as property (get value() {...}), so should be accessed as value, not as value() (refer to lib/helper.js:line 98):

    filter(object, attributes) {
        if (!Array.isArray(attributes) || attributes.length === 0) {
            return {};
        }
        let notation = new Notation(object);
        return notation.filter(attributes).value();
    },

As a result, calling permission.filter crashes with mentioned above exception.

Permission check with multiple roles changes underlying grants model.

The following simple test fails, and actually changes the underlying grant model.

    it('Check with multiple roles changes grant list', function () {
        var ac = new AccessControl(grantList);

        // Admin can update any video
        expect(ac.can(['admin']).updateAny('video').granted).toEqual(true);

        // console.log('grants', ac.getGrants());

        // This check actually changes the underlying grants
        ac.can(['user', 'admin']).updateOwn('video');

        // console.log('grants', ac.getGrants());

        // Admin can update any video (nope not anymore!)
        expect(ac.can(['admin']).updateAny('video').granted).toEqual(true);
    });

v2.0.0 How to extend role declared in JSON?

I can't seem to extend a role when declared in json. The following works but when I take the tostring and then use as access control.setGrants(thejson) it complains about $extend being a reserved word. I see that is a new addition in the changelog. How do I achieve the same result using setGrants()?

const accessControl = new AccessControl();
accessControl.grant('viewer').readAny('users');
accessControl.grant('admin').extend('viewer').createAny('users').deleteAny('users').updateAny('users');
let assert = require('assert');
assert('admin can read any user=' + accessControl.can('admin').readAny('users').granted);
console.log(`accessControl.getGrants()=${JSON.stringify(accessControl.getGrants())}`);

$> accessControl.getGrants()={"viewer":{"users":{"read:any":["*"]}},"admin":{"$extend":["viewer"],"users":{"create:any":["*"],"delete:any":["*"],"update:any":["*"]}}}

Customizing the error messages

I need to customize the error messages. For example, if a user with role of "Buyer" is trying to access my resource but grants object doesn't contain the role called "Buyer" and accesscontrol will throw "Role not found: \"Buyer\"". Here I want to take the control of error block either synchronously/asynchronously and customize the error message and send to the end point. Please provide if any one knows have done it previously.
Thanks

Add conditions to grants

One of the projects that uses this library as a foundation has implemented conditions for the grants. Look here for more information. It supports AND, OR, NOT, EQUALS, NOT_EQUALS, STARTS_WITH, LIST_CONTAINS conditions, which are really powerful to grant permission when a certain state has a certain value. This way permissions can be even more finely grained. Especially for more complex systems this would be a nice addition I think.

Filtering based on attribute value

I read #8 but I have following scenario, will it be possible to define policy

Resource: Book
Attribute: Publisher
Role: Student
How to define policy for:

  • Allow access if Publisher is XYZ
  • Deny access if Publisher is ABC

Does accesscontrol support filtering based on attribute value?

Explicit .grant() and .deny() should override any inherited permissions

Currently permissions are computed with a Union when there is inheritance. Tell me if i'm wrong. :) .

It will be awesome if in V3, we could have a switch, to choose the algorithm that make the computation.
For example, I will really appreciate to have a algorithm that make the children permissions override the parents permissions.

Something like this :

  • Parent1 (grant, video, createOwn )

  • Parent2 (grant, post, createAny/Own)

  • Chid1 extends Parent1, Parent2 ( deny, post, createAny)

Child permissions == ( video:createOwn, post:createOwn )

Using grants clientside

Any thoughts on reusing the grants object defined server side so that navigation links on the client (React frontend in this case) can be properly security trimmed according to role definition and inheritance defined server side?

I was thinking about serializing the grants and and make it available to the client via a REST endpoint.

Problem with Notation

accessControl.grant('user').create('project', ['*', '!approved']);
accessControl.grant('admin').extend('user').create('project');

accessControl.can('user').create('project').attributes; // [ '*', '!approved' ] ✔️
accessControl.can('admin').create('project').attributes; // [ '*', '!approved' ] ❌
accessControl.can(['user', 'admin']).create('project').attributes; // [ '*' ] ✔️

As you can see, the last two lines should have produces the same results - [ '*' ]. So i debugged the code and found that the problem seems to be with the Notation module that AccessControl uses.

notation.Glob.union(['*', '!approved'], ['*']); // [ '*' ]
notation.Glob.union(['*'], ['*', '!approved']) // [ '*', '!approved' ]

Then I also saw that you might've fixed the issue in Notation. Am I right in assuming that this bug will be fixed when it start using the latest Notation module version? 🥇

Possibility of Express Middleware for perm checking

I'm trying to work out how I might be able to perform an access control permission check using a code structure of the form:

  app.get('/admin/users',
    setRender('admin/users/search'),
    setRedirect({auth: '/'}),
    havePerm.readAny('profile'),
    adminusers.searchProfile);	// Users Search/results

rather than the current Express.js example methodology which amounts to predefining each permissions check and testing it within what is effectively the controller of an MVC application. It seems that such an express middleware would simply subclass the ac.can() method, returning an express-appropriate response rather than the true/false value, but I don't want to reproduce effort, if someone (3rd party) has already done this, or it's on the roadmap, completed in an unreleased branch somewhere.

Similarly, and more trivially, it might be worthwhile to include a mongoose plugin that inserts a role property in a known location in user models, to facilitate the above permission checking style, being performed where it should be in an MVC application.

If this is already out there, and I've simply overlooked it, please kindly point me to the relevent code/project. If not, I'm willing to undertake this feature development, but having taken a cursory look at the accesscontrol codebase, I'd need a little guidance as to the code structure, in order to implement it efficiently.

Thanks,
Colin

Add ability to normalize data object before filtering

I stumbled upon this when trying to filter responses from Mongoose queries. In short, every ObjectId property turns into an object when passed through filter(), which is not the intended representation. I don't think this is specific to Mongoose hence this report.

Suppose a query on an empty Mongoose model Foo:

let bar = await Foo.findOne().lean()

Now do JSON.stringify(bar):

{
  "_id": "59d4a6ea1960f7053c497508",
  "__v": 0
}

Now do JSON.stringify(permission.filter(bar)):

{
  "_id": {
    "_bsontype": "ObjectID",
    "id": {
      "type": "Buffer",
      "data": [89, 212, 166, 234, 25, 96, 247, 5, 60, 73, 117, 8]
    }
  },
  "__v": 0
}

For now I'm using this as a workaround:

permission.filter(JSON.parse(JSON.stringify(bar))) // gross

Attribute based access control

How would you implement policies like below?

Role - LocationAdmin
Attribute - Location: Texas
Can: CRUD claims made by users living in texas.

Does readOwn method include readAny inheritance?

Hi Onury,

Really like what you did with this library 👍 .

I have a question for you. If I have a permission structure like this:

[
  { role: 'admin', resource: 'user', action: 'create:any', attributes: ['*'] },
  { role: 'admin', resource: 'user', action: 'read:any', attributes: ['*'] },
  { role: 'admin', resource: 'user', action: 'update:any', attributes: ['*'] },
  { role: 'admin', resource: 'user', action: 'delete:any', attributes: ['*'] },

  { role: 'user', resource: 'user', action: 'read:own', attributes: ['*'] },
  { role: 'user', resource: 'user', action: 'update:own', attributes: ['*'] },
  { role: 'user', resource: 'user', action: 'delete:own', attributes: ['*'] },
]

And in my controller, I do a check for can(role).readOwn(resource)... if the user I am checking on has a role of admin does the code assume that because admin's have access to read:any they can also read:own?

That is what I am seeing happen in my debugger, which makes sense to me. I just want to confirm that is happening?

For instance, if I am logged in as an admin and a run a check for can(role).readOwn(resource), I am returned TRUE from granted, even though I did not specifically state an admin can read:own in my permissions above.

I ask because in another question I see that you suggest the following:

var role = req.user.role;
// check if the request is for own photos or any
var permission = (req.user.name === req.params.username)
   ? ac.can(role).updateOwn('photo')
   : ac.can(role).updateAny('photo');

Is this necessary or can I just check ac.can(role).updateOwn('photo')?

Thanks!

Filtering inside an array of collections

First of all, thanks for the amazing library. So far it has saved us a ton of time figuring out role and attribute based permissions ourselves. Really appreciate it.

A small hiccup we encountered is filtering inside collections.

Given this object:

{
  locality: 'A locality',
  street: 'A street',
  properties: [
    { 
      name: 'The Oasis',
      byLaws: 'The bylaws',
      occupants: [
        { name: 'Dan', age: 31 },
        { name: 'Roy', age: 22 }
      ]
    },
    { 
      name: 'Azure',
      byLaws: 'The bylaws',
      occupants: [
        { name: 'Annie', age: 32 },
        { name: 'Paul', age: 33 }
      ]
    }
  ]
}

I understand I can filter out street within the root with !street but how do we filter out byLaws within the properties? or how about the age within the occupants of a property?

So far I've tried !*.byLaws, !**.byLaws, !*.*.byLaws and !*.**.byLaws. For age I can imagine it would follow similar patterns for restricting byLaws. Any thoughts on how this can be done?

Thanks again

Redis Support?

I’m looking to implement authorization in a clustered Node app. Has there been an attempt to make this package have Redis support? Or is there an alternative method of sharing memory between Node instances?

how to only allow 'delete attribute' ?

I want implement 'deny delete entity' but can 'delete attribute'

Here is example:

user CANNOT delete video entity, but CAN delete video's title

const AccessControl = require('accesscontrol')

const ac = new AccessControl();

ac.grant('user').createOwn('video')
// ac.grant('user').deleteOwn('video', ['*'])
// ac.deny('user').deleteOwn('video')
ac.grant('user').deleteOwn('video', ['title'])

let permission = ac.can('user').deleteOwn('video');
console.log("can uer deleteOwn video entity= ", permission.granted);
permission = ac.can('user').deleteOwn('video', 'title');
console.log("can uer deleteOwn video title = ", permission.granted);
permission = ac.can('user').deleteOwn('video', 'id');
console.log("can uer deleteOwn video id = ", permission.granted);

but the result is

can user deleteOwn video entity=  true
can user deleteOwn video title =  true
can user deleteOwn video id =  true

it's a bug? or how to implement it?

UI widget to manage roles, resources and permissions

I was wondering if there are some example front end widgets or suggestions of rules of components that work well for end users to define and manage permissions, resources and role. Any guidance on best practices or tools to get started would be highly appreciated!

Other Possession types besides 'Own' and 'Any'?

Hi!

I stumbled upon accesscontrol while looking for existing solutions to some ABAC and I really like the API!

Question

Can I have more possession types than simply "Owner" and "Any"?

Use Case

Let's say I have a market place where people can sell products and others can buy them. I could have a Product resource where:

  • Anybody can view title, description
  • owner (vendor) can view title, description, downloadLink, downloadsCount
  • customer (user who bought the product) can view title, description, downloadLink

Thanks,
Max

Department level access

Suppose I have the following roles

  1. Employee
  2. Manager
    All the users belong to departments

Manager A can access documents of department A,
Manager B can access documents of department B

How can this be implemented using accesscontrol ? cause as per my understanding we have
can -> any or can -> all

Should we remove aliases in v3?

A quote from a fellow developer:

"I don't like aliases. They make it seem like it works differently when reading the code."

I'm thinking he's right. Should we remove aliases such as below in version 3?

  • #allow() (alias of #grant())
  • #reject() (alias of #deny())
  • #query() (alias of #can())
  • a couple more..

Please vote via 👍 (remove), or 👎 (don't remove).

Access control editor

Great work on the accesscontrol module. It seems to be perfect for my needs. I was wondering if anyone was aware of an open source editor component for creating and editing roles and permissions according to the format that accesscontrol supports?

How to import it to TypeScript project?

like this one:
import { AccessControl } from 'accesscontrol';
does not work tried * import alone but none of these alow me to call new AccessControl()

Can aynone help please?

Error when calling ".can(role)"

This is what I am getting:

//Printing permissions
console.log(ac.getGrants());
{ ADMIN: 
   { customers: 
      { 'create:any': [Object],
        'delete:any': [Object],
        'read:any': [Object] }
      }
}

But when I do

var permission = ac.can(userRole).readAny('customers');

I am getting this error: (userRole is ADMIN)

Debug: internal, implementation, error 
    TypeError: Uncaught error: Cannot read property '$extend' of undefined
    at /apps/node-apps/figgojs-live/node_modules/accesscontrol/lib/utils.js:58:35
    at Array.forEach (native)
    at Object.getFlatRoles (/apps/node-apps/figgojs-live/node_modules/accesscontrol/lib/utils.js:56:15)
    at Object.getUnionAttrsOfRoles (/apps/node-apps/figgojs-live/node_modules/accesscontrol/lib/utils.js:224:27)
    at new Permission (/apps/node-apps/figgojs-live/node_modules/accesscontrol/lib/core/Permission.js:52:45)
    at Query._getPermission (/apps/node-apps/figgojs-live/node_modules/accesscontrol/lib/core/Query.js:266:16)
    at Query.readAny (/apps/node-apps/figgojs-live/node_modules/accesscontrol/lib/core/Query.js:152:21)
    at /apps/node-apps/figgojs-live/Routes/AdminRoute.js:100:41
    at /apps/node-apps/figgojs-live/Utils/UniversalFunctions.js:589:13
    at /apps/node-apps/figgojs-live/node_modules/jsonwebtoken/index.js:155:18
    at _combinedTickCallback (internal/process/next_tick.js:73:7)
    at process._tickDomainCallback (internal/process/next_tick.js:128:9)

What about true resource check (maybe with groups)

In here we have a use case that is hard to cover (and in fact uncovered by any library) :
Instead of simply checking permissions against a role and a "resource" (which, in fact, is a more resourceType), we are trying to authorize against a role and a true resource (a resource unique instance), going through groups of resources.

The library, which is very great and has a clean and robust API, offers what I just called "role against resourceType", by granting/revoking one role to/from any instance of the resourceType, or own instance of resourceType.

What about a strategy that would allow to grant a role to that specific resource, or maybe simpler any resource in that group ?

that could lead to something like

var ac = new AccessControl();
ac.grant('user')
    .createOwn('video')
    .deleteOwn('video')
    .readGroup('video', 'group1') // <== HERE
  .grant('admin')
    .extend('user')
    .updateAny('video', ['title'])
    .deleteAny('video');

var permission = ac.can('user').createOwn('video');
console.log(permission.granted);    // —> true
console.log(permission.attributes); // —> ['*'] (all attributes)

permission = ac.can('admin').updateAny('video');
console.log(permission.granted);    // —> true
console.log(permission.attributes); // —> ['title']

var permission = ac.can('user').readGroup('video', 'group1');
console.log(permission.granted);    // —> true
console.log(permission.attributes); // —> ['*'] (all attributes)

there seem to be more than a need on this feature. See :
OptimalBits/node_acl#241
OptimalBits/node_acl#243

Combined own or any check

As far as i read, the recommended way to check for permissions is

(req.user.name === req.params.username)
   ? ac.can(role).updateOwn('photo')
   : ac.can(role).updateAny('photo');

Have you considered to provide combined own or any checks in the form of

const owned = req.user.name === req.params.username;
ac.can('user').create('profile', {owned} )
// or
ac.can('user').create('profile').isOwned(owned)

Cross inheritance

Should cross inheritance be allowed?

For example I have 2 roles - user and admin.

Now if I write -

ac.grant('admin').extend('user')
ac.grant('user').extend('admin')

Then from this moment onward, both the roles have become exactly similar. All the permissions given to admin role would apply to user role and vice-versa. Then what's the point of having 2 different roles?

Forgive if I'm missing something. And thanks for this really useful module.

attributes for users?

hi,
this is a great library. thank you for pulling this together.

I understand, at the moment, the package supports resource attributes but not user attributes? If this is correct, would it be possible to consider introducing user attributes in the future?

many thanks,
Lucas

createOwn vs createAny

Hi, @onury!

At first, I would like to thank you for such a great library!

I have one questions about createOwn and createAny methods. I don't understand how to use them from a logical point of view. If I have readOwn / readAny it's clear for me that the user of some role can read only his own resource or can read any resources (e.g. resources of the other users). The same with updateOwn / updateAny - the user can update only his own resource or any resources. And the last one is deleteOwn / deleteAny - for example, the user can delete only his own resource but the admin can delete any resources (e.g. resources of the other users).

But what about createOwn and createAny. Let's imagine that I have two roles:

  • user
  • admin

Also, in my DB I have the posts table. The user and admin can create posts.

How should I design my AC?

Let's take this grants:

let grantsObject = {
    admin: {
        posts: {
            'create:any': ['*'],
            'read:any': ['*'],
            'update:any': ['*'],
            'delete:any': ['*']
        }
    },
    user: {
        posts: {
            'create:own': ['*'],
            'read:own': ['*'],
            'update:own': ['*'],
            'delete:own': ['*']
        }
    }
};

const ac = new AccessControl(grantsObject);

What does create:any for the admin role mean? Does it mean that the admin can create posts for any other users (e.g. the admin can create the post and assign its id to some user)? I am confused with this logic.

And what about create:own for the user role? Does it mean that the user can create the posts only ... for himself?

I have read the guide and FAQ and I understand that the developer (e.g. me) should make decisions but it's hard for me to understand the idea of createOwn and createAny concepts.

Thank you for your time.

Get possible actions for role(s)

Hi !

I'm looking for a well-designed solution for showing possibles actions to users.

For example with 2 roles, "admin" and "user" :

var grants = {
	admin : {
		task : {
			"create:any" : ['*'],
			"read:any" : ['*'],
			"update:any" : ['*'],
			"delete:any" : ['*'],
		}
	},
	user : {
		task : {
			"read:any" : ['*']
		}
	}
};
var ac = new AccessControl(grants);

When someone load the list of tasks, I would like to pass an object of "possible actions doable based on the role or roles of this person".

Probably something like :

var role = ["user"];
var actions_possible = ac.getGrants(role);

which returned something like :

task : {
	"read:any" : ['*']
}

Then I can show only the view button to a "user" and view/edit/delete/create to an "admin"

With this simple example, I could just do a "getGrants()" and parse the result, but when a user have multiple roles, with some of them are inherited from others, it could be really complicated to do so.

So simple questions, am I missing something in the documentation ? And based on the implementation of this library, is it possible to create such a function (inside the library or outside in my own code).

Thank you very much and great job, very nice library to use ;-)

How to use this with database backend?

The documentation looks great with lot of details but I'm not very clear how to use this with database. How to use this with a mysql backed application where I have different resource group and within that I have resources that needs to have access control.

For example, I have folders and within the folder files. Users can have access to folders with different roles - user, admin, owner where user could view files in the folder, admin could do view, edit, create & owner can do all. Within a single file a user role can have more privileges granted. Anyone can create a new folder (owner) and add users and assign permissions.

How is such scenario implemented with this module? an example project would be helpful.

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.