diff --git a/Gruntfile.js b/Gruntfile.js index f220ec5..888661a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -11,6 +11,7 @@ module.exports = function(grunt) { var testSrc = 'test/**/*.js'; var lintOptions = { + esversion: 6, curly: true, eqeqeq: true, indent: 2, diff --git a/README.md b/README.md index 073c71e..4979210 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,7 @@ Register a getter for a role. If the role is a string of the form `entity.relation`, a getter for the entity must be registered with the [`entity`](#manager.entity) method. Roles without `.` are "simple" roles (e.g. `"admin"`) and no entity is looked up. Throws [`ConfigError`](#configerror) if -called with an invalid role name. +called with an invalid or duplicate role name. #### `entity(type, getter)` @@ -183,7 +183,7 @@ called with an invalid role name. generated while getting the entity and the second is the target entity. Register a getter for an entity. Throws [`ConfigError`](#configerror) if called -with invalid arguments. +with invalid arguments or duplicate entity name. #### `action(name, roles)` @@ -194,7 +194,7 @@ with invalid arguments. Specify the roles that a user must have to perform the named action. Throws [`ConfigError`](#configerror) if the provided roles have not yet been registered -with the [`role`](#manager.role) method. +with the [`role`](#manager.role) method or if the action name is a duplicate. #### `can(action)` diff --git a/lib/manager.js b/lib/manager.js index 363b4f8..a162bef 100644 --- a/lib/manager.js +++ b/lib/manager.js @@ -73,6 +73,11 @@ Manager.prototype.action = function(name, roles) { } return role; }); + + if (name in this.actionDefs_) { + throw new errors.ConfigError(`action already registered: ${name}`); + } + this.actionDefs_[name] = roles; }; @@ -181,6 +186,11 @@ Manager.prototype.entity = function(type, getter) { throw new errors.ConfigError( 'Entity getter must be a function that takes two arguments'); } + + if (type in this.entityGetters_) { + throw new errors.ConfigError(`entity already registered: ${type}`); + } + this.entityGetters_[type] = getter; }; @@ -294,6 +304,11 @@ Manager.prototype.role = function(role, getter) { throw new errors.ConfigError( 'Getters for simple roles (without entities) take two arguments'); } + + if (role.name in this.roleGetters_) { + throw new errors.ConfigError(`role already registered: ${role.name}`); + } + this.roleGetters_[role.name] = getter; }; diff --git a/package.json b/package.json index 4b00bb3..3a3e4fc 100644 --- a/package.json +++ b/package.json @@ -29,15 +29,15 @@ "url": "https://github.com/tschaub/authorized/issues" }, "devDependencies": { - "chai": "1.5.0", - "mocha": "1.9.0", - "grunt": "0.4.1", - "grunt-contrib-jshint": "0.4.3", - "grunt-cafe-mocha": "0.1.2", - "grunt-contrib-watch": "0.3.1", - "grunt-cli": "0.1.7", - "express": "3.2.0", - "chai-http": "0.3.0" + "chai": "^1.5.0", + "chai-http": "^4.3.0", + "express": "^4.17.1", + "grunt": "^1.3.0", + "grunt-cafe-mocha": "^0.1.2", + "grunt-cli": "^1.4.1", + "grunt-contrib-jshint": "^3.0.0", + "grunt-contrib-watch": "^1.1.0", + "mocha": "^8.3.2" }, "main": "./lib/authorized", "scripts": { @@ -45,7 +45,10 @@ "watch": "grunt watch" }, "dependencies": { - "async": "0.9.0", + "async": "^0.9.0", "pause": "0.0.1" + }, + "engines": { + "node": ">=10" } } diff --git a/test/integration/express/app.spec.js b/test/integration/express/app.spec.js index 77c3c92..b3639fc 100644 --- a/test/integration/express/app.spec.js +++ b/test/integration/express/app.spec.js @@ -56,7 +56,8 @@ describe('Usage in Express app', function() { express.json(), function(req, res, next) { var view = auth.view(req); - res.send(202, { + res.status(202) + .send({ roles: view.roles, entities: view.entities, actions: view.actions @@ -65,7 +66,8 @@ describe('Usage in Express app', function() { app.use(function(err, req, res, next) { if (err instanceof UnauthorizedError) { - res.send(401, 'Unauthorized'); + res.status(401) + .send('Unauthorized'); } else { next(err); } @@ -77,10 +79,8 @@ describe('Usage in Express app', function() { chai.request(app) .post('/organizations/org1/members') - .req(function(req) { - req.set('x-fake-user-id', 'user.1'); - }) - .res(function(res) { + .set('x-fake-user-id', 'user.1') + .end(function(err, res) { assert.strictEqual(res.status, 202); var body = res.body; assert.isFalse(body.roles.admin, 'not admin'); @@ -95,16 +95,10 @@ describe('Usage in Express app', function() { chai.request(app) .post('/organizations/org1/members') - .req(function(req) { - req.set('x-fake-user-id', 'user.2'); - }) - .res(function(res) { - assert.strictEqual(res.status, 401); - }); - + .set('x-fake-user-id', 'user.2') + .end(function(err, res) { + assert.strictEqual(res.status, 401); + }); }); - }); - - }); diff --git a/test/lib/manager.spec.js b/test/lib/manager.spec.js index 698ab4f..a147a0b 100644 --- a/test/lib/manager.spec.js +++ b/test/lib/manager.spec.js @@ -107,16 +107,25 @@ describe('Manager', function() { // pretend nobody is admin done(null, false); }); - auth.role('page.author', function(page, req, done) { - // pretend everybody is author - done(null, true); - }); assert.throws(function() { auth.action('can edit page', ['admin', 'page.author']); }); }); + it('throws when attempting to define action twice', function() { + auth.role('admin', function(req, done) { + done(null, false); + }); + + auth.action('can edit page', ['admin']); + + assert.throws(function() { + auth.action('can edit page', ['admin']); + }); + }); + + }); describe('#can()', function() { @@ -318,6 +327,16 @@ describe('Manager', function() { }, ConfigError); }); + it('throws when attempting to define entity twice', function() { + assert.doesNotThrow(function() { + auth.entity('page', (req, done) => done(null, {})); + }); + assert.throws(() => { + auth.entity('page', (req, done) => done(null, {})); + }); + }); + + }); describe('#role()', function() { @@ -405,7 +424,15 @@ describe('Manager', function() { }, ConfigError); }); - }); - + it('throws when attempting to define a role twice', function() { + auth.role('admin', function(req, done) { + // pretend nobody is admin + done(null, false); + }); + assert.throws(() => { + auth.role('admin', (req, done) => done(null, false)); + }); + }); + }); });