Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 86725e9
Showing
8 changed files
with
360 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
Copyright (c) 2012 Moveline Inc. | ||
|
||
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# Rampart [![Build Status](https://secure.travis-ci.org/Moveline/rampart.png)](http://travis-ci.org/Moveline/rampart) | ||
|
||
Authorization module with Connect/Express support | ||
|
||
## Installation | ||
|
||
```bash | ||
$ npm install rampart | ||
``` | ||
|
||
## Usage | ||
|
||
```coffee-script | ||
Auth = require './auth' | ||
Rampart = require 'rampart' | ||
express = require 'express' | ||
|
||
class Ability extends Rampart.Ability | ||
constructor: (user) -> | ||
user = user || new User | ||
|
||
if user.role is 'admin' | ||
@can 'manage', User | ||
|
||
else | ||
@can 'manage', User, {_id: user.id} | ||
|
||
app = express() | ||
app.use Auth.session() | ||
app.use Rampart.express(Ability) | ||
|
||
app.get '/', (req, res, next) -> | ||
res.send 401 unless req.user.isAllowed 'read', User | ||
|
||
``` | ||
|
||
## Tests | ||
|
||
```bash | ||
$ npm test | ||
``` | ||
|
||
## Authors [Christopher Garvis][0] & [Moveline][1] | ||
|
||
[0]: http://christophergarvis.com | ||
[1]: http://www.moveline.com | ||
|
||
## License | ||
|
||
MIT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
_ = require('underscore'); | ||
|
||
var Ability = function Ability() { | ||
this.rules = []; | ||
}; | ||
|
||
Ability.prototype.can = function can(action, subject, conditions) { | ||
this.rules.push(new Rule(true, action, subject, conditions)); | ||
}; | ||
|
||
Ability.prototype.cannot = function cannot(action, subject, conditions) { | ||
this.rules.push(new Rule(false, action, subject, conditions)); | ||
}; | ||
|
||
/** | ||
* @param {String} | ||
* @param {Object} | ||
* @returns boolean | ||
*/ | ||
Ability.prototype.isAllowed = function isAllowed(action, subject) { | ||
var rules = this.relevantRules(action, subject); | ||
var match = _.detect(rules, function(rule) { | ||
return rule.matchesConditions(action, subject); | ||
}); | ||
|
||
return match ? match.base_behavior : false; | ||
}; | ||
|
||
/** | ||
* @see Ability::isAllowed | ||
*/ | ||
Ability.prototype.isNotAllowed = function isNotAllowed() { | ||
return !this.isAllowed.apply(this, arguments); | ||
}; | ||
|
||
Ability.prototype.relevantRules = function relevantRules(action, subject) { | ||
return this.rules.filter(function(rule) { | ||
return rule.isRelevant(action, subject); | ||
}, this); | ||
}; | ||
|
||
var Rule = function(base_behavior, action, subject, conditions) { | ||
var flatten = function flatten(items) { | ||
if('array' === typeof items) { | ||
return items.reduce(function(a,b) { return a.concact(b); }); | ||
} | ||
|
||
return [items]; | ||
}; | ||
|
||
this.base_behavior = base_behavior; | ||
this.actions = flatten(action); | ||
this.subjects = flatten(subject); | ||
this.conditions = conditions; | ||
}; | ||
|
||
/** | ||
* @param {String} | ||
* @param {Object} | ||
* @returns boolean | ||
*/ | ||
Rule.prototype.isRelevant = function isRelevant(action, subject) { | ||
return this.matchesAction(action) && this.matchesSubject(subject); | ||
}; | ||
|
||
/** | ||
* @param {String} | ||
* @returns boolean | ||
*/ | ||
Rule.prototype.matchesAction = function matchesAction(action) { | ||
return this.actions.indexOf('manage') !== -1 || | ||
this.actions.indexOf(action) !== -1; | ||
}; | ||
|
||
Rule.prototype.matchesConditions = function matchesConditions(action, subject) { | ||
if(_.isUndefined(this.conditions) || _.isNull(this.conditions)) { | ||
return true; | ||
} | ||
|
||
return _.reduce(this.conditions, function(memo, value, name) { | ||
return memo && (subject[name] === value); | ||
}, true); | ||
}; | ||
|
||
/** | ||
* @param {Object} | ||
* @returns boolean | ||
*/ | ||
Rule.prototype.matchesSubject = function matchesSubject(subject) { | ||
return _.contains(this.subjects, subject) || | ||
this.subjects.some(function(value) { return subject instanceof value; }); | ||
}; | ||
|
||
var middleware = function(Ability) { | ||
return function(req, res, next) { | ||
var abilities = new Ability(req.user); | ||
|
||
req.user.isAllowed = function() { return abilities.isAllowed.apply(abilities, arguments); }; | ||
req.user.isNotAllowed = function() { return abilities.isNotAllowed.apply(abilities, arguments); }; | ||
req.abilities = abilities; | ||
|
||
next(); | ||
}; | ||
}; | ||
|
||
module.exports = { | ||
Ability: Ability, | ||
connect: middleware, | ||
express: middleware | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
{ | ||
"name": "rampart", | ||
"version": "1.0.0", | ||
"description": "Authorization module for Node.js.", | ||
"main": "./lib/rampart.js", | ||
"dependencies":{ | ||
"underscore": "~1.4.0" | ||
}, | ||
"devDependencies":{ | ||
"chai": "~1.3.0", | ||
"coffee-script": "~1.3.3", | ||
"express": "~3.0.0rc5", | ||
"mocha": "~1.6.0", | ||
"supertest": "~0.3.1" | ||
}, | ||
"directories": { | ||
"test": "test" | ||
}, | ||
"scripts": { | ||
"test": "./node_modules/mocha/bin/mocha --compilers coffee:coffee-script" | ||
}, | ||
"keywords": [ "acl", "authorization", "access", "authorize", "connect", "express" ], | ||
"author": { | ||
"name": "Moveline Inc.", | ||
"email": "info@moveline.com", | ||
"url": "https://www.moveline.com" | ||
}, | ||
"contributors": [ | ||
{ | ||
"name": "Christopher Garvis", | ||
"email": "christopher.garvis@moveline.com", | ||
"url": "https://github.com/cgarvis" | ||
} | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/Moveline/rampart.git" | ||
}, | ||
"license": "MIT" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
app = require './server' | ||
chai = require 'chai' | ||
request = require 'supertest' | ||
|
||
chai.should() | ||
|
||
describe 'Rampart Middleware', -> | ||
describe 'logged in', -> | ||
it 'should return 200', (done) -> | ||
request(app).get('/1').expect 200, done | ||
|
||
it 'should return 401', (done) -> | ||
request(app).get('/2').expect 401, done |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
connect = require 'connect' | ||
chai = require 'chai' | ||
|
||
chai.should() | ||
|
||
Rampart = require '../lib/rampart' | ||
Ability = new Rampart.Ability() | ||
|
||
class User | ||
|
||
describe 'Rampart', -> | ||
afterEach -> | ||
Ability.rules = [] | ||
|
||
describe 'can', -> | ||
it 'should create a rule', -> | ||
Ability.can 'read', User | ||
Ability.rules.should.have.length 1 | ||
|
||
describe 'cannot', -> | ||
it 'should create a rule', -> | ||
Ability.cannot 'read', User | ||
Ability.rules.should.have.length 1 | ||
|
||
describe 'isAllowed', -> | ||
describe 'class level', -> | ||
beforeEach -> | ||
Ability.can 'read', User | ||
|
||
it 'should be true on `read`', -> | ||
Ability.isAllowed('read', User).should.be.ok | ||
|
||
it 'should be false on `write`', -> | ||
Ability.isAllowed('write', User).should.not.be.ok | ||
|
||
describe 'instance level', -> | ||
beforeEach -> | ||
Ability.can 'read', User | ||
|
||
it 'should be true on `read`', -> | ||
Ability.isAllowed('read', new User).should.be.ok | ||
|
||
it 'should be true on `write`', -> | ||
Ability.isAllowed('write', new User).should.not.be.ok | ||
|
||
describe 'manage action', -> | ||
beforeEach -> | ||
Ability.can 'manage', User | ||
|
||
it 'should be true on `read`', -> | ||
Ability.isAllowed('read', User).should.be.ok | ||
|
||
it 'should be true on `write`', -> | ||
Ability.isAllowed('write', User).should.be.ok | ||
|
||
it 'should be true on `create`', -> | ||
Ability.isAllowed('create', User).should.be.ok | ||
|
||
it 'should be true on `destroy`', -> | ||
Ability.isAllowed('destory', User).should.be.ok | ||
|
||
describe 'isNotAllowed', -> | ||
describe 'class level', -> | ||
beforeEach -> | ||
Ability.can 'read', User | ||
|
||
it 'should be false on `read`', -> | ||
Ability.isNotAllowed('read', User).should.not.be.ok | ||
|
||
it 'should be true on `write`', -> | ||
Ability.isNotAllowed('write', User).should.be.ok | ||
|
||
describe 'instance level', -> | ||
beforeEach -> | ||
Ability.can 'read', User | ||
|
||
it 'should be false on `read`', -> | ||
Ability.isNotAllowed('read', new User).should.not.be.ok | ||
|
||
it 'should be true on `write`', -> | ||
Ability.isNotAllowed('write', new User).should.be.ok | ||
|
||
describe 'manage action', -> | ||
beforeEach -> | ||
Ability.can 'manage', User | ||
|
||
it 'should be false on `read`', -> | ||
Ability.isNotAllowed('read', User).should.not.be.ok | ||
|
||
it 'should be false on `write`', -> | ||
Ability.isNotAllowed('write', User).should.not.be.ok | ||
|
||
it 'should be false on `create`', -> | ||
Ability.isNotAllowed('create', User).should.not.be.ok | ||
|
||
it 'should be false on `destroy`', -> | ||
Ability.isNotAllowed('destory', User).should.not.be.ok | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
Rampart = require '../' | ||
express = require 'express' | ||
|
||
class User | ||
constructor: (id) -> | ||
@id = parseInt id | ||
|
||
class Article | ||
constructor: (author) -> | ||
@author = parseInt author | ||
|
||
class Ability extends Rampart.Ability | ||
constructor: (user) -> | ||
super | ||
@can 'read', Article, {author: user.id} | ||
|
||
app = express() | ||
app.use express.bodyParser() | ||
app.use (req, res, next) -> | ||
req.user = new User(1) | ||
next() | ||
app.use Rampart.express(Ability) | ||
app.get '/:id', (req, res, next) -> | ||
article = new Article(req.params.id) | ||
unless req.user.isAllowed 'read', article | ||
return res.send 401 | ||
res.send article | ||
|
||
module.exports = app |