functional-acl
Advanced tools
Comparing version 0.3.1 to 0.4.0
{ | ||
"name": "functional-acl", | ||
"version": "0.3.1", | ||
"version": "0.4.0", | ||
"description": "A functional implementation of access control lists (ACLs)", | ||
"main": "index.js", | ||
"main": "lib", | ||
"files": [ | ||
"lib" | ||
], | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1", | ||
"build": "babel --out-dir . src/", | ||
"watch": "npm run build -- --watch", | ||
"prepublish": "npm run build", | ||
"build": "babel --out-dir lib/ src/", | ||
"example:express": "babel-node examples/express.js", | ||
"example:simple": "babel-node examples/index.js", | ||
"example:express": "babel-node examples/express.js" | ||
"lint": "eslint src/", | ||
"prepublish": "npm run validate && npm run build", | ||
"semantic-release": "semantic-release pre && npm publish && semantic-release post", | ||
"test": "mocha --require babel-register src/**/*.test.js", | ||
"tdd": "npm run test -- -w", | ||
"validate": "npm run lint && npm run test", | ||
"watch": "npm run build -- --watch" | ||
}, | ||
@@ -19,6 +26,19 @@ "author": "Paul 'opatut' Bienkowski <npm@opatut.de>", | ||
"babel-cli": "^6.1.4", | ||
"babel-eslint": "^6.0.4", | ||
"babel-preset-es2015": "^6.1.4", | ||
"babel-preset-stage-0": "^6.1.2", | ||
"express": "^4.13.3" | ||
"babel-register": "^6.9.0", | ||
"eslint": "^2.11.1", | ||
"eslint-config-wish-technology": "^2.3.0", | ||
"eslint-plugin-babel": "^3.2.0", | ||
"eslint-plugin-flow-vars": "^0.4.0", | ||
"eslint-plugin-import": "^1.8.1", | ||
"eslint-plugin-jsx-a11y": "^1.2.2", | ||
"eslint-plugin-mocha": "^2.2.0", | ||
"eslint-plugin-react": "^5.1.1", | ||
"express": "^4.13.3", | ||
"mocha": "^2.5.3", | ||
"power-assert": "^1.4.1", | ||
"semantic-release": "^4.3.5" | ||
} | ||
} |
@@ -7,5 +7,11 @@ # Functional Access Control Lists (node-functional-acl) | ||
> An ACL specifies which users or system processes are granted access to objects, as well as what operations are allowed on given objects. Each entry in a typical ACL specifies a subject and an operation. | ||
> An ACL specifies which users or system processes are granted access to | ||
> objects, as well as what operations are allowed on given objects. Each entry | ||
> in a typical ACL specifies a subject and an operation. | ||
Functional ACL are an implementation of this concept with functional programming. It makes heavy use of [higher-order functions](https://en.wikipedia.org/wiki/Higher-order_function) as [predicates](https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)) to model user permissions. | ||
Functional ACL are an implementation of this concept with functional | ||
programming. It makes heavy use of [higher-order | ||
functions](https://en.wikipedia.org/wiki/Higher-order_function) as | ||
[predicates](https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)) to | ||
model user permissions. | ||
@@ -29,3 +35,4 @@ #### What it is not | ||
An object that describes the situation you are checking permissions for. This usually contains one or more of: | ||
An object that describes the situation you are checking permissions for. This | ||
usually contains one or more of: | ||
@@ -39,3 +46,4 @@ * the acting user (*user* / *subject*) | ||
A function that checks whether the context (or part of it) matches a certain condition. Returns true or false. Examples: | ||
A function that checks whether the context (or part of it) matches a certain | ||
condition. Returns true or false. Examples: | ||
@@ -48,5 +56,12 @@ * `admins = (context) => context.user.isAdmin` | ||
A function that returns one of `true` (allow), `false` (deny) or `null` (proceed with next rule) for a context. These rules can be easily built from predicates using the `allow(...predicates)` and `deny(...predicates)` functions. They require all predicates to match on the context for the rule to match, and then return the appropriate boolean value, or `null` if any of the predicates does not match. | ||
A function that returns one of `true` (allow), `false` (deny) or `null` | ||
(proceed with next rule) for a context. These rules can be easily built from | ||
predicates using the `allow(...predicates)` and `deny(...predicates)` | ||
functions. They require all predicates to match on the context for the rule to | ||
match, and then return the appropriate boolean value, or `null` if any of the | ||
predicates does not match. | ||
Multiple rules can be combined using the `build(...rules)` function. These are then tested in order, the first rule that matches decides the result (`true` or `false`). If no rule matches, the combined rule returns `null`. | ||
Multiple rules can be combined using the `combineRules(...rules)` function. | ||
These are then tested in order, the first rule that matches decides the result | ||
(`true` or `false`). If no rule matches, the combined rule returns `null`. | ||
@@ -56,34 +71,58 @@ ## API | ||
#### `not(predicate) => predicate` | ||
Create a predicate that matches if the original predicate did not match. | ||
#### `invert(rule) => rule` | ||
Create a rule that allows if the original rule would deny, and vice versa. Does | ||
not decide if the original rule wouldn't. | ||
#### `always() => true` | ||
Predicate that always matches. | ||
#### `never() => false` | ||
Predicate that never matches. | ||
#### `all(...predicates) => predicate` | ||
Create a predicate that matches if all predicates match. | ||
#### `any(...predicates) => predicate` | ||
Create a predicate that matches if at least one of the predicates matches. | ||
#### `none(...predicates) => predicate` | ||
Create a predicate that matches if none of the predicates matches | ||
#### `allow(predicate) => rule` | ||
Create a rule that returns true if the predicate matches. | ||
#### `deny(predicate) => rule` | ||
Create a rule that returns false if the predicate matches. | ||
#### `build(...rules) => rule` | ||
Create a rule that returns the result of the first matching rule, or null if none matches | ||
#### `combineRules(...rules) => rule` | ||
Create a rule that returns the result of the first matching rule, or null if | ||
none matches | ||
#### `forceDecisionIf(predicate, rule, undecidedResult = false) => rule` | ||
Create a rule that is triggered only if the predicate matches, and if the rule | ||
does not decide, return the `undecidedResult`. | ||
#### `enforce(rule, context) => undefined` | ||
Throws an `ACLRejectionError` if the `rule` rejects the `context`, or does not decide on the `context` (i.e. returns `null`). | ||
Instead of a `try/catch` switch, you can manually check a rule simply by calling it with a context (it is a function after all, hence this package's name). This would look something like: | ||
Throws an `ACLRejectionError` if the `rule` rejects the `context`, or does not | ||
decide on the `context` (i.e. returns `null`). | ||
const myRule = build(...); | ||
Instead of a `try/catch` switch, you can manually check a rule simply by | ||
calling it with a context (it is a function after all, hence this package's | ||
name). This would look something like: | ||
const myRule = combineRules(...); | ||
const myContext = { user: ..., operation: ..., foo: ... }; | ||
@@ -100,3 +139,3 @@ | ||
import {build, deny, allow} from '../src'; | ||
import {combineRules, deny, allow} from '../src'; | ||
@@ -108,3 +147,3 @@ const admins = ({user}) => user && user.isAdmin; | ||
const restricted = build( | ||
const restricted = combineRules( | ||
deny(guests), // guests may not ever read | ||
@@ -116,10 +155,12 @@ allow(admins), // admins may do everything | ||
// now you can check permissions simply by calling | ||
const allowed = restricted({ user: someUser, operation: someOperation }); | ||
const context = {user: myUser, operation: 'read'}; | ||
const allowed = restricted(context); | ||
// or enforce a permission (throw ACLRejectedError otherwise) | ||
enforce(restricted(...)); | ||
enforce(restricted, context); | ||
## Express middleware | ||
There is a helper in `functional-acl/express` that creates a customized middleware factory. Sounds complicated? It's not! Here we go: | ||
There is a helper in `functional-acl/express` that creates a customized | ||
middleware factory. Sounds complicated? It's not! Here we go: | ||
@@ -145,11 +186,19 @@ import aclExpress from 'functional-acl/express'; | ||
Used to derive a partial context from the request. This can for example be used to supply the current user in the context. Make sure to plug in an authorization middleware (such as *passport*) somewhere, so you can access the user (e.g. `req.user`) here. | ||
Used to derive a partial context from the request. This can for example be used | ||
to supply the current user in the context. Make sure to plug in an | ||
authorization middleware (such as *passport*) somewhere, so you can access the | ||
user (e.g. `req.user`) here. | ||
#### `createDirectContext: (...args) => context` | ||
Used to extract a partial context from the arguments passed into the resulting middleware after the rule. In the example above, there is only one expected argument after the rule, which is the `operation`. This is then merged into the context at the key `operation`. You can see the value `'read'` being passed in for the example route. | ||
Used to extract a partial context from the arguments passed into the resulting | ||
middleware after the rule. In the example above, there is only one expected | ||
argument after the rule, which is the `operation`. This is then merged into the | ||
context at the key `operation`. You can see the value `'read'` being passed in | ||
for the example route. | ||
#### `onDeny: (rule, context, req, res, next) => undefined` | ||
Used by the default *applyRule* as an error handler. Defaults to throwing an `ACLRejectionError`. | ||
Used by the default *applyRule* as an error handler. Defaults to throwing an | ||
`ACLRejectionError`. | ||
@@ -160,2 +209,4 @@ #### `applyRule: (rule, directContext, extractedContext, req, res, next) => undefined` | ||
By default, it builds a context from the direct and extracted partial contexts (direct context overwrites extracted context) and applies the rule on it. If that fails, `onDeny` is called. | ||
By default, it builds a context from the direct and extracted partial contexts | ||
(direct context overwrites extracted context) and applies the rule on it. If | ||
that fails, `onDeny` is called. |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
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
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
27026
5
432
2
203
18
1