Git Product home page Git Product logo

node_acl's Introduction

NODE ACL - Access Control Lists for Node

This module provides a minimalistic ACL implementation inspired by Zend_ACL.

When you develop a web site or application you will soon notice that sessions are not enough to protect all the available resources. Avoiding that malicious users access other users content proves a much more complicated task than anticipated. ACL can solve this problem in a flexible and elegant way.

Create roles and assign roles to users. Sometimes it may even be useful to create one role per user, to get the finest granularity possible, while in other situations you will give the asterisk permission for admin kind of functionality.

A Redis, MongoDB and In-Memory based backends are provided built-in in the module. There are other third party backends such as knex based, firebase and elasticsearch. There is also an alternative memory backend that supports regexps.

Follow manast for news and updates regarding this library.

Status

BuildStatus Dependency Status devDependency Status

Features

  • Users
  • Roles
  • Hierarchies
  • Resources
  • Express middleware for protecting resources.
  • Robust implementation with good unit test coverage.

Installation

Using npm:

npm install acl

Documentation

Examples

Create your acl module by requiring it and instantiating it with a valid backend instance:

var acl = require('acl');

// Using redis backend
acl = new acl(new acl.redisBackend(redisClient, prefix));

// Or Using the memory backend
acl = new acl(new acl.memoryBackend());

// Or Using the mongodb backend
acl = new acl(new acl.mongodbBackend(dbInstance, prefix));

All the following functions return a promise or optionally take a callback with an err parameter as last parameter. We omit them in the examples for simplicity.

Create roles implicitly by giving them permissions:

// guest is allowed to view blogs
acl.allow('guest', 'blogs', 'view')

// allow function accepts arrays as any parameter
acl.allow('member', 'blogs', ['edit', 'view', 'delete'])

Users are likewise created implicitly by assigning them roles:

acl.addUserRoles('joed', 'guest')

Hierarchies of roles can be created by assigning parents to roles:

acl.addRoleParents('baz', ['foo', 'bar'])

Note that the order in which you call all the functions is irrelevant (you can add parents first and assign permissions to roles later)

acl.allow('foo', ['blogs', 'forums', 'news'], ['view', 'delete'])

Use the wildcard to give all permissions:

acl.allow('admin', ['blogs', 'forums'], '*')

Sometimes is necessary to set permissions on many different roles and resources. This would lead to unnecessary nested callbacks for handling errors. Instead use the following:

acl.allow([
    {
        roles:['guest', 'member'],
        allows:[
            {resources:'blogs', permissions:'get'},
            {resources:['forums', 'news'], permissions:['get', 'put', 'delete']}
        ]
    },
    {
        roles:['gold', 'silver'],
        allows:[
            {resources:'cash', permissions:['sell', 'exchange']},
            {resources:['account', 'deposit'], permissions:['put', 'delete']}
        ]
    }
])

You can check if a user has permissions to access a given resource with isAllowed:

acl.isAllowed('joed', 'blogs', 'view', function(err, res){
    if(res){
        console.log("User joed is allowed to view blogs")
    }
})

Of course arrays are also accepted in this function:

acl.isAllowed('jsmith', 'blogs', ['edit', 'view', 'delete'])

Note that all permissions must be fulfilled in order to get true.

Sometimes is necessary to know what permissions a given user has over certain resources:

acl.allowedPermissions('james', ['blogs', 'forums'], function(err, permissions){
    console.log(permissions)
})

It will return an array of resource:[permissions] like this:

[{'blogs' : ['get', 'delete']},
 {'forums':['get', 'put']}]

Finally, we provide a middleware for Express for easy protection of resources.

acl.middleware()

We can protect a resource like this:

