Merge pull request #3163 from ebarault/feature/promisify-acl
Add promise support to built-in model ACL
This commit is contained in:
commit
c099d8c731
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
var g = require('../../lib/globalize');
|
var g = require('../../lib/globalize');
|
||||||
var loopback = require('../../lib/loopback');
|
var loopback = require('../../lib/loopback');
|
||||||
|
var utils = require('../../lib/utils');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var debug = require('debug')('loopback:security:acl');
|
var debug = require('debug')('loopback:security:acl');
|
||||||
|
@ -321,6 +322,7 @@ module.exports = function(ACL) {
|
||||||
ACL.checkPermission = function checkPermission(principalType, principalId,
|
ACL.checkPermission = function checkPermission(principalType, principalId,
|
||||||
model, property, accessType,
|
model, property, accessType,
|
||||||
callback) {
|
callback) {
|
||||||
|
if (!callback) callback = utils.createPromiseCallback();
|
||||||
if (principalId !== null && principalId !== undefined && (typeof principalId !== 'string')) {
|
if (principalId !== null && principalId !== undefined && (typeof principalId !== 'string')) {
|
||||||
principalId = principalId.toString();
|
principalId = principalId.toString();
|
||||||
}
|
}
|
||||||
|
@ -340,9 +342,9 @@ module.exports = function(ACL) {
|
||||||
debug('Permission denied by statically resolved permission');
|
debug('Permission denied by statically resolved permission');
|
||||||
debug(' Resolved Permission: %j', resolved);
|
debug(' Resolved Permission: %j', resolved);
|
||||||
process.nextTick(function() {
|
process.nextTick(function() {
|
||||||
if (callback) callback(null, resolved);
|
callback(null, resolved);
|
||||||
});
|
});
|
||||||
return;
|
return callback.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -350,8 +352,7 @@ module.exports = function(ACL) {
|
||||||
model: model, property: propertyQuery, accessType: accessTypeQuery}},
|
model: model, property: propertyQuery, accessType: accessTypeQuery}},
|
||||||
function(err, dynACLs) {
|
function(err, dynACLs) {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (callback) callback(err);
|
return callback(err);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
acls = acls.concat(dynACLs);
|
acls = acls.concat(dynACLs);
|
||||||
resolved = self.resolvePermission(acls, req);
|
resolved = self.resolvePermission(acls, req);
|
||||||
|
@ -359,8 +360,9 @@ module.exports = function(ACL) {
|
||||||
var modelClass = self.registry.findModel(model);
|
var modelClass = self.registry.findModel(model);
|
||||||
resolved.permission = (modelClass && modelClass.settings.defaultPermission) || ACL.ALLOW;
|
resolved.permission = (modelClass && modelClass.settings.defaultPermission) || ACL.ALLOW;
|
||||||
}
|
}
|
||||||
if (callback) callback(null, resolved);
|
return callback(null, resolved);
|
||||||
});
|
});
|
||||||
|
return callback.promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
ACL.prototype.debug = function() {
|
ACL.prototype.debug = function() {
|
||||||
|
@ -388,6 +390,7 @@ module.exports = function(ACL) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ACL.checkAccessForContext = function(context, callback) {
|
ACL.checkAccessForContext = function(context, callback) {
|
||||||
|
if (!callback) callback = utils.createPromiseCallback();
|
||||||
var self = this;
|
var self = this;
|
||||||
self.resolveRelatedModels();
|
self.resolveRelatedModels();
|
||||||
var roleModel = self.roleModel;
|
var roleModel = self.roleModel;
|
||||||
|
@ -417,10 +420,7 @@ module.exports = function(ACL) {
|
||||||
|
|
||||||
this.find({where: {model: model.modelName, property: propertyQuery,
|
this.find({where: {model: model.modelName, property: propertyQuery,
|
||||||
accessType: accessTypeQuery}}, function(err, acls) {
|
accessType: accessTypeQuery}}, function(err, acls) {
|
||||||
if (err) {
|
if (err) return callback(err);
|
||||||
if (callback) callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var inRoleTasks = [];
|
var inRoleTasks = [];
|
||||||
|
|
||||||
acls = acls.concat(staticACLs);
|
acls = acls.concat(staticACLs);
|
||||||
|
@ -452,10 +452,7 @@ module.exports = function(ACL) {
|
||||||
});
|
});
|
||||||
|
|
||||||
async.parallel(inRoleTasks, function(err, results) {
|
async.parallel(inRoleTasks, function(err, results) {
|
||||||
if (err) {
|
if (err) return callback(err, null);
|
||||||
if (callback) callback(err, null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var resolved = self.resolvePermission(effectiveACLs, req);
|
var resolved = self.resolvePermission(effectiveACLs, req);
|
||||||
if (resolved && resolved.permission === ACL.DEFAULT) {
|
if (resolved && resolved.permission === ACL.DEFAULT) {
|
||||||
|
@ -463,9 +460,10 @@ module.exports = function(ACL) {
|
||||||
}
|
}
|
||||||
debug('---Resolved---');
|
debug('---Resolved---');
|
||||||
resolved.debug();
|
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) {
|
ACL.checkAccessForToken = function(token, model, modelId, method, callback) {
|
||||||
assert(token, 'Access token is required');
|
assert(token, 'Access token is required');
|
||||||
|
if (!callback) callback = utils.createPromiseCallback();
|
||||||
var context = new AccessContext({
|
var context = new AccessContext({
|
||||||
accessToken: token,
|
accessToken: token,
|
||||||
model: model,
|
model: model,
|
||||||
|
@ -490,12 +488,10 @@ module.exports = function(ACL) {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.checkAccessForContext(context, function(err, access) {
|
this.checkAccessForContext(context, function(err, access) {
|
||||||
if (err) {
|
if (err) callback(err);
|
||||||
if (callback) callback(err);
|
else callback(null, access.permission !== ACL.DENY);
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (callback) callback(null, access.permission !== ACL.DENY);
|
|
||||||
});
|
});
|
||||||
|
return callback.promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
ACL.resolveRelatedModels = function() {
|
ACL.resolveRelatedModels = function() {
|
||||||
|
@ -515,6 +511,7 @@ module.exports = function(ACL) {
|
||||||
* @param {Function} cb Callback function
|
* @param {Function} cb Callback function
|
||||||
*/
|
*/
|
||||||
ACL.resolvePrincipal = function(type, id, cb) {
|
ACL.resolvePrincipal = function(type, id, cb) {
|
||||||
|
cb = cb || utils.createPromiseCallback();
|
||||||
type = type || ACL.ROLE;
|
type = type || ACL.ROLE;
|
||||||
this.resolveRelatedModels();
|
this.resolveRelatedModels();
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -536,6 +533,7 @@ module.exports = function(ACL) {
|
||||||
cb(err);
|
cb(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return cb.promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -546,6 +544,7 @@ module.exports = function(ACL) {
|
||||||
* @param {Function} cb Callback function
|
* @param {Function} cb Callback function
|
||||||
*/
|
*/
|
||||||
ACL.isMappedToRole = function(principalType, principalId, role, cb) {
|
ACL.isMappedToRole = function(principalType, principalId, role, cb) {
|
||||||
|
cb = cb || utils.createPromiseCallback();
|
||||||
var self = this;
|
var self = this;
|
||||||
this.resolvePrincipal(principalType, principalId,
|
this.resolvePrincipal(principalType, principalId,
|
||||||
function(err, principal) {
|
function(err, principal) {
|
||||||
|
@ -568,5 +567,6 @@ module.exports = function(ACL) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
return cb.promise;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -90,6 +90,41 @@ describe('security scopes', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('security ACLs', 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() {
|
it('should order ACL entries based on the matching score', function() {
|
||||||
var acls = [
|
var acls = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -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) {
|
it('should resolve user by id', function(done) {
|
||||||
ACL.resolvePrincipal(ACL.USER, user.id, function(err, u) {
|
ACL.resolvePrincipal(ACL.USER, user.id, function(err, u) {
|
||||||
if (err) return done(err);
|
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) {
|
it('should report isMappedToRole by user.username', function(done) {
|
||||||
ACL.isMappedToRole(ACL.USER, user.username, 'admin', function(err, flag) {
|
ACL.isMappedToRole(ACL.USER, user.username, 'admin', function(err, flag) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
|
|
Loading…
Reference in New Issue