Add promise support to built-in model ACL

This commit is contained in:
ebarault 2017-01-30 14:56:57 +01:00 committed by Miroslav Bajtoš
parent c7e0a15a44
commit a63fad402e
3 changed files with 69 additions and 20 deletions

View File

@ -32,6 +32,7 @@
var g = require('../../lib/globalize');
var loopback = require('../../lib/loopback');
var utils = require('../../lib/utils');
var async = require('async');
var assert = require('assert');
var debug = require('debug')('loopback:security:acl');
@ -321,6 +322,7 @@ module.exports = function(ACL) {
ACL.checkPermission = function checkPermission(principalType, principalId,
model, property, accessType,
callback) {
if (!callback) callback = utils.createPromiseCallback();
if (principalId !== null && principalId !== undefined && (typeof principalId !== 'string')) {
principalId = principalId.toString();
}
@ -340,9 +342,9 @@ module.exports = function(ACL) {
debug('Permission denied by statically resolved permission');
debug(' Resolved Permission: %j', resolved);
process.nextTick(function() {
if (callback) callback(null, resolved);
callback(null, resolved);
});
return;
return callback.promise;
}
var self = this;
@ -350,8 +352,7 @@ module.exports = function(ACL) {
model: model, property: propertyQuery, accessType: accessTypeQuery}},
function(err, dynACLs) {
if (err) {
if (callback) callback(err);
return;
return callback(err);
}
acls = acls.concat(dynACLs);
resolved = self.resolvePermission(acls, req);
@ -359,8 +360,9 @@ module.exports = function(ACL) {
var modelClass = self.registry.findModel(model);
resolved.permission = (modelClass && modelClass.settings.defaultPermission) || ACL.ALLOW;
}
if (callback) callback(null, resolved);
return callback(null, resolved);
});
return callback.promise;
};
ACL.prototype.debug = function() {
@ -388,6 +390,7 @@ module.exports = function(ACL) {
*/
ACL.checkAccessForContext = function(context, callback) {
if (!callback) callback = utils.createPromiseCallback();
var self = this;
self.resolveRelatedModels();
var roleModel = self.roleModel;
@ -417,10 +420,7 @@ module.exports = function(ACL) {
this.find({where: {model: model.modelName, property: propertyQuery,
accessType: accessTypeQuery}}, function(err, acls) {
if (err) {
if (callback) callback(err);
return;
}
if (err) return callback(err);
var inRoleTasks = [];
acls = acls.concat(staticACLs);
@ -452,10 +452,7 @@ module.exports = function(ACL) {
});
async.parallel(inRoleTasks, function(err, results) {
if (err) {
if (callback) callback(err, null);
return;
}
if (err) return callback(err, null);
var resolved = self.resolvePermission(effectiveACLs, req);
if (resolved && resolved.permission === ACL.DEFAULT) {
@ -463,9 +460,10 @@ module.exports = function(ACL) {
}
debug('---Resolved---');
resolved.debug();
if (callback) callback(null, resolved);
return callback(null, resolved);
});
});
return callback.promise;
};
/**
@ -480,7 +478,7 @@ module.exports = function(ACL) {
*/
ACL.checkAccessForToken = function(token, model, modelId, method, callback) {
assert(token, 'Access token is required');
if (!callback) callback = utils.createPromiseCallback();
var context = new AccessContext({
accessToken: token,
model: model,
@ -490,12 +488,10 @@ module.exports = function(ACL) {
});
this.checkAccessForContext(context, function(err, access) {
if (err) {
if (callback) callback(err);
return;
}
if (callback) callback(null, access.permission !== ACL.DENY);
if (err) callback(err);
else callback(null, access.permission !== ACL.DENY);
});
return callback.promise;
};
ACL.resolveRelatedModels = function() {
@ -515,6 +511,7 @@ module.exports = function(ACL) {
* @param {Function} cb Callback function
*/
ACL.resolvePrincipal = function(type, id, cb) {
cb = cb || utils.createPromiseCallback();
type = type || ACL.ROLE;
this.resolveRelatedModels();
switch (type) {
@ -536,6 +533,7 @@ module.exports = function(ACL) {
cb(err);
});
}
return cb.promise;
};
/**
@ -546,6 +544,7 @@ module.exports = function(ACL) {
* @param {Function} cb Callback function
*/
ACL.isMappedToRole = function(principalType, principalId, role, cb) {
cb = cb || utils.createPromiseCallback();
var self = this;
this.resolvePrincipal(principalType, principalId,
function(err, principal) {
@ -568,5 +567,6 @@ module.exports = function(ACL) {
});
});
});
return cb.promise;
};
};

View File

@ -90,6 +90,41 @@ describe('security scopes', function() {
});
describe('security ACLs', function() {
it('supports checkPermission() returning a promise', function() {
return ACL.create({
principalType: ACL.USER,
principalId: 'u001',
model: 'testModel',
property: ACL.ALL,
accessType: ACL.ALL,
permission: ACL.ALLOW,
})
.then(function() {
return ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.ALL);
})
.then(function(access) {
assert(access.permission === ACL.ALLOW);
});
});
it('supports checkAccessForContext() returning a promise', function() {
var testModel = ds.createModel('testModel', {
acls: [
{principalType: ACL.USER, principalId: 'u001',
accessType: ACL.ALL, permission: ACL.ALLOW},
],
});
return ACL.checkAccessForContext({
principals: [{type: ACL.USER, id: 'u001'}],
model: 'testModel',
accessType: ACL.ALL,
})
.then(function(access) {
assert(access.permission === ACL.ALLOW);
});
});
it('should order ACL entries based on the matching score', function() {
var acls = [
{

View File

@ -539,6 +539,13 @@ describe('role model', function() {
});
});
it('supports ACL.resolvePrincipal() returning a promise', function() {
return ACL.resolvePrincipal(ACL.USER, user.id)
.then(function(u) {
expect(u.id).to.eql(user.id);
});
});
it('should resolve user by id', function(done) {
ACL.resolvePrincipal(ACL.USER, user.id, function(err, u) {
if (err) return done(err);
@ -589,6 +596,13 @@ describe('role model', function() {
});
});
it('supports ACL.isMappedToRole() returning a promise', function() {
return ACL.isMappedToRole(ACL.USER, user.username, 'admin')
.then(function(flag) {
expect(flag).to.be.true();
});
});
it('should report isMappedToRole by user.username', function(done) {
ACL.isMappedToRole(ACL.USER, user.username, 'admin', function(err, flag) {
if (err) return done(err);