models: move Role LDL def into a json file
This commit is contained in:
parent
e9c86163aa
commit
461ae92c1c
|
@ -41,8 +41,8 @@ var AccessContext = ctx.AccessContext;
|
||||||
var Principal = ctx.Principal;
|
var Principal = ctx.Principal;
|
||||||
var AccessRequest = ctx.AccessRequest;
|
var AccessRequest = ctx.AccessRequest;
|
||||||
|
|
||||||
var role = require('./role');
|
var Role = loopback.Role;
|
||||||
var Role = role.Role;
|
assert(Role, 'Role model must be defined before ACL model');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Model for access control meta data.
|
* A Model for access control meta data.
|
||||||
|
|
|
@ -5,399 +5,385 @@ var async = require('async');
|
||||||
|
|
||||||
var AccessContext = require('../../lib/access-context').AccessContext;
|
var AccessContext = require('../../lib/access-context').AccessContext;
|
||||||
|
|
||||||
// Role model
|
|
||||||
var RoleSchema = {
|
|
||||||
id: {type: String, id: true, generated: true}, // Id
|
|
||||||
name: {type: String, required: true}, // The name of a role
|
|
||||||
description: String, // Description
|
|
||||||
|
|
||||||
// Timestamps
|
|
||||||
created: {type: Date, default: Date},
|
|
||||||
modified: {type: Date, default: Date}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Role Model
|
|
||||||
* @class
|
|
||||||
*/
|
|
||||||
var Role = loopback.createModel('Role', RoleSchema, {
|
|
||||||
relations: {
|
|
||||||
principals: {
|
|
||||||
type: 'hasMany',
|
|
||||||
model: 'RoleMapping',
|
|
||||||
foreignKey: 'roleId'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var RoleMapping = loopback.RoleMapping;
|
var RoleMapping = loopback.RoleMapping;
|
||||||
assert(RoleMapping, 'RoleMapping model must be defined before Role model');
|
assert(RoleMapping, 'RoleMapping model must be defined before Role model');
|
||||||
|
|
||||||
// Set up the connection to users/applications/roles once the model
|
|
||||||
Role.once('dataSourceAttached', function () {
|
|
||||||
var roleMappingModel = this.RoleMapping || loopback.getModelByType(RoleMapping);
|
|
||||||
Role.prototype.users = function (callback) {
|
|
||||||
roleMappingModel.find({where: {roleId: this.id,
|
|
||||||
principalType: RoleMapping.USER}}, function (err, mappings) {
|
|
||||||
if (err) {
|
|
||||||
callback && callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return mappings.map(function (m) {
|
|
||||||
return m.principalId;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Role.prototype.applications = function (callback) {
|
|
||||||
roleMappingModel.find({where: {roleId: this.id,
|
|
||||||
principalType: RoleMapping.APPLICATION}}, function (err, mappings) {
|
|
||||||
if (err) {
|
|
||||||
callback && callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return mappings.map(function (m) {
|
|
||||||
return m.principalId;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Role.prototype.roles = function (callback) {
|
|
||||||
roleMappingModel.find({where: {roleId: this.id,
|
|
||||||
principalType: RoleMapping.ROLE}}, function (err, mappings) {
|
|
||||||
if (err) {
|
|
||||||
callback && callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return mappings.map(function (m) {
|
|
||||||
return m.principalId;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
// Special roles
|
|
||||||
Role.OWNER = '$owner'; // owner of the object
|
|
||||||
Role.RELATED = "$related"; // any User with a relationship to the object
|
|
||||||
Role.AUTHENTICATED = "$authenticated"; // authenticated user
|
|
||||||
Role.UNAUTHENTICATED = "$unauthenticated"; // authenticated user
|
|
||||||
Role.EVERYONE = "$everyone"; // everyone
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add custom handler for roles
|
* The Role Model
|
||||||
* @param role
|
* @class Role
|
||||||
* @param resolver The resolver function decides if a principal is in the role
|
|
||||||
* dynamically
|
|
||||||
*
|
|
||||||
* function(role, context, callback)
|
|
||||||
*/
|
*/
|
||||||
Role.registerResolver = function(role, resolver) {
|
module.exports = function(Role) {
|
||||||
if(!Role.resolvers) {
|
|
||||||
Role.resolvers = {};
|
|
||||||
}
|
|
||||||
Role.resolvers[role] = resolver;
|
|
||||||
};
|
|
||||||
|
|
||||||
Role.registerResolver(Role.OWNER, function(role, context, callback) {
|
// Workaround for https://github.com/strongloop/loopback/issues/292
|
||||||
if(!context || !context.model || !context.modelId) {
|
Role.definition.rawProperties.created.default =
|
||||||
process.nextTick(function() {
|
Role.definition.properties.created.default = function() {
|
||||||
callback && callback(null, false);
|
return new Date();
|
||||||
});
|
};
|
||||||
return;
|
|
||||||
}
|
|
||||||
var modelClass = context.model;
|
|
||||||
var modelId = context.modelId;
|
|
||||||
var userId = context.getUserId();
|
|
||||||
Role.isOwner(modelClass, modelId, userId, callback);
|
|
||||||
});
|
|
||||||
|
|
||||||
function isUserClass(modelClass) {
|
// Workaround for https://github.com/strongloop/loopback/issues/292
|
||||||
return modelClass === loopback.User ||
|
Role.definition.rawProperties.modified.default =
|
||||||
modelClass.prototype instanceof loopback.User;
|
Role.definition.properties.modified.default = function() {
|
||||||
}
|
return new Date();
|
||||||
|
};
|
||||||
|
|
||||||
/*!
|
// Set up the connection to users/applications/roles once the model
|
||||||
* Check if two user ids matches
|
Role.once('dataSourceAttached', function() {
|
||||||
* @param {*} id1
|
var roleMappingModel = this.RoleMapping || loopback.getModelByType(RoleMapping);
|
||||||
* @param {*} id2
|
Role.prototype.users = function(callback) {
|
||||||
* @returns {boolean}
|
roleMappingModel.find({where: {roleId: this.id,
|
||||||
*/
|
principalType: RoleMapping.USER}}, function(err, mappings) {
|
||||||
function matches(id1, id2) {
|
if (err) {
|
||||||
if (id1 === undefined || id1 === null || id1 ===''
|
callback && callback(err);
|
||||||
|| id2 === undefined || id2 === null || id2 === '') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// The id can be a MongoDB ObjectID
|
|
||||||
return id1 === id2 || id1.toString() === id2.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 userId: %s', modelClass && modelClass.modelName, modelId, userId);
|
|
||||||
// No userId is present
|
|
||||||
if(!userId) {
|
|
||||||
process.nextTick(function() {
|
|
||||||
callback(null, false);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is the modelClass User or a subclass of User?
|
|
||||||
if(isUserClass(modelClass)) {
|
|
||||||
process.nextTick(function() {
|
|
||||||
callback(null, matches(modelId, userId));
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
modelClass.findById(modelId, function(err, inst) {
|
|
||||||
if(err || !inst) {
|
|
||||||
debug('Model not found for id %j', modelId);
|
|
||||||
callback && callback(err, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
debug('Model found: %j', inst);
|
|
||||||
var ownerId = inst.userId || inst.owner;
|
|
||||||
if(ownerId) {
|
|
||||||
callback && callback(null, matches(ownerId, userId));
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
// Try to follow belongsTo
|
|
||||||
for(var r in modelClass.relations) {
|
|
||||||
var rel = modelClass.relations[r];
|
|
||||||
if(rel.type === 'belongsTo' && isUserClass(rel.modelTo)) {
|
|
||||||
debug('Checking relation %s to %s: %j', r, rel.modelTo.modelName, rel);
|
|
||||||
inst[r](function(err, user) {
|
|
||||||
if(!err && user) {
|
|
||||||
debug('User found: %j', user.id);
|
|
||||||
callback && callback(null, matches(user.id, userId));
|
|
||||||
} else {
|
|
||||||
callback && callback(err, false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
return mappings.map(function(m) {
|
||||||
|
return m.principalId;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Role.prototype.applications = function(callback) {
|
||||||
|
roleMappingModel.find({where: {roleId: this.id,
|
||||||
|
principalType: RoleMapping.APPLICATION}}, function(err, mappings) {
|
||||||
|
if (err) {
|
||||||
|
callback && callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return mappings.map(function(m) {
|
||||||
|
return m.principalId;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Role.prototype.roles = function(callback) {
|
||||||
|
roleMappingModel.find({where: {roleId: this.id,
|
||||||
|
principalType: RoleMapping.ROLE}}, function(err, mappings) {
|
||||||
|
if (err) {
|
||||||
|
callback && callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return mappings.map(function(m) {
|
||||||
|
return m.principalId;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// Special roles
|
||||||
|
Role.OWNER = '$owner'; // owner of the object
|
||||||
|
Role.RELATED = "$related"; // any User with a relationship to the object
|
||||||
|
Role.AUTHENTICATED = "$authenticated"; // authenticated user
|
||||||
|
Role.UNAUTHENTICATED = "$unauthenticated"; // authenticated user
|
||||||
|
Role.EVERYONE = "$everyone"; // everyone
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add custom handler for roles
|
||||||
|
* @param role
|
||||||
|
* @param resolver The resolver function decides if a principal is in the role
|
||||||
|
* dynamically
|
||||||
|
*
|
||||||
|
* function(role, context, callback)
|
||||||
|
*/
|
||||||
|
Role.registerResolver = function(role, resolver) {
|
||||||
|
if (!Role.resolvers) {
|
||||||
|
Role.resolvers = {};
|
||||||
|
}
|
||||||
|
Role.resolvers[role] = resolver;
|
||||||
|
};
|
||||||
|
|
||||||
|
Role.registerResolver(Role.OWNER, function(role, context, callback) {
|
||||||
|
if (!context || !context.model || !context.modelId) {
|
||||||
|
process.nextTick(function() {
|
||||||
|
callback && callback(null, false);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var modelClass = context.model;
|
||||||
|
var modelId = context.modelId;
|
||||||
|
var userId = context.getUserId();
|
||||||
|
Role.isOwner(modelClass, modelId, userId, callback);
|
||||||
|
});
|
||||||
|
|
||||||
|
function isUserClass(modelClass) {
|
||||||
|
return modelClass === loopback.User ||
|
||||||
|
modelClass.prototype instanceof loopback.User;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Check if two user ids matches
|
||||||
|
* @param {*} id1
|
||||||
|
* @param {*} id2
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function matches(id1, id2) {
|
||||||
|
if (id1 === undefined || id1 === null || id1 === ''
|
||||||
|
|| id2 === undefined || id2 === null || id2 === '') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// The id can be a MongoDB ObjectID
|
||||||
|
return id1 === id2 || id1.toString() === id2.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 userId: %s', modelClass && modelClass.modelName, modelId, userId);
|
||||||
|
// No userId is present
|
||||||
|
if (!userId) {
|
||||||
|
process.nextTick(function() {
|
||||||
|
callback(null, false);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is the modelClass User or a subclass of User?
|
||||||
|
if (isUserClass(modelClass)) {
|
||||||
|
process.nextTick(function() {
|
||||||
|
callback(null, matches(modelId, userId));
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
modelClass.findById(modelId, function(err, inst) {
|
||||||
|
if (err || !inst) {
|
||||||
|
debug('Model not found for id %j', modelId);
|
||||||
|
callback && callback(err, false);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
debug('No matching belongsTo relation found for model %j and user: %j', modelId, userId);
|
debug('Model found: %j', inst);
|
||||||
callback && callback(null, false);
|
var ownerId = inst.userId || inst.owner;
|
||||||
}
|
if (ownerId) {
|
||||||
});
|
callback && callback(null, matches(ownerId, userId));
|
||||||
};
|
return;
|
||||||
|
} else {
|
||||||
|
// Try to follow belongsTo
|
||||||
|
for (var r in modelClass.relations) {
|
||||||
|
var rel = modelClass.relations[r];
|
||||||
|
if (rel.type === 'belongsTo' && isUserClass(rel.modelTo)) {
|
||||||
|
debug('Checking relation %s to %s: %j', r, rel.modelTo.modelName, rel);
|
||||||
|
inst[r](function(err, user) {
|
||||||
|
if (!err && user) {
|
||||||
|
debug('User found: %j', user.id);
|
||||||
|
callback && callback(null, matches(user.id, userId));
|
||||||
|
} else {
|
||||||
|
callback && callback(err, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug('No matching belongsTo relation found for model %j and user: %j', modelId, userId);
|
||||||
|
callback && callback(null, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Role.registerResolver(Role.AUTHENTICATED, function(role, context, callback) {
|
Role.registerResolver(Role.AUTHENTICATED, function(role, context, callback) {
|
||||||
if(!context) {
|
if (!context) {
|
||||||
|
process.nextTick(function() {
|
||||||
|
callback && callback(null, false);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Role.isAuthenticated(context, callback);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the user id is authenticated
|
||||||
|
* @param {Object} context The security context
|
||||||
|
* @callback {Function} callback
|
||||||
|
* @param {Error} err
|
||||||
|
* @param {Boolean} isAuthenticated
|
||||||
|
*/
|
||||||
|
Role.isAuthenticated = function isAuthenticated(context, callback) {
|
||||||
process.nextTick(function() {
|
process.nextTick(function() {
|
||||||
callback && callback(null, false);
|
callback && callback(null, context.isAuthenticated());
|
||||||
});
|
});
|
||||||
return;
|
};
|
||||||
}
|
|
||||||
Role.isAuthenticated(context, callback);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
Role.registerResolver(Role.UNAUTHENTICATED, function(role, context, callback) {
|
||||||
* Check if the user id is authenticated
|
process.nextTick(function() {
|
||||||
* @param {Object} context The security context
|
callback && callback(null, !context || !context.isAuthenticated());
|
||||||
* @callback {Function} callback
|
|
||||||
* @param {Error} err
|
|
||||||
* @param {Boolean} isAuthenticated
|
|
||||||
*/
|
|
||||||
Role.isAuthenticated = function isAuthenticated(context, callback) {
|
|
||||||
process.nextTick(function() {
|
|
||||||
callback && callback(null, context.isAuthenticated());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Role.registerResolver(Role.UNAUTHENTICATED, function(role, context, callback) {
|
|
||||||
process.nextTick(function() {
|
|
||||||
callback && callback(null, !context || !context.isAuthenticated());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Role.registerResolver(Role.EVERYONE, function (role, context, callback) {
|
|
||||||
process.nextTick(function () {
|
|
||||||
callback && callback(null, true); // Always true
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a given principal is in the role
|
|
||||||
*
|
|
||||||
* @param {String} role The role name
|
|
||||||
* @param {Object} context The context object
|
|
||||||
* @callback {Function} callback
|
|
||||||
* @param {Error} err
|
|
||||||
* @param {Boolean} isInRole
|
|
||||||
*/
|
|
||||||
Role.isInRole = function (role, context, callback) {
|
|
||||||
if (!(context instanceof AccessContext)) {
|
|
||||||
context = new AccessContext(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
debug('isInRole(): %s', role);
|
|
||||||
context.debug();
|
|
||||||
|
|
||||||
var resolver = Role.resolvers[role];
|
|
||||||
if (resolver) {
|
|
||||||
debug('Custom resolver found for role %s', role);
|
|
||||||
resolver(role, context, callback);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.principals.length === 0) {
|
|
||||||
debug('isInRole() returns: false');
|
|
||||||
process.nextTick(function () {
|
|
||||||
callback && callback(null, false);
|
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var inRole = context.principals.some(function (p) {
|
|
||||||
|
|
||||||
var principalType = p.type || undefined;
|
|
||||||
var principalId = p.id || undefined;
|
|
||||||
|
|
||||||
// Check if it's the same role
|
|
||||||
return principalType === RoleMapping.ROLE && principalId === role;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (inRole) {
|
Role.registerResolver(Role.EVERYONE, function(role, context, callback) {
|
||||||
debug('isInRole() returns: %j', inRole);
|
process.nextTick(function() {
|
||||||
process.nextTick(function () {
|
callback && callback(null, true); // Always true
|
||||||
callback && callback(null, true);
|
|
||||||
});
|
});
|
||||||
return;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
var roleMappingModel = this.RoleMapping || loopback.getModelByType(RoleMapping);
|
/**
|
||||||
this.findOne({where: {name: role}}, function (err, result) {
|
* Check if a given principal is in the role
|
||||||
if (err) {
|
*
|
||||||
callback && callback(err);
|
* @param {String} role The role name
|
||||||
|
* @param {Object} context The context object
|
||||||
|
* @callback {Function} callback
|
||||||
|
* @param {Error} err
|
||||||
|
* @param {Boolean} isInRole
|
||||||
|
*/
|
||||||
|
Role.isInRole = function(role, context, callback) {
|
||||||
|
if (!(context instanceof AccessContext)) {
|
||||||
|
context = new AccessContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug('isInRole(): %s', role);
|
||||||
|
context.debug();
|
||||||
|
|
||||||
|
var resolver = Role.resolvers[role];
|
||||||
|
if (resolver) {
|
||||||
|
debug('Custom resolver found for role %s', role);
|
||||||
|
resolver(role, context, callback);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!result) {
|
|
||||||
callback && callback(null, false);
|
if (context.principals.length === 0) {
|
||||||
|
debug('isInRole() returns: false');
|
||||||
|
process.nextTick(function() {
|
||||||
|
callback && callback(null, false);
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
debug('Role found: %j', result);
|
|
||||||
|
|
||||||
// Iterate through the list of principals
|
var inRole = context.principals.some(function(p) {
|
||||||
async.some(context.principals, function (p, done) {
|
|
||||||
var principalType = p.type || undefined;
|
var principalType = p.type || undefined;
|
||||||
var principalId = p.id || undefined;
|
var principalId = p.id || undefined;
|
||||||
var roleId = result.id.toString();
|
|
||||||
|
// Check if it's the same role
|
||||||
if(principalId !== null && principalId !== undefined && (typeof principalId !== 'string') ) {
|
return principalType === RoleMapping.ROLE && principalId === role;
|
||||||
principalId = principalId.toString();
|
});
|
||||||
|
|
||||||
|
if (inRole) {
|
||||||
|
debug('isInRole() returns: %j', inRole);
|
||||||
|
process.nextTick(function() {
|
||||||
|
callback && callback(null, true);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var roleMappingModel = this.RoleMapping || loopback.getModelByType(RoleMapping);
|
||||||
|
this.findOne({where: {name: role}}, function(err, result) {
|
||||||
|
if (err) {
|
||||||
|
callback && callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!result) {
|
||||||
|
callback && callback(null, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
debug('Role found: %j', result);
|
||||||
|
|
||||||
|
// Iterate through the list of principals
|
||||||
|
async.some(context.principals, function(p, done) {
|
||||||
|
var principalType = p.type || undefined;
|
||||||
|
var principalId = p.id || undefined;
|
||||||
|
var roleId = result.id.toString();
|
||||||
|
|
||||||
|
if (principalId !== null && principalId !== undefined && (typeof principalId !== 'string')) {
|
||||||
|
principalId = principalId.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (principalType && principalId) {
|
||||||
|
roleMappingModel.findOne({where: {roleId: roleId,
|
||||||
|
principalType: principalType, principalId: principalId}},
|
||||||
|
function(err, result) {
|
||||||
|
debug('Role mapping found: %j', result);
|
||||||
|
done(!err && result); // The only arg is the result
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
process.nextTick(function() {
|
||||||
|
done(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, function(inRole) {
|
||||||
|
debug('isInRole() returns: %j', inRole);
|
||||||
|
callback && callback(null, inRole);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List roles for a given principal
|
||||||
|
* @param {Object} context The security context
|
||||||
|
* @param {Function} callback
|
||||||
|
*
|
||||||
|
* @callback {Function} callback
|
||||||
|
* @param err
|
||||||
|
* @param {String[]} An array of role ids
|
||||||
|
*/
|
||||||
|
Role.getRoles = function(context, callback) {
|
||||||
|
if (!(context instanceof AccessContext)) {
|
||||||
|
context = new AccessContext(context);
|
||||||
|
}
|
||||||
|
var roles = [];
|
||||||
|
|
||||||
|
var addRole = function(role) {
|
||||||
|
if (role && roles.indexOf(role) === -1) {
|
||||||
|
roles.push(role);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
// Check against the smart roles
|
||||||
|
var inRoleTasks = [];
|
||||||
|
Object.keys(Role.resolvers).forEach(function(role) {
|
||||||
|
inRoleTasks.push(function(done) {
|
||||||
|
self.isInRole(role, context, function(err, inRole) {
|
||||||
|
if (debug.enabled) {
|
||||||
|
debug('In role %j: %j', role, inRole);
|
||||||
|
}
|
||||||
|
if (!err && inRole) {
|
||||||
|
addRole(role);
|
||||||
|
done();
|
||||||
|
} else {
|
||||||
|
done(err, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var roleMappingModel = this.RoleMapping || loopback.getModelByType(RoleMapping);
|
||||||
|
context.principals.forEach(function(p) {
|
||||||
|
// Check against the role mappings
|
||||||
|
var principalType = p.type || undefined;
|
||||||
|
var principalId = p.id || undefined;
|
||||||
|
|
||||||
|
// Add the role itself
|
||||||
|
if (principalType === RoleMapping.ROLE && principalId) {
|
||||||
|
addRole(principalId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (principalType && principalId) {
|
if (principalType && principalId) {
|
||||||
roleMappingModel.findOne({where: {roleId: roleId,
|
// Please find() treat undefined matches all values
|
||||||
principalType: principalType, principalId: principalId}},
|
inRoleTasks.push(function(done) {
|
||||||
function (err, result) {
|
roleMappingModel.find({where: {principalType: principalType,
|
||||||
debug('Role mapping found: %j', result);
|
principalId: principalId}}, function(err, mappings) {
|
||||||
done(!err && result); // The only arg is the result
|
debug('Role mappings found: %s %j', err, mappings);
|
||||||
|
if (err) {
|
||||||
|
done && done(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mappings.forEach(function(m) {
|
||||||
|
addRole(m.roleId);
|
||||||
|
});
|
||||||
|
done && done();
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
process.nextTick(function () {
|
|
||||||
done(false);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, function (inRole) {
|
|
||||||
debug('isInRole() returns: %j', inRole);
|
|
||||||
callback && callback(null, inRole);
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
};
|
async.parallel(inRoleTasks, function(err, results) {
|
||||||
|
debug('getRoles() returns: %j %j', err, roles);
|
||||||
/**
|
callback && callback(err, roles);
|
||||||
* List roles for a given principal
|
});
|
||||||
* @param {Object} context The security context
|
|
||||||
* @param {Function} callback
|
|
||||||
*
|
|
||||||
* @callback {Function} callback
|
|
||||||
* @param err
|
|
||||||
* @param {String[]} An array of role ids
|
|
||||||
*/
|
|
||||||
Role.getRoles = function (context, callback) {
|
|
||||||
if(!(context instanceof AccessContext)) {
|
|
||||||
context = new AccessContext(context);
|
|
||||||
}
|
|
||||||
var roles = [];
|
|
||||||
|
|
||||||
var addRole = function (role) {
|
|
||||||
if (role && roles.indexOf(role) === -1) {
|
|
||||||
roles.push(role);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var self = this;
|
|
||||||
// Check against the smart roles
|
|
||||||
var inRoleTasks = [];
|
|
||||||
Object.keys(Role.resolvers).forEach(function (role) {
|
|
||||||
inRoleTasks.push(function (done) {
|
|
||||||
self.isInRole(role, context, function (err, inRole) {
|
|
||||||
if(debug.enabled) {
|
|
||||||
debug('In role %j: %j', role, inRole);
|
|
||||||
}
|
|
||||||
if (!err && inRole) {
|
|
||||||
addRole(role);
|
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
done(err, null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
var roleMappingModel = this.RoleMapping || loopback.getModelByType(RoleMapping);
|
|
||||||
context.principals.forEach(function (p) {
|
|
||||||
// Check against the role mappings
|
|
||||||
var principalType = p.type || undefined;
|
|
||||||
var principalId = p.id || undefined;
|
|
||||||
|
|
||||||
// Add the role itself
|
|
||||||
if (principalType === RoleMapping.ROLE && principalId) {
|
|
||||||
addRole(principalId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (principalType && principalId) {
|
|
||||||
// Please find() treat undefined matches all values
|
|
||||||
inRoleTasks.push(function (done) {
|
|
||||||
roleMappingModel.find({where: {principalType: principalType,
|
|
||||||
principalId: principalId}}, function (err, mappings) {
|
|
||||||
debug('Role mappings found: %s %j', err, mappings);
|
|
||||||
if (err) {
|
|
||||||
done && done(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mappings.forEach(function (m) {
|
|
||||||
addRole(m.roleId);
|
|
||||||
});
|
|
||||||
done && done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
async.parallel(inRoleTasks, function (err, results) {
|
|
||||||
debug('getRoles() returns: %j %j', err, roles);
|
|
||||||
callback && callback(err, roles);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
Role: Role,
|
|
||||||
RoleMapping: RoleMapping
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"name": "Role",
|
||||||
|
"properties": {
|
||||||
|
|
||||||
|
"id": {
|
||||||
|
"type": "string",
|
||||||
|
"id": true,
|
||||||
|
"generated": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"description": "string",
|
||||||
|
|
||||||
|
"created": "date",
|
||||||
|
"modified": "date"
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"principals": {
|
||||||
|
"type": "hasMany",
|
||||||
|
"model": "RoleMapping",
|
||||||
|
"foreignKey": "roleId"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,9 @@ module.exports = function(loopback) {
|
||||||
require('../common/models/role-mapping.json'),
|
require('../common/models/role-mapping.json'),
|
||||||
require('../common/models/role-mapping.js'));
|
require('../common/models/role-mapping.js'));
|
||||||
|
|
||||||
loopback.Role = require('../common/models/role').Role;
|
loopback.Role = createModel(
|
||||||
|
require('../common/models/role.json'),
|
||||||
|
require('../common/models/role.js'));
|
||||||
|
|
||||||
loopback.ACL = createModel(
|
loopback.ACL = createModel(
|
||||||
require('../common/models/acl.json'),
|
require('../common/models/acl.json'),
|
||||||
|
|
Loading…
Reference in New Issue