NODE ACL - 用于Nodejs的权限访问控制

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


Dependency Status
Dependency Status

devDependency 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

<a name="addUserRoles"/>

addUserRoles( userId, roles, function(err) )

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.

<a name="removeUserRoles"/>

removeUserRoles( userId, roles, function(err) )

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.

<a name="userRoles" />

userRoles( userId, function(err, roles) )

Return all the roles from a given user.

Arguments

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

<a name="roleUsers" />

roleUsers( rolename, function(err, users) )

Return all users who has a given role.

Arguments

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

<a name="hasRole" />

hasRole( userId, rolename, function(err, hasRole) )

Return boolean whether user has the role

Arguments

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

<a name="addRoleParents" />

addRoleParents( role, parents, function(err) )

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.

<a name="removeRoleParents" />

removeRoleParents( role, parents, function(err) )

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].

<a name="removeRole" />

removeRole( role, function(err) )

Removes a role from the system.

Arguments

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

<a name="removeResource" />

removeResource( resource, function(err) )

Removes a resource from the system

Arguments

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

<a name="allow" />

allow( roles, resources, permissions, function(err) )

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.

<a name="removeAllow" />

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

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}

<a name="allowedPermissions" />

allowedPermissions( userId, resources, function(err, obj) )

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.

<a name="isAllowed" />

isAllowed( userId, resource, permissions, function(err, allowed) )

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.

<a name="areAnyRolesAllowed" />

areAnyRolesAllowed( roles, resource, permissions, function(err, allowed) )

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.

<a name="whatResources" />

whatResources(role, function(err, {resourceName: [permissions]})

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.

<a name="middleware" />

middleware( [numPathComponents, userId, permissions] )

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())

<a name="backend" />

backend( db, [prefix] )

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 manuel@optimalbits.com

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.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351

推荐阅读更多精彩内容

  • **2014真题Directions:Read the following text. Choose the be...
    又是夜半惊坐起阅读 9,442评论 0 23
  • 最近项目需求中需要对statusBar做透明穿透处理,刚开始没想太多,随着开发进行,发现坑位那绝对是满满的,主要就...
    浪够_阅读 1,414评论 1 3
  • 1.创建web API项目 2.创建项目 3.添加模型 4.添加控制器添加前 添加 添加代码: 5.运行首页 6....
    冰点雨阅读 6,124评论 1 0
  • 平常就爱关注灵性成长与宇宙真相这方面的资讯,有天晚上,无意中找到了一个名为准备转变的微信公众号,里面分享了鸣响...
    Freyayanwen阅读 3,124评论 0 5
  • 梦 梦里我做了一场修行 关于你的眉眼和嬉笑 关于昨晚夜半惊醒的秘密 我被一场大梦压弯了脊梁 一遍又一遍 谁在我耳边...
    月亮住在太阳上阅读 268评论 2 1