app.put('/blogs/:id', acl.middleware(), function(req, res, next){}

The middleware will protect the resource named by req.url, pick the user from req.session.userId and check the permission for req.method, so the above would be equivalent to something like this:

acl.isAllowed(req.session.userId, '/blogs/12345', 'put')

The middleware accepts 3 optional arguments, that are useful in some situations. For example, sometimes we cannot consider the whole url as the resource:

app.put('/blogs/:id/comments/:commentId', acl.middleware(3), function(req, res, next){}

In this case the resource will be just the three first components of the url (without the ending slash).

It is also possible to add a custom userId or check for other permissions than the method:

app.put('/blogs/:id/comments/:commentId', acl.middleware(3, 'joed', 'post'), function(req, res, next){}

Methods

Adds roles to a given user id.

Arguments

    userId   {String|Number} User id.
    roles    {String|Array} Role(s) to add to the user id.
    callback {Function} Callback called when finished.

Remove roles from a given user.

Arguments

    userId   {String|Number} User id.
    roles    {String|Array} Role(s) to remove to the user id.
    callback {Function} Callback called when finished.

Return all the roles from a given user.

Arguments

    userId   {String|Number} User id.
    callback {Function} Callback called when finished.

Return all users who has a given role.

Arguments

    rolename   {String|Number} User id.
    callback {Function} Callback called when finished.

Return boolean whether user has the role

Arguments

    userId   {String|Number} User id.
    rolename {String|Number} role name.
    callback {Function} Callback called when finished.

Adds a parent or parent list to role.

Arguments

    role     {String} Child role.
    parents  {String|Array} Parent role(s) to be added.
    callback {Function} Callback called when finished.

Removes a parent or parent list from role.

If parents is not specified, removes all parents.

Arguments

    role     {String} Child role.
    parents  {String|Array} Parent role(s) to be removed [optional].
    callback {Function} Callback called when finished [optional].

Removes a role from the system.

Arguments

    role     {String} Role to be removed
    callback {Function} Callback called when finished.

### removeResource( resource, function(err) )

Removes a resource from the system

Arguments

    resource {String} Resource to be removed
    callback {Function} Callback called when finished.

Adds the given permissions to the given roles over the given resources.

Arguments

    roles       {String|Array} role(s) to add permissions to.
    resources   {String|Array} resource(s) to add permisisons to.
    permissions {String|Array} permission(s) to add to the roles over the resources.
    callback    {Function} Callback called when finished.

allow( permissionsArray, function(err) )

Arguments

    permissionsArray {Array} Array with objects expressing what permissions to give.
       [{roles:{String|Array}, allows:[{resources:{String|Array}, permissions:{String|Array}]]

    callback         {Function} Callback called when finished.

Remove permissions from the given roles owned by the given role.

Note: we loose atomicity when removing empty role_resources.

Arguments

    role        {String}
    resources   {String|Array}
    permissions {String|Array}
    callback    {Function}

Returns all the allowable permissions a given user have to access the given resources.

It returns an array of objects where every object maps a resource name to a list of permissions for that resource.

Arguments

    userId    {String|Number} User id.
    resources {String|Array} resource(s) to ask permissions for.
    callback  {Function} Callback called when finished.

Checks if the given user is allowed to access the resource for the given permissions (note: it must fulfill all the permissions).

Arguments

    userId      {String|Number} User id.
    resource    {String} resource to ask permissions for.
    permissions {String|Array} asked permissions.
    callback    {Function} Callback called with the result.

Returns true if any of the given roles have the right permissions.

Arguments

    roles       {String|Array} Role(s) to check the permissions for.
    resource    {String} resource to ask permissions for.
    permissions {String|Array} asked permissions.
    callback    {Function} Callback called with the result.

Returns what resources a given role has permissions over.

Arguments

    role        {String|Array} Roles
    callback    {Function} Callback called with the result.

whatResources(role, permissions, function(err, resources) )

Returns what resources a role has the given permissions over.

Arguments

    role        {String|Array} Roles
    permissions {String|Array} Permissions
    callback    {Function} Callback called with the result.

Middleware for express.

To create a custom getter for userId, pass a function(req, res) which returns the userId when called (must not be async).

Arguments

    numPathComponents {Number} number of components in the url to be considered part of the resource name.
    userId            {String|Number|Function} the user id for the acl system (defaults to req.session.userId)
    permissions       {String|Array} the permission(s) to check for (defaults to req.method.toLowerCase())

Creates a backend instance. All backends except Memory require driver or database instance. useSingle is only applicable to the MongoDB backend.

Arguments

    db        {Object} Database instance
    prefix    {String} Optional collection prefix
    useSingle     {Boolean} Create one collection for all resources (defaults to false)
var mongodb = require('mongodb');
mongodb.connect("mongodb://127.0.0.1:27017/acltest", function(error, db) {
  var mongoBackend = new acl.mongodbBackend(db, 'acl_');
});

Creates a new MongoDB backend using database instance db.

var client = require('redis').createClient(6379, '127.0.0.1', {no_ready_check: true});
var redisBackend = new acl.redisBackend(client);

Creates a new Redis backend using Redis client client.

Tests

Run tests with npm (requires mocha):

 npm test

Future work

  • Support for denials (deny a role a given permission)

License

(The MIT License)

Copyright (c) 2011-2013 Manuel Astudillo [email protected]

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.

node_acl's People

Contributors

0xashish avatar akashdeepsinghal avatar alonl avatar atinux avatar c0d3x42 avatar caasjj avatar danielrohers avatar devinea avatar diversario avatar ebellbog avatar freakthemighty avatar fyockm avatar gabeisman avatar inolasco avatar jac1013 avatar jelder avatar jonmassot avatar jurko-gospodnetic avatar lancers avatar leodutra avatar listepo avatar manast avatar mertcetin avatar mgcrea avatar nemtsov avatar paulhill avatar santhisenan avatar stevenblack avatar tthew avatar yonjah 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  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

node_acl's Issues

whatResources() does not handle wildcard

Take the following example:

acl.allow('superuser','user.management','*',cb);

acl.whatResources('superuser','read',cb(resources){
          console.log("Resources: %j",resources);
})

My understanding of the wildcard means that 'superuser' should have all permission on 'user.managment'. So the log should output "user.management" as a valid resource, but instead the output is empty. If I replace 'read' with '*' it works, suggesting it is only matching strings, not the wildcard logic.

Using app.use(path, acl.middleware(0)) with different verbs causes last called verb to be reused

Let's say we use:

app.use('/api', acl.middleware(0)).

In file acl.js, in method middleware() line 495, variable 'actions' is checked, and if not truthy it is assigned to.

If this '/api' receives a call with 'get' method, it will execute accordingly and the closure variable 'actions' will receive the value of 'get'.
On subsequent hits with other methods (ex. 'post') the following lines (538-540) will fail:

if(!actions){
    actions = req.method.toLowerCase();
}

Because 'actions' is a closure variable and already assigned on the previous 'GET' invocation it will persist and be used on the current 'POST' invocation.

I propose a solution to be applied on line 514 like so:

 var _userId = userId, currentActions = actions, 

And also replace 'actions' with 'currentActions' throughout the function returned @ line 513.

Let me know what you think.

Possible to give permissions per property?

Would it be possible to give a permission, not just per resource but also per property? I.e. so a user can be limited to only seeing certain properties of a given resource, or only able to edit certain properties and view others.

Happy to fork and implement myself just would like to see how feasible it would be. Thanks.

Design patterns and examples

Can you suggest any good examples of sites, apps, etc. using this module? I'm still having a bit of trouble wrapping my head around some of the concepts of ACL, which is preventing me from fully integrating this module.

Specifically, I'm stuck on the following:

  • How to limit access when listing content. For example, user matt has view, list permission fro articles #1 - 100 but not articles 101-200. When matt queries GET api/articles how do I limit his access to articles 1-100? Would I access ACL to get his available resources, and set something like find().where('_id').in(mattsArticles). Would I even use ACL for this?
  • How to grant "guest" permissions, where there is no user or userId available for ACL to check against.
  • Where to do the updating or roles: is it better to do this in the controllers, or in the model schema, in pre.('save') middleware?

Thanks in advance for helping me understand this better!

Update npm

Npm repo has different files of same version of the node_acl
(just look at npm package.json)

Resources with periods cause issues when using mongodb as backend

If you use resource with periods, e.g., /path/myfile.ext, and mongodb, the periods are not encoded before being inserted into the DB. The result is the 'resources' collection that contains document with the resources names spilt on the period. E.g., "/path/myfile"/ext=True. This is causing a problem when trying to remove a resource because I assume the resource can't be found.

Workaround, encode periods with Unicode equivalent "\uff0E" in every caller to ACL api when using resources.

I recommend ACL encodes MongoDB control characters for the caller in mongodb-backend.js functions.

authentication errors not being displayed

acl.middleware is not correctly displaying the error messages for the errors 'User not authenticated' and 'Insufficient permissions to access resource'. I just get a 500 [object Object] being displayed.

I think the following might fix it: (seems to work for me)

var HttpError = function(errorCode, msg){
  this.errorCode = errorCode;
  this.message = msg

  Error.captureStackTrace(this, arguments.callee);
  return Error.call(this, msg);
}

removeAllow is removing all permissions

I am having an issue where I am removing a permission with

removeAllow('usr_230', 'aco_9990', 'view')

removes ALL of the role's permissions. In this case, the usr_230 role had 'view' and 'update' permissions, but after running the above code, it also no longer has 'update' permissions. Help please!

Routes with paramters

Hi,

I can't seem to understand how to work with express routes with parameters..
Basically what I'm trying to do is to make the following code to grant access to the resource while now it blocks it:

acl.allow('guestRole', 'blogs/:blogId', 'view')
acl.addUserRoles('guest', 'guestRole')

acl.isAllowed('guest', 'blogs/123', 'view', function(err, res){
    if(res){
        console.log("Granted")
    } else {
        console.log("Denied")
    }
});

I would expect the resource 'blogs/123' to be mapped to 'blogs/:blogId' but it looks like it does string comparison only, ignoring the fact that ':blogId' could be any number.

I hope I clearly explained my self :)

Thanks

Ignore prefix in middleware

I would like to add an option to the expressjs middleware to ignore a url prefix. This is useful if you are protecting resources that are beneath a namespace such as 'api/v1/posts/1'. In this case it makes sense to store the resource as 'posts/1' in the access control list rather than the full url.

For my own project I've made this change.

FreakTheMighty@6a6602e

I've also noted in my commit that I don't think the options should be passed as positional arguments as there are cases where you would want to specify some and not others. It looks like the contract module would need to be updated to support keyword arguments, is that correct?

MongoDB backend: Use a single collection to hold allows_RESOURCE

Using a mongodb backend, Is it really necessary to create one collection for every resource? My DB got bloated with a lot of collections after setting up just a few of my routes paths as resource names. And each of these collections only contain one document like this:

allows_/public/states
{
"_id": { "$oid" : "53488267b1d5dc96edf1c378" },
"key": "guest",
"get": true
}

Wouldn't it be better to have a single collection allows_resource with documents like this:
{
"_id": { "$oid" : "53488267b1d5dc96edf1c378" },
"name": "/public/states",
"key": "guest",
"get": true
}

If it were this way, I could define my resources by full paths, including any id's in them (like /books/123/pages/456) and not needing to normalize them (to /books/:id/pages/:id), and using the bundled middleware would be easier. But as it is now, the DB would get REALLY bloated :(

The rest is great though

experiencing same problem as issue #11

Im having what appears to be the same problem described in issue #11.

I have the following role defined

acl.allow('client', ['/user'], ['get', 'put', 'post'], function(err){
if(err){console.log('error adding permissions');}
});

when attempting to delete a user i get this error

object Object]
at new HttpError (/vagrant/www/project/node_modules/acl/lib/acl.js:489:11)
at /vagrant/www/project/node_modules/acl/lib/acl.js:532:14
at /vagrant/www/project/node_modules/acl/lib/acl.js:685:13
at Object.MemoryBackend.union (/vagrant/www/project/node_modules/acl/lib/memory-backend.js:78:7)
at /vagrant/www/project/node_modules/acl/lib/acl.js:679:22
at Object.MemoryBackend.union (/vagrant/www/project/node_modules/acl/lib/memory-backend.js:76:7)
at Acl._checkPermissions (/vagrant/www/project/node_modules/acl/lib/acl.js:666:16)
at Acl.areAnyRolesAllowed (/vagrant/www/project/node_modules/acl/lib/acl.js:387:10)
at /vagrant/www/project/node_modules/acl/lib/acl.js:359:12
at Object.MemoryBackend.get (/vagrant/www/project/node_modules/acl/lib/memory-backend.js:55:7)
DELETE /user/5262979da409582466000003 500 16ms

however when the role is configured with the requested resource (delete in this case) it works fine
acl.allow('client', ['/user'], ['get', 'put', 'post', 'delete'], function(err){
if(err){console.log('error adding permissions');}
});
Also everything appears to work correctly when using the isAllowed method

I run into the same error when using both redis and memory backends.

Question on tests and async'ly connecting backends

Hi, I am trying to add a MongoDB backend to node_acl, modeled on the redis backend. Since I don't use Redis, I have this question: does the backend assume a connection that has already been established? Because the tests start synchronously, without waiting for the database to be opened/connected etc. This doesn't work with MongoDB - I have to open the connection first and then the tests can be started. I can rewrite the test suite accordingly, but maybe I am doing something wrong.

Move contract.js into a npm module?

node_acl_knex issue
For the test I required the file incorrectly

contract = require('../node_modules/acl/lib/contract');

But the knex backend needs it in order to follow the Implements. For now I will copy the file to the lib folder to the node_acl_knex repo but maybe contract should be it's own module?

acl design question

I just added support for memcached as a backend. As a part of that, I was hoping to add regex support. Unfortunately, memcached does not support regexes, but as I dug deeper, I think it wouldn't help me even if memcached did support regexes. I think the basic problem preventing regex support is the regex string (the resource string in the acl declaration) is what is stored in the backend. We have no way of matching the user requested resource to the regex without looking up all resources for the user's roles and comparing all of them to see if any match. Am I understanding the problem correctly?

As I thought about this more, I began to wonder why we are storing the roles and permissions in a backend at all. I understand why we need to link a user to a role, but that can be done in the session. Was there a specific design consideration in putting the resources in the backend instead of leaving those in memory?

array version of acl.allow throws error

Hi,

If I call acl.allow with following parameters:

acl.allow([{roles:'guest',
allows:
{resources: ['/test1','/test2'], permissions: 'get'}
},
{roles:'user',
allows:[
{resources: ['/test1','/test2'], permissions: 'get'},
{resources: ['/test3','/test4'], permissions: ['get', 'put']},
{resources:'/test4', permissions:['get', 'post']}
]
}
], function(err) {

});

It throws following error: TypeError: Object # has no method 'forEach'

The error is originating from acl.js line: 551.

Thanks,
Syed.

mongodbBackend with mongoose??

Hi. I'm trying to configure ACL with Mongoose module instead of the MongoDB module, does it work? I'm getting this error when defining roles with acl.allow:
TypeError: Object # has no method 'collection'.
I printed the acl object, here is the output:
ACL CONFIG { logger: undefined,
11:07:27 web.1 | backend:
11:07:27 web.1 | { db:
11:07:27 web.1 | { connections: [Object],
11:07:27 web.1 | plugins: [],
11:07:27 web.1 | models: [Object],
11:07:27 web.1 | modelSchemas: [Object],
11:07:27 web.1 | options: [Object] },
11:07:27 web.1 | prefix: 'acl_' } }
Thanks in advanced for your help.

Great

This is not an issue, just a comment: keep up the good work, this look promising

Functions as optional arguments

Please make a callback function as an optional argument when invoking acl.methods.
For now it's throw the "Parameter missmatch" error if function is not passed.

And don't work examples from the documentation like acl.allow ('guest', 'blogs', 'view')
I'm use mongo-backend and code from github (not npm, because of #22)

MongoDb backend and middleware bad actions

Hi,

With the MongoDb backend, the action parameter of the isAllowed function should be :

  • create
  • edit
  • delete
  • view

Or in the middleware, if the action param is not specified, the param is extract form the request method:

if (!actions) {
actions = req.method.toLowerCase();
}

So, actually, if param is not explicitly specified, isAllowed will always return false.

Readme is missing text for line: redisBackend = require(');

First of all, Thanks a lot for supporting memory backend.

Please update the readme. Right now I have no idea how to instantiate a MemoryBackend as the readme is missing characters at that line.

Can you please update it and let me know how can I instantiate MemoryBackend?

Thanks and really appreciate your commitment to improve this wonderful library.

problem with acl.middleware

Hello,

I try to execute an exemple like this :

var acl = require('acl');

acl = new acl(new acl.memoryBackend());

// error checking callback
var cb = function(err){
if(err) console.log(err);
}
acl.allow([{
roles: 'admin',
allows: [{
resources: 'db',
permissions: ['write', 'delete','read']
}]
},{
roles: 'user',
allows: [{
resources: 'home',
permissions: 'read'
}]
}],cb);

// assing user ids to roles
acl.addUserRoles("john","user",cb);
acl.addUserRoles("mary","admin",cb);

app.get('/home', acl.middleware(1, 'john', 'write'), home.index); // home.index is my route

and i got the following error :
Express
500 [object Object]
at new HttpError (E:\00_Work\99_Programmes\Enide-Studio\ws\TestProject\node_modules\acl\lib\acl.js:489:11)
at E:\00_Work\99_Programmes\Enide-Studio\ws\TestProject\node_modules\acl\lib\acl.js:532:14
at E:\00_Work\99_Programmes\Enide-Studio\ws\TestProject\node_modules\acl\lib\acl.js:685:13
at Object.MemoryBackend.union (E:\00_Work\99_Programmes\Enide-Studio\ws\TestProject\node_modules\acl\lib\memory-backend.js:78:7)
at E:\00_Work\99_Programmes\Enide-Studio\ws\TestProject\node_modules\acl\lib\acl.js:679:22
at Object.MemoryBackend.union (E:\00_Work\99_Programmes\Enide-Studio\ws\TestProject\node_modules\acl\lib\memory-backend.js:78:7)
at Acl._checkPermissions (E:\00_Work\99_Programmes\Enide-Studio\ws\TestProject\node_modules\acl\lib\acl.js:666:16)
at Acl.areAnyRolesAllowed (E:\00_Work\99_Programmes\Enide-Studio\ws\TestProject\node_modules\acl\lib\acl.js:387:10)
at E:\00_Work\99_Programmes\Enide-Studio\ws\TestProject\node_modules\acl\lib\acl.js:359:12
at Object.MemoryBackend.get (E:\00_Work\99_Programmes\Enide-Studio\ws\TestProject\node_modules\acl\lib\memory-backend.js:55:7)

How use this middleware to controle the route access?

Thanks for your help :)

Cannot read property 'length' of undefined when i do `isAllowed`

> acl.isAllowed('guest','blogs','view', function(err, res){
... if(res){
..... console.log('........isallowed.........');
..... }
... })
TypeError: Cannot read property 'length' of undefined
    at Acl.isAllowed (/Users/kaushik/dev/my_repos/railway_routes/node_modules/acl/lib/acl.js:358:19)
    at Object.MemoryBackend.get (/Users/kaushik/dev/my_repos/railway_routes/node_modules/acl/lib/memory-backend.js:57:7)
    at Acl.isAllowed (/Users/kaushik/dev/my_repos/railway_routes/node_modules/acl/lib/acl.js:355:16)
    at repl:1:6
    at REPLServer.self.eval (repl.js:109:21)
    at Interface.<anonymous> (repl.js:248:12)
    at Interface.EventEmitter.emit (events.js:96:17)
    at Interface._onLine (readline.js:200:10)
    at Interface._normalWrite._line_buffer (readline.js:309:12)
    at Array.forEach (native)

Support for MySQL

How can this module be used to provide support for an installation with a MySQL database?

Removing a role removes the entire "allows" document.

Using MongoDB as a backend, I'm doing this (in the REPL):

acl.allow(['a', 'b', 'c'], ['pages', 'blogs', 'texts'], ['read', 'view', 'see'], cb)

MongoDB now has:

db.allows_blogs.find()
{
  "_id": ObjectId("5212aa4823f2e3d825dfc2d8"),
  "key": [
    "a",
    "b",
    "c"
  ],
  "read": true,
  "see": true,
  "view": true
}

Now I want to remove a:

acl.removeRole('a', cb)

MongoDB document is gone:

db.allows_blogs.find()
>

Is this behavior expected?

Implement plugin for Hapi based framework

Currently only Express based middle-ware is implemented in the library. This is a great library and should be available for other framework also. Is it possible to implement plugin for Hapi based framework?

Throws parameter mismatch error

var acl = require('acl');

// Using the memory backend
acl = new acl(new acl.memoryBackend());

acl.allow([{roles:['guest','member'],
allows:[
{resources:'blogs', permissions:'get'},
{resources:['forums','news'], permissions:['get','put','delete']}]
},
{roles:['gold','silver'],
allows:[
{resources:'cash', permissions:['sell','exchange']},
{resources:['account','deposit'], permissions:['put','delete']}]
}
]);

It throws

Parameter missmatch.
Input:
( [object Object],[object Object]{array} )
Accepted:
([object Arguments])
([object Arguments])

throw new Error('Broke parameter contract');

Middleware question

How should i use the middleware as GLOBAL

i'm trying

app.use(acl.middleware());

but the midleware stores the request ACTION in a closure variable named "actions" and doesn't update after the first request :
line 512 of acl.js

if(!actions){
      console.log("method:" + req.method.toLowerCase());
      actions = req.method.toLowerCase();
    }

Is this the normal behavior or i'm doing something wrong?

because with this behavior i need to call the midleware function for every action that i need to protect.

module name has a '/' in the begining

Hi,

acl.js line: 510: resource = url.split('/').slice(0,numPathComponents+1).join('/');

here, if my link is: localhost:3000/user/mylink and I call middleware(1), the resource extracted by the above code line is: '/user'. So in order to make it work, in calls to allow, I have to define permissions like this:
acl.allow('user',
'/user', >>>> Notice extra slash at the beginning.
'*',
function(err) {

As the documentation does not say something about this, is it the way you decided to keep resources? If you dont use the slash in the beginning, it will be more intuitive for users of the lib to list permissions for resources.

Thanks,
Syed.

isAllowed not working as expected

Firstly I'd like to say thanks for a great contribution. It's exactly what I 'think' i'm looking for.

I can't get it working though sadly. isAllowed doesn't seem to be working as expected. Here's my setup.

  1. I create the instance (using mongoose) and add a role and resource.
    https://github.com/Webdesignwill/siteify/blob/master/config/acl.js
var acl = require('acl'), node_acl;
module.exports.initialize = function (app, db, config) {
  node_acl = new acl(
    new acl.mongodbBackend(db.connection.db, 'acl_')
  );
  node_acl.allow([{
    roles : 'admin',
    allows : [{
      resources: '/private',
      permissions: ['create', 'remove', 'update', 'delete']
    }]
  }]);
};
module.exports.node_acl = function () {
  return node_acl;
};
  1. Add some dummy routes
    https://github.com/Webdesignwill/siteify/blob/master/config/routes.js
app.post('/getPermissions', Controllers.User.getPermissions);
app.get('/private', function (req, res, next) {
  // for the hell of it
});
  1. Add controller for registering a user and create the role to 'admin
    https://github.com/Webdesignwill/siteify/blob/master/app/controllers/user.js
module.exports.register = function (req, res, next) {
  User.register({
      displayname : req.body.displayname,
      email : req.body.email,
      password : req.body.password
    }, function (err, user) {
    if (err) return next(err);
    node_acl.addUserRoles(user._id.toString(), 'admin');
    res.json(parseUserObject(user));
  });
};
  1. Then request the getPermissions url for testing returning the permissions.
    https://github.com/Webdesignwill/siteify/blob/master/app/controllers/user.js
module.exports.getPermissions = function (req, res, next) {
  User.findOne({ _id : req.body.id }, function (err, user) {
    if(err) return next(err);
    // This doesnt work either.
    // node_acl.allowedPermissions(user._id.toString(), '/private', function (err, permissions) {
    //   res.json(permissions);
    // });

    // req.body.permission is one of these ['create', 'remove', 'update', 'delete'] none of which work
    node_acl.isAllowed(user._id.toString(), '/private', req.body.permission, function (err, response) {
      res.json({result : response});
    });
  });
};

in Mongo browser
db.users.find() returns :

{ "_id" : ObjectId("5463bbfc499e84ff08cd340c"), "email" : "[email protected]", "hashed_password" : "$2a$10$Xh7beAx4iVv68kWUfw1bW.wMFPWuZC52mP679hdnBBFONCClmHBSm", "company" : "Company name", "lastname" : "Last name", "firstname" : "First name", "displayname" : "Akaskero", "__v" : 0 }

db.acl_meta.find() returns :

{ "_id" : ObjectId("5463bbfc01f9139ba069b17c"), "key" : "users", "5463bbfc499e84ff08cd340c" : true }

db.acl_users.find() returns :

{ "_id" : ObjectId("5463bbfc01f9139ba069b17d"), "key" : "5463bbfc499e84ff08cd340c", "admin" : true }

Expected : {'/private' : ['create', 'remove', 'update', 'delete']}
Actual : {'/private' : []}

I can't figure out from countless examples what I'm missing here. Any help would be gratefully appreciated.

Resource collection still exists in mongodb after removeResource

When adding a resource to a role a collection is created in the mongodb such as

acl_allows_/todos/5330457a7f15e8ed5acc9978

but if that item is deleted I no longer require any acl for it as it doesn't exist.

I've used removeAllow and removeResource but the collection still exists in the database.

Not sure if I'm doing something wrong or if this a bug.

mongodump. aclallows_* collections are not included

Looks like if we do a mongodump the collection names that start with aclallows_ are not included in the dump.

Can it be because the names includes /?

I have tables that are called for instance
aclallows_/sites/0

Based on MongoDB null and / should not be used in the name of the collection. I would highly suggest that the code be refactored to use a different character besides the /. Consider a legal place holder in the collection name.

Possibility for adding custom checks

I'm wondering if its possible to create custom checks, for example https://github.com/djvirgen/virgen-acl#custom-assertions

This would allow for checking access on a more fine grained level. For example, users are allows to comment on blogs. However only the user who created the blog can edit it.

I'm imaging the code would look something like:

acl.isAllowed('author', 'blogs', 'view', function(err, res){
    if(res){
        console.log("Role author is allowed to view blogs")
    }
}

And as middleware:

acl.middleware('author', 'blogs', 'edit')

Which all looks normal, except that in the case of blogs, edit is a custom function we've defined.

acl.defineResourceAction('blogs', 'edit', function(user, resource, next) {
    // rather than allowing all access by role, we are checking by user id
    if(resource.author.id === user.userId) {
        next(); // continue middleware
    } else {
        acl.notAuthorizedHandler(); // allows user to override with custom handler
    }
});

In the above examples, because we've defined a function for 'edit' it would be used instead of the standard check.

Does this make sense? Is it a good approach?

If a role does not have permission over a certain resource, the lib crashes

If a role does not have permission over a certain resource, the lib crashes with the following stacktrace:

TypeError: Cannot read property 'length' of undefined
at E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\node_modules\acl\lib\acl.js:677:28
at Object.union (E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\node_modules\acl\lib\memory-backend.js:78:7)
at E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\node_modules\acl\lib\acl.js:674:22
at Object.union (E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\node_modules\acl\lib\memory-backend.js:76:7)
at [object Object]._checkPermissions (E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\node_modules\acl\lib\acl.js:661:16)
at [object Object].areAnyRolesAllowed (E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\node_modules\acl\lib\acl.js:382:10)
at E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\node_modules\acl\lib\acl.js:354:12
at Object.get (E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\node_modules\acl\lib\memory-backend.js:55:7)
at [object Object].isAllowed (E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\node_modules\acl\lib\acl.js:350:16)
at E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\node_modules\acl\lib\acl.js:519:9

Please have a look.

Support for regexes?

This is similar to #64

I have a situation where a user should not have access to a parent resource but should have access to a child resource. ie: /parent/{parentId}/child

In this situation, the user does not have access to /parent/{parentId}, but they do need to access child. They do not have the child id so they can't access /child/{childId}. However, there is a relationship between parent and child and so we want to access it as a subresource.

I can't seem to find a way to do this today. If it's not available, could regexes be added to support this?

Similarly, I had a route /resource/:resourceId, and I allowed access to that resource like this:

acl.allow([
    {
        roles:['role'],
        allows:[           
            {resources:['/resource'], permissions:['get', 'post', 'put']}
        ]
    }
])

This didn't work. When I inspected the acl.middleware, it's taking the req.url and matching that to the permissions. Of course, my permissions do not contain an allowance for /resource with a specific id. I tried using /resource/* but that didn't work either. This is another place I think regexes could really help.

Readme is not clear, how do I allow only user 12345 to access user/12345?

from the Readme I see how to secure user/, but not specifically when a user id matches req.params.id. I've been playing with nodejs for only a little while, so if this is something simple that doesn't need to be in the Readme, sorry!

Also, lets say I want to only let people with access to hackathon/5 permission to access project/1, project/2, project/3. A user with no access to hackathon/5 would not be able to access these projects. Is that possible with this acl library?

permissions table available in Postgresql DB

I have permissions table with all users permission informations available in Postgresql DB, is it possible to avoid redis and connect to Postgresql DB to pull permission data ?. please advice.

logger not defined

Hi,

I am instantiating Acl using memory backend but when the call goes into middleware, I am getting following error:

ReferenceError: logger is not defined
at E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\node_modules\acl\lib\acl.js:518:5
at callbacks (E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\node_modules\express\lib\router\index.js:165:11)
at E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\server.js:28:11
at Object.end (E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\node_modules\acl\lib\memory-backend.js:34:5)
at [object Object].addUserRoles (E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\node_modules\acl\lib\acl.js:72:16)
at E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\server.js:27:9
at callbacks (E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\node_modules\express\lib\router\index.js:165:11)
at param (E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\node_modules\express\lib\router\index.js:139:11)
at pass (E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\node_modules\express\lib\router\index.js:146:5)
at Router._dispatch (E:\SoftwareVenture\Projects\NodeMobileBackend\mobileshopper\moobs\node_modules\express\lib\router\index.js:173:4)

The Acl function's signature allows to skip logger and here is how I am instantiating my Acl object:

var acl = require('acl'),
memoryBackend = require('./node_modules/acl/lib/memory-backend');

var myacl = new acl(new memoryBackend(), null);

I dont see in tests.js that you are calling into middleware() so that may be the reason why you missed this. Can you please guide me how I can workaround this issue or please fix this issue soon.

Thanks.

Give a single user a specific permissions on a resource

Does node ACL allow giving specific permissions to a user on a resource?

For example their role might have "read" access, but once the user has been invited to the resource they are given "edit" permission.

The only solution I can think of is to create a role for every user, but this seems a bit messy. Is there any other way to do this?

Deny of resources

Hello,

when do you think denying of stuff will be finished?

Greetings Kersten

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.