@luislobo/rbac2
Advanced tools
Comparing version 1.1.2 to 1.1.3
213
index.js
@@ -1,140 +0,143 @@ | ||
var _ = require('lodash'), | ||
async = require('async'); | ||
const _ = require('lodash'); | ||
const async = require('async'); | ||
function RBAC(rules, checkFullPath, cacheTrees) { | ||
this.rules = rules; | ||
this.checkFullPath = !!checkFullPath; | ||
this.cacheTrees = !!cacheTrees; | ||
if (this.cacheTrees) { | ||
this.trees = {}; | ||
} | ||
this.rules = rules; | ||
this.checkFullPath = !!checkFullPath; | ||
this.cacheTrees = !!cacheTrees; | ||
if (this.cacheTrees) { | ||
this.trees = {}; | ||
} | ||
} | ||
RBAC.prototype = { | ||
'check': function (role, permission, params, cb) { | ||
var result = false; | ||
'check': function(role, permission, params, cb) { | ||
let result = false; | ||
// If params not given, consider last argument as callback | ||
if (arguments.length < 4) { | ||
cb = params; | ||
params = {}; | ||
} | ||
// If params not given, consider last argument as callback | ||
if (arguments.length < 4) { | ||
cb = params; | ||
params = {}; | ||
} | ||
// Create a rbac tree from the current role | ||
var tree = this.getTree(role); | ||
// Create a rbac tree from the current role | ||
const tree = this.getTree(role); | ||
// Find all paths from root to permission | ||
var paths = findPaths(tree, permission); | ||
// Sort by shortest first (i.e. no. of nodes) | ||
paths = _.sortBy(paths, function (path) { | ||
return path.length; | ||
}); | ||
// Find all paths from root to permission | ||
let paths = findPaths(tree, permission); | ||
// Sort by shortest first (i.e. no. of nodes) | ||
paths = _.sortBy(paths, function(path) { | ||
return path.length; | ||
}); | ||
var checkFullPath = this.checkFullPath; | ||
// Check each path serially | ||
async.eachSeries(paths, function (path, cb) { | ||
checkPath(path, 1, params, checkFullPath, function (err, res) { | ||
if (!err && res) { | ||
result = true; | ||
return cb(new BreakError('passed')); | ||
} | ||
const checkFullPath = this.checkFullPath; | ||
// Check each path serially | ||
async.eachSeries(paths, function(path, cb) { | ||
checkPath(path, 1, params, checkFullPath, function(err, res) { | ||
if (!err && res) { | ||
result = true; | ||
return cb(new BreakError('passed')); | ||
} | ||
cb(err, null); | ||
}); | ||
}, function (err) { | ||
if (err && err instanceof BreakError) { | ||
return cb(null, result); | ||
} | ||
cb(err, null); | ||
}); | ||
}, function(err) { | ||
if (err && err instanceof BreakError) { | ||
return cb(null, result); | ||
} | ||
cb(err, result); | ||
}); | ||
}, | ||
'getTree': function (role) { | ||
if (!this.cacheTrees) { | ||
return { | ||
'value' : role, | ||
'children': toTree(role, this.rules) | ||
}; | ||
} | ||
if (this.trees[role]) { | ||
return this.trees[role]; | ||
} | ||
this.trees[role] = { | ||
'value' : role, | ||
'children': toTree(role, this.rules) | ||
}; | ||
return this.trees[role]; | ||
cb(err, result); | ||
}); | ||
}, | ||
'getTree': function(role) { | ||
if (!this.cacheTrees) { | ||
return { | ||
'value': role, | ||
'children': toTree(role, this.rules), | ||
}; | ||
} | ||
if (this.trees[role]) { | ||
return this.trees[role]; | ||
} | ||
this.trees[role] = { | ||
'value': role, | ||
'children': toTree(role, this.rules), | ||
}; | ||
return this.trees[role]; | ||
}, | ||
}; | ||
/* eslint-disable-next-line no-unused-vars */ | ||
function BreakError(msg) { | ||
Error.apply(this, arguments); | ||
Error.apply(this, arguments); | ||
} | ||
BreakError.prototype = new Error(); | ||
function toTree(role, rules) { | ||
var self = arguments.callee; | ||
const self = arguments.callee; | ||
return _.reduce(rules, function (arr, rule) { | ||
if (rule.a === role) { | ||
arr.push({ | ||
'value' : rule.can, | ||
'when' : rule.when, | ||
'children': self(rule.can, rules) | ||
}); | ||
} | ||
return arr; | ||
}, []); | ||
return _.reduce(rules, function(arr, rule) { | ||
if (rule.a === role) { | ||
arr.push({ | ||
'value': rule.can, | ||
'when': rule.when, | ||
'children': self(rule.can, rules), | ||
}); | ||
} | ||
return arr; | ||
}, []); | ||
} | ||
function findPaths(root, permission) { | ||
var self = arguments.callee; | ||
var paths = []; | ||
const self = arguments.callee; | ||
const paths = []; | ||
if (root.value === permission) { | ||
paths.push([root]); | ||
} else { | ||
_.each(root.children, function (child) { | ||
var childpaths = self(child, permission); | ||
if (root.value === permission) { | ||
paths.push([root]); | ||
} else { | ||
_.each(root.children, function(child) { | ||
const childpaths = self(child, permission); | ||
_.each(childpaths, function (childpath) { | ||
var path = [root]; | ||
path.push.apply(path, childpath); | ||
paths.push(path); | ||
}); | ||
}); | ||
} | ||
_.each(childpaths, function(childpath) { | ||
const path = [root]; | ||
path.push.apply(path, childpath); | ||
paths.push(path); | ||
}); | ||
}); | ||
} | ||
return paths; | ||
return paths; | ||
} | ||
function checkPath(path, index, params, checkFullPath, cb) { | ||
if (index >= path.length) { | ||
// reached end | ||
return cb(null, true); | ||
} | ||
if (index >= path.length) { | ||
// reached end | ||
return cb(null, true); | ||
} | ||
var self = arguments.callee; | ||
var node = path[index]; | ||
const self = arguments.callee; | ||
const node = path[index]; | ||
if (!node.when) { | ||
if (!checkFullPath || !node.children) { | ||
// no condition to get access to this node, | ||
// permission granted | ||
return cb(null, true); | ||
} else { | ||
return self(path, index + 1, params, checkFullPath, cb); | ||
} | ||
if (!node.when) { | ||
if (!checkFullPath || !node.children) { | ||
// no condition to get access to this node, | ||
// permission granted | ||
return cb(null, true); | ||
} else { | ||
// test condition associated with current node | ||
node.when(params, function (err, res) { | ||
if (!err && res) { | ||
// reached this node, move on to next | ||
self(path, index + 1, params, checkFullPath, cb); | ||
} else { | ||
return cb(err, false); | ||
} | ||
}); | ||
return self(path, index + 1, params, checkFullPath, cb); | ||
} | ||
} else { | ||
// test condition associated with current node | ||
node.when(params, function(err, res) { | ||
if (!err && res) { | ||
// reached this node, move on to next | ||
self(path, index + 1, params, checkFullPath, cb); | ||
} else { | ||
return cb(err, false); | ||
} | ||
}); | ||
} | ||
} | ||
module.exports = RBAC; |
{ | ||
"name": "@luislobo/rbac2", | ||
"version": "1.1.2", | ||
"version": "1.1.3", | ||
"description": "Simple RBAC checker with support for context checks.", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "PATH=$(npm bin):$PATH mocha --recursive --full-trace --bail --check-leaks --reporter list tests" | ||
"lint": "eslint --format codeframe '**/*.js'", | ||
"test": "npm run lint && mocha --recursive --full-trace --bail --check-leaks --reporter list tests" | ||
}, | ||
@@ -34,4 +35,5 @@ "keywords": [ | ||
"devDependencies": { | ||
"mocha": "^9.0.1" | ||
"mocha": "^9.0.1", | ||
"eslint": "^7.29.0" | ||
} | ||
} |
# rbac2 | ||
[![NPM version](https://badge.fury.io/js/rbac2.svg)](http://badge.fury.io/js/rbac2) [![Build Status](https://travis-ci.org/ramniquesingh/rbac2.svg?branch=master)](https://travis-ci.org/ramniquesingh/rbac2) | ||
[![NPM version](https://badge.fury.io/js/%40luislobo%2Frbac2.svg)](https://badge.fury.io/js/%40luislobo%2Frbac2) [![Build Status](https://travis-ci.org/luislobo/rbac2.svg?branch=master)](https://travis-ci.org/luislobo/rbac2) | ||
@@ -4,0 +4,0 @@ Simple RBAC checker with support for context checks. |
@@ -1,100 +0,100 @@ | ||
var assert = require('assert'), | ||
RBAC = require('../'); | ||
var assert = require('assert'); | ||
var RBAC = require('../'); | ||
var rules = [ | ||
{a: 'visitor' , can: 'read articles'}, | ||
{a: 'user' , can: 'vote on articles'}, | ||
{a: 'article editor' , can: 'edit article'}, | ||
{a: 'user' , can: 'article editor', when: function (params, cb) { | ||
if (params.userId === 2) { | ||
return cb(null, true); | ||
} | ||
cb(null, false); | ||
}}, | ||
{a: 'admin' , can: 'user'}, | ||
{a: 'admin' , can: 'article editor'}, | ||
{a: 'superadmin' , can: 'delete user'}, | ||
{a: 'superadmin' , can: 'admin'}, | ||
{a: 'user' , can: 'visitor'}, | ||
{a: 'user' , can: 'read articles'} | ||
{a: 'visitor' , can: 'read articles'}, | ||
{a: 'user' , can: 'vote on articles'}, | ||
{a: 'article editor' , can: 'edit article'}, | ||
{a: 'user' , can: 'article editor', when: function (params, cb) { | ||
if (params.userId === 2) { | ||
return cb(null, true); | ||
} | ||
cb(null, false); | ||
}}, | ||
{a: 'admin' , can: 'user'}, | ||
{a: 'admin' , can: 'article editor'}, | ||
{a: 'superadmin' , can: 'delete user'}, | ||
{a: 'superadmin' , can: 'admin'}, | ||
{a: 'user' , can: 'visitor'}, | ||
{a: 'user' , can: 'read articles'} | ||
]; | ||
describe('RBAC', function () { | ||
var rbac = new RBAC(rules); | ||
var rbac = new RBAC(rules); | ||
describe('check', function () { | ||
it('should work with no-condition paths', function (done) { | ||
rbac.check('admin', 'read articles', function (err, res) { | ||
if (err) { | ||
throw err; | ||
} | ||
assert.ok(res); | ||
done(); | ||
}); | ||
}); | ||
describe('check', function () { | ||
it('should work with no-condition paths', function (done) { | ||
rbac.check('admin', 'read articles', function (err, res) { | ||
if (err) { | ||
throw err; | ||
} | ||
assert.ok(res); | ||
done(); | ||
}); | ||
}); | ||
it('should work with conditional functions - fail case', function (done) { | ||
rbac.check('user', 'edit article', function (err, res) { | ||
if (err) { | ||
throw err; | ||
} | ||
it('should work with conditional functions - fail case', function (done) { | ||
rbac.check('user', 'edit article', function (err, res) { | ||
if (err) { | ||
throw err; | ||
} | ||
assert.ok(!res); | ||
done(); | ||
}); | ||
}); | ||
assert.ok(!res); | ||
done(); | ||
}); | ||
}); | ||
it('should work with conditional functions - pass case', function (done) { | ||
rbac.check('user', 'edit article', { | ||
userId: 2 | ||
}, function (err, res) { | ||
if (err) { | ||
throw err; | ||
} | ||
it('should work with conditional functions - pass case', function (done) { | ||
rbac.check('user', 'edit article', { | ||
userId: 2 | ||
}, function (err, res) { | ||
if (err) { | ||
throw err; | ||
} | ||
assert.ok(res); | ||
done(); | ||
}); | ||
}); | ||
assert.ok(res); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('RBAC with caching', function () { | ||
var rbac = new RBAC(rules, false, true); | ||
var rbac = new RBAC(rules, false, true); | ||
describe('check', function () { | ||
it('should work with no-condition paths', function (done) { | ||
rbac.check('admin', 'read articles', function (err, res) { | ||
if (err) { | ||
throw err; | ||
} | ||
assert.ok(res); | ||
done(); | ||
}); | ||
}); | ||
describe('check', function () { | ||
it('should work with no-condition paths', function (done) { | ||
rbac.check('admin', 'read articles', function (err, res) { | ||
if (err) { | ||
throw err; | ||
} | ||
assert.ok(res); | ||
done(); | ||
}); | ||
}); | ||
it('should work with conditional functions - fail case', function (done) { | ||
rbac.check('user', 'edit article', function (err, res) { | ||
if (err) { | ||
throw err; | ||
} | ||
it('should work with conditional functions - fail case', function (done) { | ||
rbac.check('user', 'edit article', function (err, res) { | ||
if (err) { | ||
throw err; | ||
} | ||
assert.ok(!res); | ||
done(); | ||
}); | ||
}); | ||
assert.ok(!res); | ||
done(); | ||
}); | ||
}); | ||
it('should work with conditional functions - pass case', function (done) { | ||
rbac.check('user', 'edit article', { | ||
userId: 2 | ||
}, function (err, res) { | ||
if (err) { | ||
throw err; | ||
} | ||
it('should work with conditional functions - pass case', function (done) { | ||
rbac.check('user', 'edit article', { | ||
userId: 2 | ||
}, function (err, res) { | ||
if (err) { | ||
throw err; | ||
} | ||
assert.ok(res); | ||
done(); | ||
}); | ||
}); | ||
assert.ok(res); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
21384
15
210
2