rest-access
Advanced tools
Comparing version 1.2.1 to 1.3.0
73
index.js
@@ -20,42 +20,46 @@ const wildcard = require('wildcard') | ||
const path = req.originalUrl.split(queryHashSplitPattern)[0] | ||
const userPermission = req[permissionProperty] | ||
const permission = req[permissionProperty] | ||
// res.locals userCan property for things like templates. | ||
req.userCan = (permission) => !hasPermission(userPermission, permission) | ||
req.userCan = (requiredPermission) => !hasPermission(permission, requiredPermission) | ||
// method: wildcard(req, def) | ||
// path: wildcard(def, req) | ||
// permission: wildcard(def, req) | ||
// if access is allowed call `next()`, otherwise call `next(new NotPermittedError())` | ||
const reason = isBlocked(method, path, permission) | ||
next(reason ? new NotPermittedError(reason) : undefined) | ||
} | ||
} | ||
// procedure description: | ||
// iterate over rules, check if: | ||
// 1. the method/path matches and if so, | ||
// 2. check if the permission matches: yes: next(), no: call next(new NotPermittedError('access not permitted')) | ||
if (!rules || rules.length === 0) return next() | ||
function isBlocked (method, path, permission) { | ||
path = path.split(queryHashSplitPattern)[0] | ||
for (let i = 0; i < rules.length; i++) { | ||
let rule = rules[i] | ||
// procedure description: | ||
// iterate over rules, check if: | ||
// 1. the method/path matches and if so, | ||
// 2. check if the permission matches: return `false (boolean)`: when not blocked, return `reason (string)` when the access is not allowed | ||
if (!rules || rules.length === 0) return false | ||
// check rule arguments: | ||
if (!rule.length || rule.length < 3 || rule.length > 4) { | ||
return next(new TypeError('wrong access rule definition. must have 3 or 4 arguments.')) | ||
} | ||
for (let i = 0; i < rules.length; i++) { | ||
let rule = rules[i] | ||
// check method (1.argument) | ||
if (typeof rule[0] === 'string') rule[0] = rule[0].split(termSplitPattern) | ||
if (!rule[0].includes(method) && !rule[0].includes('*')) continue // with next rule | ||
// check rule arguments: | ||
if (!rule.length || rule.length < 3 || rule.length > 4) { | ||
return 'wrong access rule definition. must have 3 or 4 arguments' | ||
} | ||
// path (2.argument) | ||
if (!wildcard(rule[1], path, pathSegmentSplitPattern).length) continue // with next rule | ||
// check method (1.argument) | ||
if (typeof rule[0] === 'string') rule[0] = rule[0].split(termSplitPattern) | ||
if (!rule[0].includes(method) && !rule[0].includes('*')) continue // with next rule | ||
// permission (3.argument) | ||
// so method and path matched, now the permission has to match to, otherwise access is denied -> new NotPermittedError('access not permitted') | ||
let matches = hasPermission(userPermission, rule[2], rule[3]) | ||
if (rule[3] && !matches) continue | ||
return next(matches) | ||
} | ||
// path (2.argument) | ||
if (!wildcard(rule[1], path, pathSegmentSplitPattern).length) continue // with next rule | ||
// no matching access definition | ||
return next(new NotPermittedError('no matching access rule found')) | ||
// permission (3.argument) | ||
// so method and path matched, now the permission has to match to, otherwise access is denied -> new NotPermittedError('access not permitted') | ||
let matches = hasPermission(permission, rule[2], rule[3]) | ||
if (rule[3] && !matches) continue | ||
return matches | ||
} | ||
// no matching access definition | ||
return 'no matching access rule found' | ||
} | ||
@@ -72,8 +76,8 @@ | ||
// so method and path matched, now the permission has to match to, otherwise access is denied -> new NotPermittedError('access not permitted') | ||
if (routePermission === '*') return undefined // with next rule | ||
if (!userPermission) return new NotPermittedError('not authenticated') | ||
if (routePermission === '*') return false // with next rule | ||
if (!userPermission) return 'not authenticated' | ||
if (typeof userPermission === 'string') userPermission = userPermission.split(termSplitPattern) | ||
if (typeof routePermission === 'string') routePermission = routePermission.split(termSplitPattern) | ||
if (!Array.isArray(routePermission)) { | ||
return new TypeError('wrong permission format: ' + routePermission) | ||
return 'wrong permission format: ' + routePermission | ||
} | ||
@@ -85,4 +89,4 @@ const matches = routePermission.some(p => { | ||
}) | ||
if ((!block && !matches) || (block && matches)) return new NotPermittedError('access not permitted') | ||
return undefined | ||
if ((!block && !matches) || (block && matches)) return 'access not permitted' | ||
return false | ||
} | ||
@@ -112,2 +116,3 @@ | ||
} | ||
restAccessRules.isBlocked = isBlocked | ||
restAccessRules.middleware = middleware | ||
@@ -114,0 +119,0 @@ restAccessRules.restrict = restrict |
{ | ||
"name": "rest-access", | ||
"version": "1.2.1", | ||
"version": "1.3.0", | ||
"description": "role/scope based REST access control", | ||
@@ -15,3 +15,4 @@ "main": "index.js", | ||
"test:client": "node examples/client.js", | ||
"test": "run-p -r \"test:server\" \"test:client\"" | ||
"test:integration": "run-p -r \"test:server\" \"test:client\"", | ||
"test": "node tests/test-access.js" | ||
}, | ||
@@ -18,0 +19,0 @@ "author": "Andi Neck", |
@@ -9,2 +9,4 @@ # rest-access | ||
> `rest-access` can be used to restrict access to resources. it can be used as a standalone solution or as a express/connect middleware. | ||
## usage | ||
@@ -90,3 +92,24 @@ | ||
**app.midleware** | ||
**access.isBlocked** | ||
this function can be used to check if the access to the required endpoint is blocked. | ||
`isBlocked(method, path, permission)` | ||
Given this definition: | ||
```js | ||
access([ | ||
['GET', '/api/*', 'api:*'], | ||
['GET,POST,PUT,DELETE', '/api/*', 'api:write'], | ||
]) | ||
``` | ||
The following result is expected: | ||
```js | ||
access.isBlocked('GET', '/api/hello', 'api:read') // -> returns `false` | ||
access.isBlocked('POST', '/api/message', 'api:write') // -> returns `false` | ||
access.isBlocked('PUT', '/api/message/today', 'api:read') // -> returns `'access not permitted'` | ||
``` | ||
**access.midleware** | ||
middleware function | ||
@@ -99,3 +122,3 @@ | ||
**app.restrict** | ||
**access.restrict** | ||
restrict single route | ||
@@ -108,2 +131,10 @@ | ||
**access.getRules** | ||
use this function to return a copy of the existing rules. | ||
example: | ||
```js | ||
restAccess.getRules().forEach(rule => console.log(rule.join(' '))); | ||
``` | ||
#### extends | ||
@@ -116,2 +147,4 @@ | ||
**run unittests** | ||
```sh | ||
@@ -121,2 +154,7 @@ npm test | ||
**run integrationtests** | ||
```sh | ||
npm test:integration | ||
``` | ||
## license | ||
@@ -123,0 +161,0 @@ |
18005
7
350
160