Skip to content

Commit 9b625f2

Browse files
committed
Merge pull request #182 from TechChange/get-many-optimization
Get many optimization
2 parents 7b18da7 + 34b5870 commit 9b625f2

File tree

8 files changed

+298
-70
lines changed

8 files changed

+298
-70
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
node_modules/*
22
.DS_Store
33
*~
4-
.idea/
4+
.idea/
5+
.tags*

lib/acl.js

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ var Acl = function (backend, logger, options){
6565
backend.getAsync = bluebird.promisify(backend.get);
6666
backend.cleanAsync = bluebird.promisify(backend.clean);
6767
backend.unionAsync = bluebird.promisify(backend.union);
68+
if (backend.unions) {
69+
backend.unionsAsync = bluebird.promisify(backend.unions);
70+
}
6871
};
6972

7073
/**
@@ -424,13 +427,17 @@ Acl.prototype.removePermissions = function(role, resources, permissions, cb){
424427
*/
425428
Acl.prototype.allowedPermissions = function(userId, resources, cb){
426429
if (!userId)
427-
return cb(null, []);
430+
return cb(null, {});
428431

429432
contract(arguments)
430433
.params('string|number', 'string|array', 'function')
431434
.params('string|number', 'string|array')
432435
.end();
433436

437+
if (this.backend.unionsAsync) {
438+
return this.optimizedAllowedPermissions(userId, resources, cb);
439+
}
440+
434441
var _this = this;
435442
resources = makeArray(resources);
436443

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

456+
/**
457+
optimizedAllowedPermissions( userId, resources, function(err, obj) )
458+
459+
Returns all the allowable permissions a given user have to
460+
access the given resources.
461+
462+
It returns a map of resource name to a list of permissions for that resource.
463+
464+
This is the same as allowedPermissions, it just takes advantage of the unions
465+
function if available to reduce the number of backend queries.
466+
467+
@param {String|Number} User id.
468+
@param {String|Array} resource(s) to ask permissions for.
469+
@param {Function} Callback called when finished.
470+
*/
471+
Acl.prototype.optimizedAllowedPermissions = function(userId, resources, cb){
472+
if (!userId) {
473+
return cb(null, {});
474+
}
475+
476+
contract(arguments)
477+
.params('string|number', 'string|array', 'function|undefined')
478+
.params('string|number', 'string|array')
479+
.end();
480+
481+
resources = makeArray(resources);
482+
var self = this;
483+
484+
return this._allUserRoles(userId).then(function(roles) {
485+
var buckets = resources.map(allowsBucket);
486+
if (roles.length === 0) {
487+
var emptyResult = {};
488+
buckets.forEach(function(bucket) {
489+
emptyResult[bucket] = [];
490+
});
491+
return bluebird.resolve(emptyResult);
492+
}
493+
494+
return self.backend.unionsAsync(buckets, roles);
495+
}).then(function(response) {
496+
var result = {};
497+
Object.keys(response).forEach(function(bucket) {
498+
result[keyFromAllowsBucket(bucket)] = response[bucket];
499+
});
500+
501+
return result;
502+
}).nodeify(cb);
503+
};
504+
449505
/**
450506
isAllowed( userId, resource, permissions, function(err, allowed) )
451507
@@ -644,7 +700,7 @@ Acl.prototype.middleware = function(numPathComponents, userId, actions){
644700
}else if(allowed === false){
645701
if (acl.logger) {
646702
acl.logger.debug('Not allowed '+_actions+' on '+resource+' by user '+_userId);
647-
acl.allowedPermissions(_userId, resource, function(err, obj){
703+
acl.allowedPermissions(_userId, resource, function(err, obj){
648704
acl.logger.debug('Allowed permissions: '+util.inspect(obj));
649705
});
650706
}
@@ -748,7 +804,7 @@ Acl.prototype._allRoles = function(roleNames, cb){
748804
// Return all roles in the hierarchy including the given roles.
749805
//
750806
Acl.prototype._allRoles = function(roleNames){
751-
var _this = this, roles;
807+
var _this = this;
752808

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

820+
//
821+
// Return all roles in the hierarchy of the given user.
822+
//
823+
Acl.prototype._allUserRoles = function(userId) {
824+
var _this = this;
825+
826+
return this.userRoles(userId).then(function(roles) {
827+
if (roles && roles.length > 0) {
828+
return _this._allRoles(roles);
829+
} else {
830+
return [];
831+
}
832+
});
833+
};
834+
764835
//
765836
// Returns an array with resources for the given roles.
766837
//
@@ -848,10 +919,12 @@ function allowsBucket(role){
848919
return 'allows_'+role;
849920
}
850921

922+
function keyFromAllowsBucket(str) {
923+
return str.replace(/^allows_/, '');
924+
}
925+
851926

852927
// -----------------------------------------------------------------------------------
853928

854929

855930
exports = module.exports = Acl;
856-
857-

lib/backend.js

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,88 @@
11
/**
2-
Backend Interface.
3-
4-
Implement this API for providing a backend for the acl module.
2+
Backend Interface.
3+
4+
Implement this API for providing a backend for the acl module.
55
*/
66

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

99
var Backend = {
10-
/**
10+
/**
1111
Begins a transaction.
1212
*/
1313
begin : function(){
1414
// returns a transaction object
1515
},
16-
16+
1717
/**
1818
Ends a transaction (and executes it)
1919
*/
2020
end : function(transaction, cb){
21-
contract(arguments).params('object', 'function').end();
21+
contract(arguments).params('object', 'function').end();
2222
// Execute transaction
2323
},
24-
24+
2525
/**
2626
Cleans the whole storage.
2727
*/
2828
clean : function(cb){
2929
contract(arguments).params('function').end();
3030
},
31-
31+
3232
/**
3333
Gets the contents at the bucket's key.
3434
*/
3535
get : function(bucket, key, cb){
36-
contract(arguments)
37-
.params('string', 'string|number', 'function')
38-
.end();
36+
contract(arguments)
37+
.params('string', 'string|number', 'function')
38+
.end();
3939
},
4040

41-
/**
42-
Returns the union of the values in the given keys.
43-
*/
41+
/**
42+
Gets the union of contents of the specified keys in each of the specified buckets and returns
43+
a mapping of bucket to union.
44+
*/
45+
unions : function(bucket, keys, cb){
46+
contract(arguments)
47+
.params('array', 'array', 'function')
48+
.end();
49+
},
50+
51+
/**
52+
Returns the union of the values in the given keys.
53+
*/
4454
union : function(bucket, keys, cb){
4555
contract(arguments)
46-
.params('string', 'array', 'function')
47-
.end();
48-
},
49-
56+
.params('string', 'array', 'function')
57+
.end();
58+
},
59+
5060
/**
51-
Adds values to a given key inside a bucket.
52-
*/
53-
add : function(transaction, bucket, key, values){
54-
contract(arguments)
55-
.params('object', 'string', 'string|number','string|array|number')
61+
Adds values to a given key inside a bucket.
62+
*/
63+
add : function(transaction, bucket, key, values){
64+
contract(arguments)
65+
.params('object', 'string', 'string|number','string|array|number')
5666
.end();
57-
},
58-
67+
},
68+
5969
/**
6070
Delete the given key(s) at the bucket
6171
*/
6272
del : function(transaction, bucket, keys){
63-
contract(arguments)
64-
.params('object', 'string', 'string|array')
65-
.end();
73+
contract(arguments)
74+
.params('object', 'string', 'string|array')
75+
.end();
6676
},
67-
68-
/**
69-
Removes values from a given key inside a bucket.
70-
*/
71-
remove : function(transaction, bucket, key, values){
72-
contract(arguments)
73-
.params('object', 'string', 'string|number','string|array|number')
77+
78+
/**
79+
Removes values from a given key inside a bucket.
80+
*/
81+
remove : function(transaction, bucket, key, values){
82+
contract(arguments)
83+
.params('object', 'string', 'string|number','string|array|number')
7484
.end();
75-
},
85+
},
7686
}
7787

7888
exports = module.exports = Backend;

lib/memory-backend.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,28 @@ MemoryBackend.prototype = {
5959
}
6060
},
6161

62+
/**
63+
Gets the union of the keys in each of the specified buckets
64+
*/
65+
unions : function(buckets, keys, cb){
66+
contract(arguments)
67+
.params('array', 'array', 'function')
68+
.end();
69+
70+
var self = this;
71+
var results = {};
72+
73+
buckets.forEach(function(bucket) {
74+
if(self._buckets[bucket]){
75+
results[bucket] = _.uniq(_.flatten(_.values(_.pick(self._buckets[bucket], keys))));
76+
}else{
77+
results[bucket] = [];
78+
}
79+
});
80+
81+
cb(null, results);
82+
},
83+
6284
/**
6385
Returns the union of the values in the given keys.
6486
*/

0 commit comments

Comments
 (0)