diff --git a/common/models/role.js b/common/models/role.js index 056388635..722f9f641 100644 --- a/common/models/role.js +++ b/common/models/role.js @@ -171,7 +171,8 @@ module.exports = function(Role) { 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 no requester id, deny the resolver if (!userId) { process.nextTick(function() { callback(null, false); @@ -193,34 +194,41 @@ module.exports = function(Role) { if (callback) callback(err, false); return; } - debug('Model found: %j', inst); - var ownerId = inst.userId || inst.owner; - // Ensure ownerId exists and is not a function/relation - if (ownerId && 'function' !== typeof ownerId) { - if (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](processRelatedUser); - return; - } + + // 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); + if (inst[r](processRelatedUser)) return callback(null, true); } + debug('No matching belongsTo relation found for model %j and user: %j', modelId, userId); - if (callback) callback(null, false); } function processRelatedUser(err, user) { if (!err && user) { debug('User found: %j', user.id); - if (callback) callback(null, matches(user.id, userId)); - } else { - if (callback) callback(err, false); + if (callback && matches(user.id, userId)) { + return true; + } + return false; } } + + // Checking the userId or owner field for resolving owner role + // is now done after fetching belongsTo relation to make possible + // to have multiple resolver role + var ownerId = inst.userId || inst.owner; + // Ensure ownerId exists and is not a function/relation + if (ownerId && 'function' !== typeof ownerId) { + if (callback && matches(ownerId, userId)) callback(null, true); + return; + } + + // Finally DENY the owner role after sending + if (callback) callback(null, false); }); }; diff --git a/test/role.test.js b/test/role.test.js index d6eccf79f..a3de8cfd1 100644 --- a/test/role.test.js +++ b/test/role.test.js @@ -434,6 +434,58 @@ describe('role model', function() { }); }); + it('should be able to handle multiple owners roles', function(done) { + var Message = app.registry.createModel('Message', { + name: String, + userId: Number, + senderId: Number, + }, { + relations: { + user: { + type: 'belongsTo', + model: 'User', + foreignKey: 'userId', + }, + sender: { + type: 'belongsTo', + model: 'User', + foreignKey: 'senderId', + }, + }, + }); + + app.model(Message, {dataSource: 'db'}); + + User.create([ + {name: 'pierre', email: 'pierre@xyz.com', password: 'foobar'}, + {name: 'henry', email: 'henry@xyz.com', password: 'foobar'}, + ], function(err, users) { + if (err) return done(err); + Message.create([ + {name: 'message1', userId: users[0].id, senderId: users[1].id}, + {name: 'message2'}, + ], function(err, messages) { + var role1 = { + principalType: ACL.USER, principalId: users[0].id, + model: Message, id: messages[0].id, + }; + var role2 = { + principalType: ACL.USER, principalId: users[1].id, + model: Message, id: messages[0].id, + }; + Role.isInRole(Role.OWNER, role1, function(err, yes) { + if (err) return done(err); + assert(yes); + Role.isInRole(Role.OWNER, role2, function(err, yes) { + if (err) return done(err); + assert(yes); + done(); + }); + }); + }); + }); + }); + describe('isMappedToRole', function() { var user, app, role;