Merge pull request #3146 from strongloop/feature/promisify-role
Promise-ify built-in Role model
This commit is contained in:
commit
5fd79b14f4
|
@ -8,6 +8,7 @@ var loopback = require('../../lib/loopback');
|
||||||
var debug = require('debug')('loopback:security:role');
|
var debug = require('debug')('loopback:security:role');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
var utils = require('../../lib/utils');
|
||||||
|
|
||||||
var AccessContext = require('../../lib/access-context').AccessContext;
|
var AccessContext = require('../../lib/access-context').AccessContext;
|
||||||
|
|
||||||
|
@ -37,21 +38,40 @@ module.exports = function(Role) {
|
||||||
* Fetch all users assigned to this role
|
* Fetch all users assigned to this role
|
||||||
* @function Role.prototype#users
|
* @function Role.prototype#users
|
||||||
* @param {object} [query] query object passed to model find call
|
* @param {object} [query] query object passed to model find call
|
||||||
* @param {Function} [callback]
|
* @callback {Function} [callback] The callback function
|
||||||
|
* @param {String|Error} err The error string or object
|
||||||
|
* @param {Array} list The list of users.
|
||||||
|
* @promise
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Fetch all applications assigned to this role
|
* Fetch all applications assigned to this role
|
||||||
* @function Role.prototype#applications
|
* @function Role.prototype#applications
|
||||||
* @param {object} [query] query object passed to model find call
|
* @param {object} [query] query object passed to model find call
|
||||||
* @param {Function} [callback]
|
* @callback {Function} [callback] The callback function
|
||||||
|
* @param {String|Error} err The error string or object
|
||||||
|
* @param {Array} list The list of applications.
|
||||||
|
* @promise
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Fetch all roles assigned to this role
|
* Fetch all roles assigned to this role
|
||||||
* @function Role.prototype#roles
|
* @function Role.prototype#roles
|
||||||
* @param {object} [query] query object passed to model find call
|
* @param {object} [query] query object passed to model find call
|
||||||
* @param {Function} [callback]
|
* @callback {Function} [callback] The callback function
|
||||||
|
* @param {String|Error} err The error string or object
|
||||||
|
* @param {Array} list The list of roles.
|
||||||
|
* @promise
|
||||||
*/
|
*/
|
||||||
Role.prototype[rel] = function(query, callback) {
|
Role.prototype[rel] = function(query, callback) {
|
||||||
|
if (!callback) {
|
||||||
|
if (typeof query === 'function') {
|
||||||
|
callback = query;
|
||||||
|
query = {};
|
||||||
|
} else {
|
||||||
|
callback = utils.createPromiseCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!query) query = {};
|
||||||
|
|
||||||
roleModel.resolveRelatedModels();
|
roleModel.resolveRelatedModels();
|
||||||
var relsToModels = {
|
var relsToModels = {
|
||||||
users: roleModel.userModel,
|
users: roleModel.userModel,
|
||||||
|
@ -68,6 +88,7 @@ module.exports = function(Role) {
|
||||||
|
|
||||||
var model = relsToModels[rel];
|
var model = relsToModels[rel];
|
||||||
listByPrincipalType(this, model, relsToTypes[rel], query, callback);
|
listByPrincipalType(this, model, relsToTypes[rel], query, callback);
|
||||||
|
return callback.promise;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -166,17 +187,23 @@ module.exports = function(Role) {
|
||||||
* @param {Function} modelClass The model class
|
* @param {Function} modelClass The model class
|
||||||
* @param {*} modelId The model ID
|
* @param {*} modelId The model ID
|
||||||
* @param {*} userId The user ID
|
* @param {*} userId The user ID
|
||||||
* @param {Function} callback Callback function
|
* @callback {Function} [callback] The callback function
|
||||||
|
* @param {String|Error} err The error string or object
|
||||||
|
* @param {Boolean} isOwner True if the user is an owner.
|
||||||
|
* @promise
|
||||||
*/
|
*/
|
||||||
Role.isOwner = function isOwner(modelClass, modelId, userId, callback) {
|
Role.isOwner = function isOwner(modelClass, modelId, userId, callback) {
|
||||||
assert(modelClass, 'Model class is required');
|
assert(modelClass, 'Model class is required');
|
||||||
|
if (!callback) callback = utils.createPromiseCallback();
|
||||||
|
|
||||||
debug('isOwner(): %s %s userId: %s', modelClass && modelClass.modelName, modelId, userId);
|
debug('isOwner(): %s %s userId: %s', modelClass && modelClass.modelName, modelId, userId);
|
||||||
|
|
||||||
// No userId is present
|
// No userId is present
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
process.nextTick(function() {
|
process.nextTick(function() {
|
||||||
callback(null, false);
|
callback(null, false);
|
||||||
});
|
});
|
||||||
return;
|
return callback.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is the modelClass User or a subclass of User?
|
// Is the modelClass User or a subclass of User?
|
||||||
|
@ -184,7 +211,7 @@ module.exports = function(Role) {
|
||||||
process.nextTick(function() {
|
process.nextTick(function() {
|
||||||
callback(null, matches(modelId, userId));
|
callback(null, matches(modelId, userId));
|
||||||
});
|
});
|
||||||
return;
|
return callback.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
modelClass.findById(modelId, function(err, inst) {
|
modelClass.findById(modelId, function(err, inst) {
|
||||||
|
@ -222,6 +249,7 @@ module.exports = function(Role) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return callback.promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
Role.registerResolver(Role.AUTHENTICATED, function(role, context, callback) {
|
Role.registerResolver(Role.AUTHENTICATED, function(role, context, callback) {
|
||||||
|
@ -241,11 +269,14 @@ module.exports = function(Role) {
|
||||||
* @callback {Function} callback Callback function.
|
* @callback {Function} callback Callback function.
|
||||||
* @param {Error} err Error object.
|
* @param {Error} err Error object.
|
||||||
* @param {Boolean} isAuthenticated True if the user is authenticated.
|
* @param {Boolean} isAuthenticated True if the user is authenticated.
|
||||||
|
* @promise
|
||||||
*/
|
*/
|
||||||
Role.isAuthenticated = function isAuthenticated(context, callback) {
|
Role.isAuthenticated = function isAuthenticated(context, callback) {
|
||||||
|
if (!callback) callback = utils.createPromiseCallback();
|
||||||
process.nextTick(function() {
|
process.nextTick(function() {
|
||||||
if (callback) callback(null, context.isAuthenticated());
|
if (callback) callback(null, context.isAuthenticated());
|
||||||
});
|
});
|
||||||
|
return callback.promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
Role.registerResolver(Role.UNAUTHENTICATED, function(role, context, callback) {
|
Role.registerResolver(Role.UNAUTHENTICATED, function(role, context, callback) {
|
||||||
|
@ -269,12 +300,23 @@ module.exports = function(Role) {
|
||||||
* @callback {Function} callback Callback function.
|
* @callback {Function} callback Callback function.
|
||||||
* @param {Error} err Error object.
|
* @param {Error} err Error object.
|
||||||
* @param {Boolean} isInRole True if the principal is in the specified role.
|
* @param {Boolean} isInRole True if the principal is in the specified role.
|
||||||
|
* @promise
|
||||||
*/
|
*/
|
||||||
Role.isInRole = function(role, context, callback) {
|
Role.isInRole = function(role, context, callback) {
|
||||||
if (!(context instanceof AccessContext)) {
|
if (!(context instanceof AccessContext)) {
|
||||||
context = new AccessContext(context);
|
context = new AccessContext(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!callback) {
|
||||||
|
callback = utils.createPromiseCallback();
|
||||||
|
// historically, isInRole is returning the Role instance instead of true
|
||||||
|
// we are preserving that behaviour for callback-based invocation,
|
||||||
|
// but fixing it when invoked in Promise mode
|
||||||
|
callback.promise = callback.promise.then(function(isInRole) {
|
||||||
|
return !!isInRole;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.resolveRelatedModels();
|
this.resolveRelatedModels();
|
||||||
|
|
||||||
debug('isInRole(): %s', role);
|
debug('isInRole(): %s', role);
|
||||||
|
@ -291,7 +333,7 @@ module.exports = function(Role) {
|
||||||
callback
|
callback
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return;
|
return callback.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.principals.length === 0) {
|
if (context.principals.length === 0) {
|
||||||
|
@ -299,7 +341,7 @@ module.exports = function(Role) {
|
||||||
process.nextTick(function() {
|
process.nextTick(function() {
|
||||||
if (callback) callback(null, false);
|
if (callback) callback(null, false);
|
||||||
});
|
});
|
||||||
return;
|
return callback.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
var inRole = context.principals.some(function(p) {
|
var inRole = context.principals.some(function(p) {
|
||||||
|
@ -315,7 +357,7 @@ module.exports = function(Role) {
|
||||||
process.nextTick(function() {
|
process.nextTick(function() {
|
||||||
if (callback) callback(null, true);
|
if (callback) callback(null, true);
|
||||||
});
|
});
|
||||||
return;
|
return callback.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
var roleMappingModel = this.roleMappingModel;
|
var roleMappingModel = this.roleMappingModel;
|
||||||
|
@ -358,6 +400,7 @@ module.exports = function(Role) {
|
||||||
if (callback) callback(null, inRole);
|
if (callback) callback(null, inRole);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
return callback.promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -367,13 +410,20 @@ module.exports = function(Role) {
|
||||||
* @callback {Function} callback Callback function.
|
* @callback {Function} callback Callback function.
|
||||||
* @param {Error} err Error object.
|
* @param {Error} err Error object.
|
||||||
* @param {String[]} roles An array of role IDs
|
* @param {String[]} roles An array of role IDs
|
||||||
|
* @promise
|
||||||
*/
|
*/
|
||||||
Role.getRoles = function(context, options, callback) {
|
Role.getRoles = function(context, options, callback) {
|
||||||
if (!callback && typeof options === 'function') {
|
if (!callback) {
|
||||||
callback = options;
|
if (typeof options === 'function') {
|
||||||
options = {};
|
callback = options;
|
||||||
|
options = {};
|
||||||
|
} else {
|
||||||
|
callback = utils.createPromiseCallback();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!options) options = {};
|
||||||
|
|
||||||
if (!(context instanceof AccessContext)) {
|
if (!(context instanceof AccessContext)) {
|
||||||
context = new AccessContext(context);
|
context = new AccessContext(context);
|
||||||
}
|
}
|
||||||
|
@ -452,6 +502,7 @@ module.exports = function(Role) {
|
||||||
debug('getRoles() returns: %j %j', err, roles);
|
debug('getRoles() returns: %j %j', err, roles);
|
||||||
if (callback) callback(err, roles);
|
if (callback) callback(err, roles);
|
||||||
});
|
});
|
||||||
|
return callback.promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
Role.validatesUniquenessOf('name', {message: 'already exists'});
|
Role.validatesUniquenessOf('name', {message: 'already exists'});
|
||||||
|
|
|
@ -305,6 +305,56 @@ describe('role model', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('supports isInRole() returning a Promise', function(done) {
|
||||||
|
var userData = {name: 'Raymond', email: 'x@y.com', password: 'foobar'};
|
||||||
|
User.create(userData, function(err, user) {
|
||||||
|
if (err) return done(err);
|
||||||
|
Role.create({name: 'userRole'}, function(err, role) {
|
||||||
|
if (err) return done(err);
|
||||||
|
var principalData = {
|
||||||
|
principalType: RoleMapping.USER,
|
||||||
|
principalId: user.id,
|
||||||
|
};
|
||||||
|
role.principals.create(principalData, function(err, p) {
|
||||||
|
if (err) return done(err);
|
||||||
|
Role.isInRole('userRole', principalData)
|
||||||
|
.then(function(inRole) {
|
||||||
|
expect(inRole).to.be.true();
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports getRole() returning a Promise', function(done) {
|
||||||
|
var userData = {name: 'Raymond', email: 'x@y.com', password: 'foobar'};
|
||||||
|
User.create(userData, function(err, user) {
|
||||||
|
if (err) return done(err);
|
||||||
|
Role.create({name: 'userRole'}, function(err, role) {
|
||||||
|
if (err) return done(err);
|
||||||
|
var principalData = {
|
||||||
|
principalType: RoleMapping.USER,
|
||||||
|
principalId: user.id,
|
||||||
|
};
|
||||||
|
role.principals.create(principalData, function(err, p) {
|
||||||
|
if (err) return done(err);
|
||||||
|
Role.getRoles(principalData)
|
||||||
|
.then(function(roles) {
|
||||||
|
expect(roles).to.eql([
|
||||||
|
Role.AUTHENTICATED,
|
||||||
|
Role.EVERYONE,
|
||||||
|
role.id,
|
||||||
|
]);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should support owner role resolver', function(done) {
|
it('should support owner role resolver', function(done) {
|
||||||
Role.registerResolver('returnPromise', function(role, context) {
|
Role.registerResolver('returnPromise', function(role, context) {
|
||||||
return new Promise(function(resolve) {
|
return new Promise(function(resolve) {
|
||||||
|
@ -730,6 +780,30 @@ describe('role model', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('supports Promise API', function(done) {
|
||||||
|
var userData = {name: 'Raymond', email: 'x@y.com', password: 'foobar'};
|
||||||
|
User.create(userData, function(err, user) {
|
||||||
|
if (err) return done(err);
|
||||||
|
Role.create({name: 'userRole'}, function(err, role) {
|
||||||
|
if (err) return done(err);
|
||||||
|
var principalData = {
|
||||||
|
principalType: RoleMapping.USER,
|
||||||
|
principalId: user.id,
|
||||||
|
};
|
||||||
|
role.principals.create(principalData, function(err, p) {
|
||||||
|
if (err) return done(err);
|
||||||
|
role.users()
|
||||||
|
.then(function(users) {
|
||||||
|
var userIds = users.map(function(u) { return u.id; });
|
||||||
|
expect(userIds).to.eql([user.id]);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('isOwner', function() {
|
describe('isOwner', function() {
|
||||||
|
@ -759,5 +833,19 @@ describe('role model', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('supports Promise API', function(done) {
|
||||||
|
var credentials = {email: 'test@example.com', password: 'pass'};
|
||||||
|
User.create(credentials, function(err, user) {
|
||||||
|
if (err) return done(err);
|
||||||
|
|
||||||
|
Role.isOwner(User, user.id, user.id)
|
||||||
|
.then(function(result) {
|
||||||
|
expect(result, 'isOwner result').to.equal(true);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue