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 debug = require('debug')('role');
var assert = require('assert');
var async = require('async');
// Role model
var RoleSchema = {
@ -167,7 +169,7 @@ Role.registerResolver(Role.OWNER, function(role, context, callback) {
var modelClass = context.model;
var id = context.id;
var userId = context.userId || context.principalId;
isOwner(modelClass, id, userId, callback);
Role.isOwner(modelClass, id, userId, callback);
});
function isUserClass(modelClass) {
@ -175,8 +177,16 @@ function isUserClass(modelClass) {
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
if(!userId) {
process.nextTick(function() {
@ -188,12 +198,12 @@ function isOwner(modelClass, id, userId, callback) {
// Is the modelClass User or a subclass of User?
if(isUserClass(modelClass)) {
process.nextTick(function() {
callback(null, id === userId);
callback(null, modelId === userId);
});
return;
}
modelClass.findById(id, function(err, inst) {
modelClass.findById(modelId, function(err, inst) {
if(err || !inst) {
callback && callback(err, false);
return;
@ -222,7 +232,7 @@ function isOwner(modelClass, id, userId, callback) {
callback && callback(null, false);
}
});
}
};
Role.registerResolver(Role.AUTHENTICATED, function(role, context, callback) {
if(!context) {
@ -231,15 +241,19 @@ Role.registerResolver(Role.AUTHENTICATED, function(role, context, callback) {
});
return;
}
var userId = context.principalId;
isAuthenticated(userId, callback);
Role.isAuthenticated(context, 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() {
callback && callback(null, !!userId);
callback && callback(null, !!context.principalId);
});
}
};
Role.registerResolver(Role.UNAUTHENTICATED, function(role, context, callback) {
process.nextTick(function() {
@ -304,25 +318,60 @@ Role.isInRole = function (role, context, callback) {
/**
* List roles for a given principal
* @param {String} principalType
* @param {String|Number} principalId
* @param {Object} context The security context
* @param {Function} callback
*
* @callback callback
* @param err
* @param {String[]} An array of role ids
*/
Role.getRoles = function (principalType, principalId, callback) {
RoleMapping.find({where: {principalType: principalType, principalId: principalId}}, function (err, mappings) {
if (err) {
callback && callback(err);
return;
}
var roles = [];
mappings.forEach(function (m) {
roles.push(m.roleId);
Role.getRoles = function (context, callback) {
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);
}
});
});
callback && callback(null, roles);
});
// 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) {
done && done(err);
return;
}
mappings.forEach(function (m) {
if (roles.indexOf(m.roleId) === -1) {
roles.push(m.roleId);
}
});
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);
});
Role.getRoles(RoleMapping.USER, user.id, function (err, roles) {
assert.equal(roles.length, 1);
assert.equal(roles[0], role.id);
Role.getRoles({principalType: RoleMapping.USER, principalId: user.id}, function (err, roles) {
assert.equal(roles.length, 3); // everyone, authenticated, userRole
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) {
assert.equal(roles.length, 0);
Role.getRoles({principalType: RoleMapping.APP, principalId: user.id}, function (err, roles) {
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) {
assert.equal(roles.length, 0);
Role.getRoles({principalType: RoleMapping.USER, principalId: 100}, function (err, roles) {
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);
});
});
});
});