Skip to content

Commit

Permalink
Merge pull request OptimalBits#182 from TechChange/get-many-optimization
Browse files Browse the repository at this point in the history
Get many optimization
  • Loading branch information
manast committed Mar 23, 2016
2 parents 7b18da7 + 34b5870 commit 9b625f2
Show file tree
Hide file tree
Showing 8 changed files with 298 additions and 70 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
node_modules/*
.DS_Store
*~
.idea/
.idea/
.tags*
83 changes: 78 additions & 5 deletions lib/acl.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ var Acl = function (backend, logger, options){
backend.getAsync = bluebird.promisify(backend.get);
backend.cleanAsync = bluebird.promisify(backend.clean);
backend.unionAsync = bluebird.promisify(backend.union);
if (backend.unions) {
backend.unionsAsync = bluebird.promisify(backend.unions);
}
};

/**
Expand Down Expand Up @@ -424,13 +427,17 @@ Acl.prototype.removePermissions = function(role, resources, permissions, cb){
*/
Acl.prototype.allowedPermissions = function(userId, resources, cb){
if (!userId)
return cb(null, []);
return cb(null, {});

contract(arguments)
.params('string|number', 'string|array', 'function')
.params('string|number', 'string|array')
.end();

if (this.backend.unionsAsync) {
return this.optimizedAllowedPermissions(userId, resources, cb);
}

var _this = this;
resources = makeArray(resources);

Expand All @@ -446,6 +453,55 @@ Acl.prototype.allowedPermissions = function(userId, resources, cb){
}).nodeify(cb);
};

/**
optimizedAllowedPermissions( userId, resources, function(err, obj) )
Returns all the allowable permissions a given user have to
access the given resources.
It returns a map of resource name to a list of permissions for that resource.
This is the same as allowedPermissions, it just takes advantage of the unions
function if available to reduce the number of backend queries.
@param {String|Number} User id.
@param {String|Array} resource(s) to ask permissions for.
@param {Function} Callback called when finished.
*/
Acl.prototype.optimizedAllowedPermissions = function(userId, resources, cb){
if (!userId) {
return cb(null, {});
}

contract(arguments)
.params('string|number', 'string|array', 'function|undefined')
.params('string|number', 'string|array')
.end();

resources = makeArray(resources);
var self = this;

return this._allUserRoles(userId).then(function(roles) {
var buckets = resources.map(allowsBucket);
if (roles.length === 0) {
var emptyResult = {};
buckets.forEach(function(bucket) {
emptyResult[bucket] = [];
});
return bluebird.resolve(emptyResult);
}

return self.backend.unionsAsync(buckets, roles);
}).then(function(response) {
var result = {};
Object.keys(response).forEach(function(bucket) {
result[keyFromAllowsBucket(bucket)] = response[bucket];
});

return result;
}).nodeify(cb);
};

/**
isAllowed( userId, resource, permissions, function(err, allowed) )
Expand Down Expand Up @@ -644,7 +700,7 @@ Acl.prototype.middleware = function(numPathComponents, userId, actions){
}else if(allowed === false){
if (acl.logger) {
acl.logger.debug('Not allowed '+_actions+' on '+resource+' by user '+_userId);
acl.allowedPermissions(_userId, resource, function(err, obj){
acl.allowedPermissions(_userId, resource, function(err, obj){
acl.logger.debug('Allowed permissions: '+util.inspect(obj));
});
}
Expand Down Expand Up @@ -748,7 +804,7 @@ Acl.prototype._allRoles = function(roleNames, cb){
// Return all roles in the hierarchy including the given roles.
//
Acl.prototype._allRoles = function(roleNames){
var _this = this, roles;
var _this = this;

return this._rolesParents(roleNames).then(function(parents){
if(parents.length > 0){
Expand All @@ -761,6 +817,21 @@ Acl.prototype._allRoles = function(roleNames){
});
};

//
// Return all roles in the hierarchy of the given user.
//
Acl.prototype._allUserRoles = function(userId) {
var _this = this;

return this.userRoles(userId).then(function(roles) {
if (roles && roles.length > 0) {
return _this._allRoles(roles);
} else {
return [];
}
});
};

//
// Returns an array with resources for the given roles.
//
Expand Down Expand Up @@ -848,10 +919,12 @@ function allowsBucket(role){
return 'allows_'+role;
}

function keyFromAllowsBucket(str) {
return str.replace(/^allows_/, '');
}


// -----------------------------------------------------------------------------------


exports = module.exports = Acl;


82 changes: 46 additions & 36 deletions lib/backend.js
Original file line number Diff line number Diff line change
@@ -1,78 +1,88 @@
/**
Backend Interface.
Implement this API for providing a backend for the acl module.
Backend Interface.
Implement this API for providing a backend for the acl module.
*/

var contract = require('./contract');

var Backend = {
/**
/**
Begins a transaction.
*/
begin : function(){
// returns a transaction object
},

/**
Ends a transaction (and executes it)
*/
end : function(transaction, cb){
contract(arguments).params('object', 'function').end();
contract(arguments).params('object', 'function').end();
// Execute transaction
},

/**
Cleans the whole storage.
*/
clean : function(cb){
contract(arguments).params('function').end();
},

/**
Gets the contents at the bucket's key.
*/
get : function(bucket, key, cb){
contract(arguments)
.params('string', 'string|number', 'function')
.end();
contract(arguments)
.params('string', 'string|number', 'function')
.end();
},

/**
Returns the union of the values in the given keys.
*/
/**
Gets the union of contents of the specified keys in each of the specified buckets and returns
a mapping of bucket to union.
*/
unions : function(bucket, keys, cb){
contract(arguments)
.params('array', 'array', 'function')
.end();
},

/**
Returns the union of the values in the given keys.
*/
union : function(bucket, keys, cb){
contract(arguments)
.params('string', 'array', 'function')
.end();
},
.params('string', 'array', 'function')
.end();
},

/**
Adds values to a given key inside a bucket.
*/
add : function(transaction, bucket, key, values){
contract(arguments)
.params('object', 'string', 'string|number','string|array|number')
Adds values to a given key inside a bucket.
*/
add : function(transaction, bucket, key, values){
contract(arguments)
.params('object', 'string', 'string|number','string|array|number')
.end();
},
},

/**
Delete the given key(s) at the bucket
*/
del : function(transaction, bucket, keys){
contract(arguments)
.params('object', 'string', 'string|array')
.end();
contract(arguments)
.params('object', 'string', 'string|array')
.end();
},
/**
Removes values from a given key inside a bucket.
*/
remove : function(transaction, bucket, key, values){
contract(arguments)
.params('object', 'string', 'string|number','string|array|number')

/**
Removes values from a given key inside a bucket.
*/
remove : function(transaction, bucket, key, values){
contract(arguments)
.params('object', 'string', 'string|number','string|array|number')
.end();
},
},
}

exports = module.exports = Backend;
22 changes: 22 additions & 0 deletions lib/memory-backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,28 @@ MemoryBackend.prototype = {
}
},

/**
Gets the union of the keys in each of the specified buckets
*/
unions : function(buckets, keys, cb){
contract(arguments)
.params('array', 'array', 'function')
.end();

var self = this;
var results = {};

buckets.forEach(function(bucket) {
if(self._buckets[bucket]){
results[bucket] = _.uniq(_.flatten(_.values(_.pick(self._buckets[bucket], keys))));
}else{
results[bucket] = [];
}
});

cb(null, results);
},

/**
Returns the union of the values in the given keys.
*/
Expand Down
Loading

0 comments on commit 9b625f2

Please sign in to comment.