Skip to content

Commit

Permalink
syncrhonously add entity intercepts for ACLs
Browse files Browse the repository at this point in the history
otherwise the seneca stack order is messed up and permissions are run before everything else.
  • Loading branch information
nherment committed Oct 9, 2014
1 parent 28d5dcd commit d42884a
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 40 deletions.
52 changes: 20 additions & 32 deletions lib/ACLMicroservicesBuilder.js
Expand Up @@ -2,10 +2,16 @@
var _ = require('lodash')
var AccessControlProcedure = require('access-controls')

var debug = require('debug')('seneca-perm:ACLMicroservicesBuilder');
var DEBUG = false

var ACL_ERROR_CODE = 'perm/fail/acl'

function debug() {
if(DEBUG) {
console.log.apply(console, arguments)
}
}

function ACLMicroservicesBuilder(seneca) {
this._ACLProcedureResolver = undefined
this._seneca = seneca
Expand Down Expand Up @@ -35,8 +41,8 @@ function ACLMicroservicesBuilder(seneca) {

if(args.perm$) {

debug(JSON.stringify(args))
self._executeSavePermissions(args, callback)
this.ACLMicroservicesBuilder = self;
self._executeSavePermissions.call(this, args, callback)

} else {

Expand Down Expand Up @@ -171,7 +177,6 @@ ACLMicroservicesBuilder.prototype._executeReadPermissions = function(args, callb
ACLMicroservicesBuilder.prototype._executeListPermissions = function(args, callback) {
var self = this
var roles = extractRoles(args)
debug('ACLMicroservicesBuilder.prototype._executeListPermissions, roles: %s', roles);
var context = extractContext(args)
var entityDef = extractEntityDef(args)

Expand Down Expand Up @@ -222,15 +227,14 @@ ACLMicroservicesBuilder.prototype._executeSavePermissions = function(args, callb
var self = this

var roles = extractRoles(args)
debug('ACLMicroservicesBuilder.prototype._executeSavePermissions, roles: %s', roles);
var context = extractContext(args)
var entityDef = extractEntityDef(args)

delete args.perm$

if(args.ent.id) { // update

this._loadAndAuthorize(entityDef, args.ent.id, args.cmd, roles, context, function(err, dbEntity) {
self.ACLMicroservicesBuilder._loadAndAuthorize(entityDef, args.ent.id, args.cmd, roles, context, function(err, dbEntity) {

if(err) {

Expand All @@ -239,7 +243,7 @@ ACLMicroservicesBuilder.prototype._executeSavePermissions = function(args, callb
} else {

// also execute permission checks on the new attributes
self._deepAuthorize(entityDef, args.ent, args.cmd, 'save_new', roles, context, true, function(err, filteredEntity) {
self.ACLMicroservicesBuilder._deepAuthorize(entityDef, args.ent, args.cmd, 'save_new', roles, context, true, function(err, filteredEntity) {

if(err) {

Expand All @@ -251,12 +255,13 @@ ACLMicroservicesBuilder.prototype._executeSavePermissions = function(args, callb

delete args.perm$

self._seneca.act(args, function(err, entity) {
self.prior(args, function(err, entity) {
if(err) {
return callback(err, undefined)
}

// the entity returned by the save needs to be filtered again
self._filter(entityDef, entity, 'load', roles, context, function(err, entity) {
self.ACLMicroservicesBuilder._filter(entityDef, entity, 'load', roles, context, function(err, entity) {
callback(err, entity)
})

Expand All @@ -269,20 +274,20 @@ ACLMicroservicesBuilder.prototype._executeSavePermissions = function(args, callb

} else { // create

self._deepAuthorize(entityDef, args.ent, args.cmd, 'save_new', roles, context, true, function(err, filteredEntity) {
self.ACLMicroservicesBuilder._deepAuthorize(entityDef, args.ent, args.cmd, 'save_new', roles, context, true, function(err, filteredEntity) {

if(err) {
callback(err, undefined)
} else {

delete args.perm$

self._seneca.act(args, function(err, entity) {
self.prior(args, function(err, entity) {
if(err) {
return callback(err, undefined)
}
// the entity returned by the save needs to be filtered again
self._filter(entityDef, entity, 'load', roles, context, function(err, entity) {
self.ACLMicroservicesBuilder._filter(entityDef, entity, 'load', roles, context, function(err, entity) {
callback(err, entity)
})
})
Expand Down Expand Up @@ -317,7 +322,6 @@ ACLMicroservicesBuilder.prototype._loadAndAuthorize = function(entityDef, entity
}

ACLMicroservicesBuilder.prototype._filter = function(entityDef, entity, action, roles, context, callback) {
debug('ACLMicroservicesBuilder.prototype._filter, roles: %s', roles);
var aclProcedure = AccessControlProcedure.getProcedureForEntity(this._ACLProcedureResolver, entityDef, action)
if(aclProcedure) {
aclProcedure.authorize(entity, action, roles, context, function(err, authDecision) {
Expand All @@ -336,7 +340,6 @@ ACLMicroservicesBuilder.prototype._filter = function(entityDef, entity, action,
}

ACLMicroservicesBuilder.prototype._deepAuthorize = function(entityDef, entity, action, ruleAction, roles, context, applyFilters, callback) {
debug('ACLMicroservicesBuilder.prototype._deepAuthorize, roles: %s', roles);
var self = this

var aclProcedure = AccessControlProcedure.getProcedureForEntity(self._ACLProcedureResolver, entityDef, action)
Expand All @@ -357,21 +360,7 @@ ACLMicroservicesBuilder.prototype._deepAuthorize = function(entityDef, entity, a
callback(err, undefined)
} else {
//console.log(JSON.stringify(authDecision, null, 2))
if(authDecision) {
debug('_deepAuthorize decision ', JSON.stringify(authDecision));
if(authDecision.authorize) {
debug('authorized: %s', authDecision.authorize);
}
if(authDecision.history && _.isArray(authDecision.history)) {
debug('history follows:');
_.each(authDecision.history, function(historyItem){
debug(historyItem);
});
}
} else {
debug('authDecision is falsey in aclProcedure.authorize() callback');
}

debug('_deepAuthorize decision', JSON.stringify(authDecision))
var inheritDetails = inherit(authDecision)

if(applyFilters) {
Expand All @@ -396,7 +385,6 @@ ACLMicroservicesBuilder.prototype._deepAuthorize = function(entityDef, entity, a
callback(undefined, entity)

} else if(!authDecision.authorize && !authDecision.hard && action === 'list') {

entity = removeEntityFields(allowedFields, entity)
callback(undefined, entity)

Expand All @@ -411,7 +399,7 @@ ACLMicroservicesBuilder.prototype._deepAuthorize = function(entityDef, entity, a
}
})
} else {
console.log('no acls for', JSON.stringify(entityDef), action)
debug('no acls for', JSON.stringify(entityDef), action)
callback(undefined, entity)
}
}
Expand All @@ -432,7 +420,7 @@ function removeEntityFields(allowedFields, entity) {
for(var property in entity) {
if(entity.hasOwnProperty(property)) {
var propertyAllowed = allowedFields.indexOf(property);
if(propertyAllowed === -1) {
if(propertyAllowed === -1) {
delete entity[property];
}
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -27,7 +27,7 @@
"chai": "1.8.x"
},
"dependencies": {
"access-controls": "0.4.0",
"access-controls": "0.4.1",
"debug": "2.0.0",
"lodash": "2.4.1"
},
Expand Down
4 changes: 2 additions & 2 deletions perm.js
Expand Up @@ -202,6 +202,7 @@ module.exports = function(options) {
}
}

buildACLs()

globalSeneca.add({init:name}, function(args,done){

Expand All @@ -221,12 +222,11 @@ module.exports = function(options) {

options.entity = _.isBoolean(options.entity) ? (options.entity ? ['-/-/-'] : []) : (options.entity || [])

buildACLs()

_.each(options.entity,function( entspec ){
_.each(cmds,function(cmd){
entspec = _.isString(entspec) ? globalSeneca.util.parsecanon(entspec) : entspec
var spec = _.extend({role:'entity',cmd:cmd},entspec)

globalSeneca.add(spec,permcheck)
})
})
Expand Down
2 changes: 1 addition & 1 deletion test/perm.acl.filters.test.js
Expand Up @@ -17,7 +17,7 @@ describe('perm acl', function() {

var si = seneca()

si.use( '../perm.js', {
si.use( require('../perm.js'), {
accessControls: [{
name: 'access to region attribute',
roles: ['foobar', 'region'],
Expand Down
10 changes: 6 additions & 4 deletions test/perm.acl.test.js
Expand Up @@ -86,6 +86,7 @@ describe('perm acl', function() {
base: undefined,
name: 'item'
}],
hard: true,
control: 'required',
actions: ['list', 'load'],
conditions: [{
Expand Down Expand Up @@ -189,7 +190,7 @@ describe('perm acl', function() {
assert.equal(err.code, 'perm/fail/acl', 'expected error code to be ACL related')



done()
}) }) }) })

Expand All @@ -214,7 +215,7 @@ describe('perm acl', function() {
;pf1.save$(function(err,pf1){
assert.isNull(err)


done()
}) }) })

Expand Down Expand Up @@ -273,11 +274,12 @@ describe('perm acl', function() {
assert.isNull(err, err)
assert.isNotNull(pf3.id)

console.log('LIST PUBLIC NOW')
;pf1.list$(function(err, publicList) {
console.log('LIST PUBLIC BACK')
assert.isNull(err, err)
assert.isNotNull(publicList)
assert.equal(publicList.length, 1, 'permissions should filter out forbidden objects')

assert.equal(publicList.length, 1, 'permissions should filter out forbidden objects: ' + JSON.stringify(publicList))

;pf2.list$(function(err, privateList) {

Expand Down

0 comments on commit d42884a

Please sign in to comment.