Enhance getRoles() to support smart roles

This commit is contained in:
Raymond Feng 2013-12-11 09:06:21 -08:00
parent 82eeaeee6b
commit 178674ec9a
2 changed files with 90 additions and 31 deletions

View File

@ -1,5 +1,7 @@
var loopback = require('../loopback'); var loopback = require('../loopback');
var debug = require('debug')('role'); var debug = require('debug')('role');
var assert = require('assert');
var async = require('async');
// Role model // Role model
var RoleSchema = { var RoleSchema = {
@ -167,7 +169,7 @@ Role.registerResolver(Role.OWNER, function(role, context, callback) {
var modelClass = context.model; var modelClass = context.model;
var id = context.id; var id = context.id;
var userId = context.userId || context.principalId; var userId = context.userId || context.principalId;
isOwner(modelClass, id, userId, callback); Role.isOwner(modelClass, id, userId, callback);
}); });
function isUserClass(modelClass) { function isUserClass(modelClass) {
@ -175,8 +177,16 @@ function isUserClass(modelClass) {
modelClass.prototype instanceof loopback.User; modelClass.prototype instanceof loopback.User;
} }
function isOwner(modelClass, id, userId, callback) { /**
debug('isOwner(): %s %s %s', modelClass && modelClass.modelName, id, userId); * Check if a given userId is the owner the model instance
* @param {Function} modelClass The model class
* @param {*} modelId The model id
* @param {*) userId The user id
* @param {Function} callback
*/
Role.isOwner = function isOwner(modelClass, modelId, userId, callback) {
assert(modelClass, 'Model class is required');
debug('isOwner(): %s %s %s', modelClass && modelClass.modelName, modelId, userId);
// No userId is present // No userId is present
if(!userId) { if(!userId) {
process.nextTick(function() { process.nextTick(function() {
@ -188,12 +198,12 @@ function isOwner(modelClass, id, userId, callback) {
// Is the modelClass User or a subclass of User? // Is the modelClass User or a subclass of User?
if(isUserClass(modelClass)) { if(isUserClass(modelClass)) {
process.nextTick(function() { process.nextTick(function() {
callback(null, id === userId); callback(null, modelId === userId);
}); });
return; return;
} }
modelClass.findById(id, function(err, inst) { modelClass.findById(modelId, function(err, inst) {
if(err || !inst) { if(err || !inst) {
callback && callback(err, false); callback && callback(err, false);
return; return;
@ -222,7 +232,7 @@ function isOwner(modelClass, id, userId, callback) {
callback && callback(null, false); callback && callback(null, false);
} }
}); });
} };
Role.registerResolver(Role.AUTHENTICATED, function(role, context, callback) { Role.registerResolver(Role.AUTHENTICATED, function(role, context, callback) {
if(!context) { if(!context) {
@ -231,15 +241,19 @@ Role.registerResolver(Role.AUTHENTICATED, function(role, context, callback) {
}); });
return; return;
} }
var userId = context.principalId; Role.isAuthenticated(context, callback);
isAuthenticated(userId, callback);
}); });
function isAuthenticated(userId, callback) { /**
* Check if the user id is authenticated
* @param {Object} context The security context
* @param {Function} callback The callback function
*/
Role.isAuthenticated = function isAuthenticated(context, callback) {
process.nextTick(function() { process.nextTick(function() {
callback && callback(null, !!userId); callback && callback(null, !!context.principalId);
}); });
} };
Role.registerResolver(Role.UNAUTHENTICATED, function(role, context, callback) { Role.registerResolver(Role.UNAUTHENTICATED, function(role, context, callback) {
process.nextTick(function() { process.nextTick(function() {
@ -304,25 +318,60 @@ Role.isInRole = function (role, context, callback) {
/** /**
* List roles for a given principal * List roles for a given principal
* @param {String} principalType * @param {Object} context The security context
* @param {String|Number} principalId
* @param {Function} callback * @param {Function} callback
* *
* @callback callback * @callback callback
* @param err * @param err
* @param {String[]} An array of role ids * @param {String[]} An array of role ids
*/ */
Role.getRoles = function (principalType, principalId, callback) { Role.getRoles = function (context, callback) {
RoleMapping.find({where: {principalType: principalType, principalId: principalId}}, function (err, mappings) { debug('getRoles(): %j', context);
var roles = [];
// Check against the smart roles
var inRoleTasks = [];
Object.keys(Role.resolvers).forEach(function (role) {
inRoleTasks.push(function (done) {
Role.isInRole(role, context, function (err, inRole) {
if (!err && inRole) {
if (roles.indexOf(role) === -1) {
roles.push(role);
}
done();
} else {
done(err, null);
}
});
});
});
// Check against the role mappings
var principalType = context.principalType || undefined;
var principalId = context.principalId || undefined;
if (principalType && principalId) {
// Please find() treat undefined matches all values
inRoleTasks.push(function (done) {
RoleMapping.find({where: {principalType: principalType,
principalId: principalId}}, function (err, mappings) {
if (err) { if (err) {
callback && callback(err); done && done(err);
return; return;
} }
var roles = [];
mappings.forEach(function (m) { mappings.forEach(function (m) {
if (roles.indexOf(m.roleId) === -1) {
roles.push(m.roleId); roles.push(m.roleId);
}
}); });
callback && callback(null, roles); done && done();
});
});
}
async.parallel(inRoleTasks, function (err, results) {
debug('getRoles() return: %j %j', err, results);
callback && callback(err, roles);
}); });
}; };

View File

@ -85,17 +85,27 @@ describe('role model', function () {
assert(!err && exists === false); assert(!err && exists === false);
}); });
Role.getRoles(RoleMapping.USER, user.id, function (err, roles) { Role.getRoles({principalType: RoleMapping.USER, principalId: user.id}, function (err, roles) {
assert.equal(roles.length, 1); assert.equal(roles.length, 3); // everyone, authenticated, userRole
assert.equal(roles[0], role.id); assert(roles.indexOf(role.id) >=0);
assert(roles.indexOf(Role.EVERYONE) >=0);
assert(roles.indexOf(Role.AUTHENTICATED) >=0);
}); });
Role.getRoles(RoleMapping.APP, user.id, function (err, roles) { Role.getRoles({principalType: RoleMapping.APP, principalId: user.id}, function (err, roles) {
assert.equal(roles.length, 0); assert.equal(roles.length, 2);
assert(roles.indexOf(Role.EVERYONE) >=0);
assert(roles.indexOf(Role.AUTHENTICATED) >=0);
}); });
Role.getRoles(RoleMapping.USER, 100, function (err, roles) { Role.getRoles({principalType: RoleMapping.USER, principalId: 100}, function (err, roles) {
assert.equal(roles.length, 0); assert.equal(roles.length, 2);
assert(roles.indexOf(Role.EVERYONE) >=0);
assert(roles.indexOf(Role.AUTHENTICATED) >=0);
});
Role.getRoles({principalType: RoleMapping.USER, principalId: null}, function (err, roles) {
assert.equal(roles.length, 2);
assert(roles.indexOf(Role.EVERYONE) >=0);
assert(roles.indexOf(Role.UNAUTHENTICATED) >=0);
}); });
}); });
}); });
}); });