express-reaccess
Advanced tools
Comparing version 0.0.4 to 0.0.5
{ | ||
"name": "express-reaccess", | ||
"version": "0.0.4", | ||
"version": "0.0.5", | ||
"description": "Express/Connect middleware to manage API access on a RegExp basis", | ||
@@ -5,0 +5,0 @@ "main": "src/index.js", |
@@ -14,3 +14,3 @@ # express-reaccess | ||
rightsProp: 'user.rights', | ||
userProp: 'user' | ||
valuesProp: 'user' | ||
})); | ||
@@ -88,8 +88,8 @@ | ||
req.user.rights = [{ | ||
path: '/organizations/:orgId/users.json' | ||
methods: reaccess.GET | reaccess.POST | ||
path: '/organizations/:orgId/users.json' | ||
methods: reaccess.GET | reaccess.POST | ||
}]; | ||
``` | ||
### options.userProp | ||
### options.valuesProp | ||
Type: `String` | ||
@@ -103,8 +103,8 @@ | ||
req.user.rights = [{ | ||
path: '/organizations/:org.id/users.json' | ||
methods: reaccess.GET | reaccess.POST | ||
path: '/organizations/:org.id/users.json' | ||
methods: reaccess.GET | reaccess.POST | ||
}]; | ||
``` | ||
He will be able to access this URI /organizations/1/users.json if a previously | ||
set middleware have set the `req.user.org.id` to `1` and `options.userProp` to | ||
set middleware have set the `req.user.org.id` to `1` and `options.valuesProp` to | ||
`'user'`. | ||
@@ -171,3 +171,39 @@ | ||
## Want more ? | ||
express-reaccess supports multivalued path templates. The following rights/values couple: | ||
``` | ||
req.user.rights = [{ | ||
path: '/organizations/:organizations.#.id/users/:id.json' | ||
methods: reaccess.GET | reaccess.POST | ||
}]; | ||
req.user.organizations = [{ | ||
id: 1, | ||
name: 'FranceJS' | ||
}, { | ||
id: 2, | ||
name: 'ChtiJS' | ||
}]; | ||
req.user.id = 3; | ||
``` | ||
Will give access to GET/POST /organizations/1/users/3.json and | ||
GET/POST /organizations/2/users/3.json. | ||
You also can use the express-reaccess middleware several times to bring a | ||
fine access control of your API to your consumers: | ||
``` | ||
// Access control based on the pricing plan of the user organization | ||
app.use(reaccess({ | ||
rightsProp: 'pricingPlan.rights', | ||
valuesProp: 'organization' | ||
})); | ||
// Access control based on the user rights set per each organization administrator | ||
app.use(reaccess({ | ||
rightsProp: 'user.rights', | ||
valuesProp: 'user' | ||
})); | ||
``` | ||
## Stats | ||
@@ -174,0 +210,0 @@ |
@@ -8,2 +8,3 @@ var escRegExp = require('escape-regexp-component'); | ||
options.rightsProp = options.rightsProp || 'user.rights'; | ||
options.valuesProp = options.valuesProp || options.userProp; // Legacy | ||
options.errorConstructor = options.errorConstructor || Error; | ||
@@ -14,26 +15,31 @@ options.accessErrorMessage = options.accessErrorMessage || 'Unauthorized access!'; | ||
var rights = getValues([req], options.rightsProp)[0]; | ||
var user; | ||
var rootValues; | ||
if(!(rights && rights instanceof Array)) { | ||
throw new Error('The rights property must be an array.'); | ||
} | ||
if(options.userProp) { | ||
user = getValues([req], options.userProp)[0]; | ||
if(options.valuesProp) { | ||
rootValues = getValues([req], options.valuesProp); | ||
} | ||
if(rights.some(function(right) { | ||
var path = ''; | ||
if(!(right.methods && right.methods&reaccess[req.method.toUpperCase()])) { | ||
if(!('undefined' !== typeof right.methods && | ||
'undefined' !== typeof right.path && | ||
right.methods&reaccess[req.method.toUpperCase()])) { | ||
return false; | ||
} | ||
path = user ? | ||
right.path.replace(/(.*\/|^):([a-z0-9_\-\.\*\@\#]+)(\/.*|$)/, | ||
function($, $1, $2, $3) { | ||
var values = getValues([user], $2); | ||
if(values.length) { | ||
return $1 + (1 === values.length ? | ||
escRegExp(values[0]) : | ||
'(' + values.map(escRegExp).join('|') + ')') + $3; | ||
} | ||
return ''; | ||
}) : | ||
right.path; | ||
path = right.path; | ||
if(options.valuesProp) { | ||
while(/(.*\/|^):([a-z0-9_\-\.\*\@\#]+)(\/.*|$)/.test(path)) { | ||
path = path.replace(/(.*\/|^):([a-z0-9_\-\.\*\@\#]+)(\/.*|$)/, | ||
function($, $1, $2, $3) { | ||
var values = getValues(rootValues, $2); | ||
if(values.length) { | ||
return $1 + (1 === values.length ? | ||
escRegExp(values[0]) : | ||
'(' + values.map(escRegExp).join('|') + ')') + $3; | ||
} | ||
return ''; | ||
}); | ||
} | ||
} | ||
return path && new RegExp('^'+path+'$').test(req.path); | ||
@@ -40,0 +46,0 @@ })) { |
@@ -8,10 +8,10 @@ var assert = require('assert'); | ||
it('when there is no rights', function(done) { | ||
testReq() | ||
.expect(500, 'Unauthorized access!') | ||
.end(function(err, res){ | ||
if(err) throw err; | ||
done(); | ||
}); | ||
}); | ||
it('when there is no rights', function(done) { | ||
testReq() | ||
.expect(500, 'Unauthorized access!') | ||
.end(function(err, res){ | ||
if(err) throw err; | ||
done(); | ||
}); | ||
}); | ||
it('when there is no rights matching the path', function(done) { | ||
@@ -21,3 +21,3 @@ testReq({ | ||
path: '/bar', | ||
methods: reaccess.METHODS | ||
methods: reaccess.ALL_MASK | ||
} | ||
@@ -36,6 +36,6 @@ }) | ||
path: '/bar', | ||
methods: reaccess.METHODS | ||
methods: reaccess.ALL_MASK | ||
},{ | ||
path: '/plop', | ||
methods: reaccess.METHODS | ||
methods: reaccess.ALL_MASK | ||
}] | ||
@@ -54,6 +54,6 @@ }) | ||
path: '/bar', | ||
methods: reaccess.METHODS | ||
methods: reaccess.ALL_MASK | ||
},{ | ||
path: '/plop', | ||
methods: reaccess.METHODS | ||
methods: reaccess.ALL_MASK | ||
}] | ||
@@ -72,6 +72,6 @@ }) | ||
path: '/foo/:bar.ba.pa.pa/plop', | ||
methods: reaccess.METHODS ^ reaccess.GET | ||
methods: reaccess.ALL_MASK ^ reaccess.GET | ||
},{ | ||
path: '/plop', | ||
methods: reaccess.METHODS | ||
methods: reaccess.ALL_MASK | ||
}] | ||
@@ -86,2 +86,38 @@ }) | ||
it('when there is a templated value with no value', function(done) { | ||
testReq({ | ||
rightsObj: [{ | ||
path: '/foo/:bar.foo', | ||
methods: reaccess.ALL_MASK | ||
}], | ||
valuesProp: 'user.content', | ||
userObj: {} | ||
}, { | ||
valuesProp: 'user.content' | ||
}, '/foo/') | ||
.expect(500, /Unauthorized access!/) | ||
.end(function(err, res){ | ||
if(err) throw err; | ||
done(); | ||
}); | ||
}); | ||
it('when there is a templated value with no value and several templates', function(done) { | ||
testReq({ | ||
rightsObj: [{ | ||
path: '/foo/:bar.foo/:bar.foo', | ||
methods: reaccess.ALL_MASK | ||
}], | ||
valuesProp: 'user.content', | ||
userObj: {} | ||
}, { | ||
valuesProp: 'user.content' | ||
}, '/foo//') | ||
.expect(500, /Unauthorized access!/) | ||
.end(function(err, res){ | ||
if(err) throw err; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
@@ -117,3 +153,3 @@ | ||
}], | ||
userProp: 'user.content', | ||
valuesProp: 'user.content', | ||
userObj: { | ||
@@ -130,3 +166,3 @@ bar: { | ||
}, { | ||
userProp: 'user.content' | ||
valuesProp: 'user.content' | ||
}, '/foo/1/plop') | ||
@@ -149,3 +185,3 @@ .expect(200, 'plop') | ||
}], | ||
userProp: 'user.content', | ||
valuesProp: 'user.content', | ||
userObj: { | ||
@@ -156,3 +192,3 @@ bar: 'a', | ||
}, { | ||
userProp: 'user.content' | ||
valuesProp: 'user.content' | ||
}, '/foo/a/plop') | ||
@@ -175,3 +211,3 @@ .expect(200, 'plop') | ||
}], | ||
userProp: 'user.content', | ||
valuesProp: 'user.content', | ||
userObj: { | ||
@@ -190,3 +226,3 @@ bar: { | ||
}, { | ||
userProp: 'user.content' | ||
valuesProp: 'user.content' | ||
}, '/foo/1/plop') | ||
@@ -209,3 +245,3 @@ .expect(200, 'plop') | ||
}], | ||
userProp: 'user.content', | ||
valuesProp: 'user.content', | ||
userObj: { | ||
@@ -224,3 +260,3 @@ bar: { | ||
}, { | ||
userProp: 'user.content' | ||
valuesProp: 'user.content' | ||
}, '/foo/1/plop') | ||
@@ -243,3 +279,3 @@ .expect(200, 'plop') | ||
}], | ||
userProp: 'user.content', | ||
valuesProp: 'user.content', | ||
userObj: { | ||
@@ -263,3 +299,3 @@ bar: { | ||
}, { | ||
userProp: 'user.content' | ||
valuesProp: 'user.content' | ||
}, '/foo/1/plop') | ||
@@ -273,2 +309,23 @@ .expect(200, 'plop') | ||
it('whith several templates for a single path', function(done) { | ||
testReq({ | ||
rightsObj: [{ | ||
path: '/foo/:bar/:foo', | ||
methods: reaccess.ALL_MASK | ||
}], | ||
valuesProp: 'user.content', | ||
userObj: { | ||
bar: 'bapapa', | ||
foo: 'lcontact' | ||
} | ||
}, { | ||
valuesProp: 'user.content' | ||
}, '/foo/bapapa/lcontact') | ||
.expect(200, 'plop') | ||
.end(function(err, res){ | ||
if(err) throw err; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
@@ -296,8 +353,8 @@ | ||
options.rightsObj = options.rightsObj || []; | ||
options.userProp = options.userProp || ''; | ||
options.valuesProp = options.valuesProp || ''; | ||
options.userObj = options.userObj || {}; | ||
return function (req, res, next) { | ||
setProp(req, options.rightsProp, options.rightsObj); | ||
if(options.userProp) { | ||
setProp(req, options.userProp, options.userObj); | ||
if(options.valuesProp) { | ||
setProp(req, options.valuesProp, options.userObj); | ||
} | ||
@@ -304,0 +361,0 @@ next(); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
18854
425
211