Update eslint and eslint-config to latest

This commit is contained in:
Miroslav Bajtoš 2017-12-12 09:33:15 +01:00
parent fdb453943a
commit 73cc950b1b
No known key found for this signature in database
GPG Key ID: 6F2304BA9361C7E3
32 changed files with 1277 additions and 1287 deletions

View File

@ -111,8 +111,8 @@ module.exports = function(AccessToken) {
// replacement for deprecated req.param() // replacement for deprecated req.param()
id = req.params && req.params[param] !== undefined ? req.params[param] : id = req.params && req.params[param] !== undefined ? req.params[param] :
req.body && req.body[param] !== undefined ? req.body[param] : req.body && req.body[param] !== undefined ? req.body[param] :
req.query && req.query[param] !== undefined ? req.query[param] : req.query && req.query[param] !== undefined ? req.query[param] :
undefined; undefined;
if (typeof id === 'string') { if (typeof id === 'string') {
return id; return id;
@ -227,7 +227,7 @@ module.exports = function(AccessToken) {
AccessToken.prototype.validate = function(cb) { AccessToken.prototype.validate = function(cb) {
try { try {
assert( assert(
this.created && typeof this.created.getTime === 'function', this.created && typeof this.created.getTime === 'function',
'token.created must be a valid Date' 'token.created must be a valid Date'
); );
assert(this.ttl !== 0, 'token.ttl must be not be 0'); assert(this.ttl !== 0, 'token.ttl must be not be 0');

View File

@ -332,8 +332,8 @@ module.exports = function(ACL) {
* @param {AccessRequest} result The resolved access request. * @param {AccessRequest} result The resolved access request.
*/ */
ACL.checkPermission = function checkPermission(principalType, principalId, ACL.checkPermission = function checkPermission(principalType, principalId,
model, property, accessType, model, property, accessType,
callback) { callback) {
if (!callback) callback = utils.createPromiseCallback(); if (!callback) callback = utils.createPromiseCallback();
if (principalId !== null && principalId !== undefined && (typeof principalId !== 'string')) { if (principalId !== null && principalId !== undefined && (typeof principalId !== 'string')) {
principalId = principalId.toString(); principalId = principalId.toString();
@ -363,15 +363,15 @@ module.exports = function(ACL) {
var self = this; var self = this;
this.find({where: {principalType: principalType, principalId: principalId, this.find({where: {principalType: principalType, principalId: principalId,
model: model, property: propertyQuery, accessType: accessTypeQuery}}, model: model, property: propertyQuery, accessType: accessTypeQuery}},
function(err, dynACLs) { function(err, dynACLs) {
if (err) { if (err) {
return callback(err); return callback(err);
} }
acls = acls.concat(dynACLs); acls = acls.concat(dynACLs);
// resolved is an instance of AccessRequest // resolved is an instance of AccessRequest
resolved = self.resolvePermission(acls, req); resolved = self.resolvePermission(acls, req);
return callback(null, resolved); return callback(null, resolved);
}); });
return callback.promise; return callback.promise;
}; };

View File

@ -223,7 +223,7 @@ module.exports = function(KeyValueModel) {
function throwNotAttached(modelName, methodName) { function throwNotAttached(modelName, methodName) {
throw new Error(g.f( throw new Error(g.f(
'Cannot call %s.%s(). ' + 'Cannot call %s.%s(). ' +
'The %s method has not been setup. ' + 'The %s method has not been setup. ' +
'The {{KeyValueModel}} has not been correctly attached ' + 'The {{KeyValueModel}} has not been correctly attached ' +
'to a {{DataSource}}!', 'to a {{DataSource}}!',
modelName, methodName, methodName)); modelName, methodName, methodName));

View File

@ -539,10 +539,10 @@ module.exports = function(Role) {
if (principalType && principalId) { if (principalType && principalId) {
roleMappingModel.findOne({where: {roleId: roleId, roleMappingModel.findOne({where: {roleId: roleId,
principalType: principalType, principalId: principalId}}, principalType: principalType, principalId: principalId}},
function(err, result) { function(err, result) {
debug('Role mapping found: %j', result); debug('Role mapping found: %j', result);
done(!err && result); // The only arg is the result done(!err && result); // The only arg is the result
}); });
} else { } else {
process.nextTick(function() { process.nextTick(function() {
done(false); done(false);

View File

@ -749,9 +749,9 @@ module.exports = function(User) {
displayPort + displayPort +
urlPath + urlPath +
'?' + qs.stringify({ '?' + qs.stringify({
uid: '' + verifyOptions.user[pkName], uid: '' + verifyOptions.user[pkName],
redirect: verifyOptions.redirect, redirect: verifyOptions.redirect,
}); });
verifyOptions.to = verifyOptions.to || user.email; verifyOptions.to = verifyOptions.to || user.email;
verifyOptions.subject = verifyOptions.subject || g.f('Thanks for Registering'); verifyOptions.subject = verifyOptions.subject || g.f('Thanks for Registering');

View File

@ -349,9 +349,9 @@ app.enableAuth = function(options) {
var modelId = modelInstance && modelInstance.id || var modelId = modelInstance && modelInstance.id ||
// replacement for deprecated req.param() // replacement for deprecated req.param()
(req.params && req.params.id !== undefined ? req.params.id : (req.params && req.params.id !== undefined ? req.params.id :
req.body && req.body.id !== undefined ? req.body.id : req.body && req.body.id !== undefined ? req.body.id :
req.query && req.query.id !== undefined ? req.query.id : req.query && req.query.id !== undefined ? req.query.id :
undefined); undefined);
var modelName = Model.modelName; var modelName = Model.modelName;
@ -464,7 +464,7 @@ app._verifyAuthModelRelations = function() {
console.warn( console.warn(
'The app configuration follows the multiple user models setup ' + 'The app configuration follows the multiple user models setup ' +
'as described in http://ibm.biz/setup-loopback-auth', 'as described in http://ibm.biz/setup-loopback-auth',
'The built-in role resolver $owner is not currently compatible ' + 'The built-in role resolver $owner is not currently compatible ' +
'with this configuration and should not be used in production.'); 'with this configuration and should not be used in production.');
} }

View File

@ -845,7 +845,7 @@ module.exports = function(registry) {
], ],
description: format('Counts %s of %s.', scopeName, this.modelName), description: format('Counts %s of %s.', scopeName, this.modelName),
accessType: 'READ', accessType: 'READ',
returns: {arg: 'count', type: 'number'}, returns: {arg: 'count', type: 'number'},
}); });
}; };
@ -890,7 +890,7 @@ module.exports = function(registry) {
acceptArgs = [ acceptArgs = [
{ {
arg: paramName, type: 'any', http: {source: 'path'}, arg: paramName, type: 'any', http: {source: 'path'},
description: format('Foreign key for %s.', relation.name), description: format('Foreign key for %s.', relation.name),
required: true, required: true,
}, },
]; ];

View File

@ -1485,7 +1485,7 @@ module.exports = function(registry) {
function applyUpdate(Model, id, current, data, change, conflicts, options, cb) { function applyUpdate(Model, id, current, data, change, conflicts, options, cb) {
var Change = Model.getChangeModel(); var Change = Model.getChangeModel();
var rev = current ? Change.revisionForInst(current) : null; var rev = current ? Change.revisionForInst(current) : null;
if (rev !== change.prev) { if (rev !== change.prev) {
debug('Detected non-rectified change of %s %j', debug('Detected non-rectified change of %s %j',

View File

@ -230,7 +230,7 @@ Registry.prototype.configureModel = function(ModelCtor, config) {
// configuration, so that the datasource picks up updated relations // configuration, so that the datasource picks up updated relations
if (config.dataSource) { if (config.dataSource) {
assert(config.dataSource instanceof DataSource, assert(config.dataSource instanceof DataSource,
'Cannot configure ' + ModelCtor.modelName + 'Cannot configure ' + ModelCtor.modelName +
': config.dataSource must be an instance of DataSource'); ': config.dataSource must be an instance of DataSource');
ModelCtor.attachTo(config.dataSource); ModelCtor.attachTo(config.dataSource);
debug('Attached model `%s` to dataSource `%s`', debug('Attached model `%s` to dataSource `%s`',
@ -398,7 +398,7 @@ Registry.prototype.memory = function(name) {
name = name || 'default'; name = name || 'default';
var memory = ( var memory = (
this._memoryDataSources || (this._memoryDataSources = {}) this._memoryDataSources || (this._memoryDataSources = {})
)[name]; )[name];
if (!memory) { if (!memory) {
memory = this._memoryDataSources[name] = this.createDataSource({ memory = this._memoryDataSources[name] = this.createDataSource({

View File

@ -226,7 +226,7 @@ proto._findLayerByHandler = function(handler) {
if (this._router.stack[k].handle === handler || if (this._router.stack[k].handle === handler ||
// NewRelic replaces the handle and keeps it as __NR_original // NewRelic replaces the handle and keeps it as __NR_original
this._router.stack[k].handle['__NR_original'] === handler this._router.stack[k].handle['__NR_original'] === handler
) { ) {
return this._router.stack[k]; return this._router.stack[k];
} else { } else {
// Aggressively check if the original handler has been wrapped // Aggressively check if the original handler has been wrapped

View File

@ -68,14 +68,14 @@
"cookie-parser": "^1.3.4", "cookie-parser": "^1.3.4",
"coveralls": "^2.11.15", "coveralls": "^2.11.15",
"dirty-chai": "^1.2.2", "dirty-chai": "^1.2.2",
"eslint-config-loopback": "^8.0.0", "eslint-config-loopback": "^10.0.0",
"express-session": "^1.14.0", "express-session": "^1.14.0",
"grunt": "^1.0.1", "grunt": "^1.0.1",
"grunt-browserify": "^5.0.0", "grunt-browserify": "^5.0.0",
"grunt-cli": "^1.2.0", "grunt-cli": "^1.2.0",
"grunt-contrib-uglify": "^2.0.0", "grunt-contrib-uglify": "^2.0.0",
"grunt-contrib-watch": "^1.0.0", "grunt-contrib-watch": "^1.0.0",
"grunt-eslint": "^19.0.0", "grunt-eslint": "^20.1.0",
"grunt-karma": "^2.0.0", "grunt-karma": "^2.0.0",
"grunt-mocha-test": "^0.12.7", "grunt-mocha-test": "^0.12.7",
"karma": "^1.1.2", "karma": "^1.1.2",

View File

@ -30,7 +30,7 @@ function rewriteUserLiteral(req, currentUserLiteral, next) {
// Replace /me/ with /current-user-id/ // Replace /me/ with /current-user-id/
var urlBeforeRewrite = req.url; var urlBeforeRewrite = req.url;
req.url = req.url.replace(literalRegExp, req.url = req.url.replace(literalRegExp,
'/' + req.accessToken.userId + '$1'); '/' + req.accessToken.userId + '$1');
if (req.url !== urlBeforeRewrite) { if (req.url !== urlBeforeRewrite) {
debug('req.url has been rewritten from %s to %s', urlBeforeRewrite, debug('req.url has been rewritten from %s to %s', urlBeforeRewrite,

View File

@ -139,39 +139,39 @@ describe('loopback.token(options)', function() {
}); });
it('does not search default keys when searchDefaultTokenKeys is false', it('does not search default keys when searchDefaultTokenKeys is false',
function(done) { function(done) {
var tokenId = this.token.id; var tokenId = this.token.id;
var app = createTestApp( var app = createTestApp(
this.token, this.token,
{token: {searchDefaultTokenKeys: false}}, {token: {searchDefaultTokenKeys: false}},
done); done);
var agent = request.agent(app); var agent = request.agent(app);
// Set the token cookie // Set the token cookie
agent.get('/token').expect(200).end(function(err, res) { agent.get('/token').expect(200).end(function(err, res) {
if (err) return done(err); if (err) return done(err);
// Make a request that sets the token in all places searched by default // Make a request that sets the token in all places searched by default
agent.get('/check-access?access_token=' + tokenId) agent.get('/check-access?access_token=' + tokenId)
.set('X-Access-Token', tokenId) .set('X-Access-Token', tokenId)
.set('authorization', tokenId) .set('authorization', tokenId)
// Expect 401 because there is no (non-default) place configured where // Expect 401 because there is no (non-default) place configured where
// the middleware should load the token from // the middleware should load the token from
.expect(401) .expect(401)
.end(done); .end(done);
});
}); });
});
it('populates req.token from an authorization header with bearer token with base64', it('populates req.token from an authorization header with bearer token with base64',
function(done) { function(done) {
var token = this.token.id; var token = this.token.id;
token = 'Bearer ' + new Buffer(token).toString('base64'); token = 'Bearer ' + new Buffer(token).toString('base64');
createTestAppAndRequest(this.token, done) createTestAppAndRequest(this.token, done)
.get('/') .get('/')
.set('authorization', token) .set('authorization', token)
.expect(200) .expect(200)
.end(done); .end(done);
}); });
it('populates req.token from an authorization header with bearer token', function(done) { it('populates req.token from an authorization header with bearer token', function(done) {
var token = this.token.id; var token = this.token.id;
@ -346,29 +346,29 @@ describe('loopback.token(options)', function() {
describe('loading multiple instances of token middleware', function() { describe('loading multiple instances of token middleware', function() {
it('skips when req.token is already present and no further options are set', it('skips when req.token is already present and no further options are set',
function(done) { function(done) {
var tokenStub = {id: 'stub id'}; var tokenStub = {id: 'stub id'};
app.use(function(req, res, next) { app.use(function(req, res, next) {
req.accessToken = tokenStub; req.accessToken = tokenStub;
next(); next();
});
app.use(loopback.token({model: Token}));
app.get('/', function(req, res, next) {
res.send(req.accessToken);
});
request(app).get('/')
.set('Authorization', this.token.id)
.expect(200)
.end(function(err, res) {
if (err) return done(err);
expect(res.body).to.eql(tokenStub);
done();
}); });
}); app.use(loopback.token({model: Token}));
app.get('/', function(req, res, next) {
res.send(req.accessToken);
});
request(app).get('/')
.set('Authorization', this.token.id)
.expect(200)
.end(function(err, res) {
if (err) return done(err);
expect(res.body).to.eql(tokenStub);
done();
});
});
it('does not overwrite valid existing token (has "id" property) ' + it('does not overwrite valid existing token (has "id" property) ' +
' when overwriteExistingToken is falsy', ' when overwriteExistingToken is falsy',
@ -509,21 +509,21 @@ describe('AccessToken', function() {
}); });
it('allows eternal tokens when enabled by User.allowEternalTokens', it('allows eternal tokens when enabled by User.allowEternalTokens',
function(done) { function(done) {
var Token = givenLocalTokenModel(); var Token = givenLocalTokenModel();
// Overwrite User settings - enable eternal tokens // Overwrite User settings - enable eternal tokens
Token.app.models.User.settings.allowEternalTokens = true; Token.app.models.User.settings.allowEternalTokens = true;
Token.create({userId: '123', ttl: -1}, function(err, token) { Token.create({userId: '123', ttl: -1}, function(err, token) {
if (err) return done(err);
token.validate(function(err, isValid) {
if (err) return done(err); if (err) return done(err);
expect(isValid, 'isValid').to.equal(true); token.validate(function(err, isValid) {
done(); if (err) return done(err);
expect(isValid, 'isValid').to.equal(true);
done();
});
}); });
}); });
});
}); });
describe('.findForRequest()', function() { describe('.findForRequest()', function() {
@ -649,7 +649,7 @@ describe('app.enableAuth()', function() {
}); });
}); });
it('prevent remote call with app setting status on denied ACL', function(done) { it('denies remote call with app setting status 403', function(done) {
createTestAppAndRequest(this.token, {app: {aclErrorStatus: 403}}, done) createTestAppAndRequest(this.token, {app: {aclErrorStatus: 403}}, done)
.del('/tests/123') .del('/tests/123')
.expect(403) .expect(403)
@ -667,7 +667,7 @@ describe('app.enableAuth()', function() {
}); });
}); });
it('prevent remote call with app setting status on denied ACL', function(done) { it('denies remote call with app setting status 404', function(done) {
createTestAppAndRequest(this.token, {model: {aclErrorStatus: 404}}, done) createTestAppAndRequest(this.token, {model: {aclErrorStatus: 404}}, done)
.del('/tests/123') .del('/tests/123')
.expect(404) .expect(404)

View File

@ -50,51 +50,51 @@ describe('security scopes', function() {
it('should allow access to models for the given scope by wildcard', function() { it('should allow access to models for the given scope by wildcard', function() {
Scope.create({name: 'userScope', description: 'access user information'}, Scope.create({name: 'userScope', description: 'access user information'},
function(err, scope) { function(err, scope) {
ACL.create({ ACL.create({
principalType: ACL.SCOPE, principalId: scope.id, principalType: ACL.SCOPE, principalId: scope.id,
model: 'User', property: ACL.ALL, model: 'User', property: ACL.ALL,
accessType: ACL.ALL, permission: ACL.ALLOW, accessType: ACL.ALL, permission: ACL.ALLOW,
}, function(err, resource) { }, function(err, resource) {
Scope.checkPermission('userScope', 'User', ACL.ALL, ACL.ALL, checkResult); Scope.checkPermission('userScope', 'User', ACL.ALL, ACL.ALL, checkResult);
Scope.checkPermission('userScope', 'User', 'name', ACL.ALL, checkResult); Scope.checkPermission('userScope', 'User', 'name', ACL.ALL, checkResult);
Scope.checkPermission('userScope', 'User', 'name', ACL.READ, checkResult); Scope.checkPermission('userScope', 'User', 'name', ACL.READ, checkResult);
});
}); });
});
}); });
it('should allow access to models for the given scope', function() { it('should allow access to models for the given scope', function() {
Scope.create({name: 'testModelScope', description: 'access testModel information'}, Scope.create({name: 'testModelScope', description: 'access testModel information'},
function(err, scope) { function(err, scope) {
ACL.create({ ACL.create({
principalType: ACL.SCOPE, principalId: scope.id, principalType: ACL.SCOPE, principalId: scope.id,
model: 'testModel', property: 'name',
accessType: ACL.READ, permission: ACL.ALLOW,
}, function(err, resource) {
ACL.create({principalType: ACL.SCOPE, principalId: scope.id,
model: 'testModel', property: 'name', model: 'testModel', property: 'name',
accessType: ACL.WRITE, permission: ACL.DENY, accessType: ACL.READ, permission: ACL.ALLOW,
}, function(err, resource) { }, function(err, resource) {
ACL.create({principalType: ACL.SCOPE, principalId: scope.id,
model: 'testModel', property: 'name',
accessType: ACL.WRITE, permission: ACL.DENY,
}, function(err, resource) {
// console.log(resource); // console.log(resource);
Scope.checkPermission('testModelScope', 'testModel', ACL.ALL, ACL.ALL, Scope.checkPermission('testModelScope', 'testModel', ACL.ALL, ACL.ALL,
function(err, perm) { function(err, perm) {
assert(perm.permission === ACL.DENY); // because name.WRITE == DENY assert(perm.permission === ACL.DENY); // because name.WRITE == DENY
}); });
Scope.checkPermission('testModelScope', 'testModel', 'name', ACL.ALL, Scope.checkPermission('testModelScope', 'testModel', 'name', ACL.ALL,
function(err, perm) { function(err, perm) {
assert(perm.permission === ACL.DENY); // because name.WRITE == DENY assert(perm.permission === ACL.DENY); // because name.WRITE == DENY
}); });
Scope.checkPermission('testModelScope', 'testModel', 'name', ACL.READ, Scope.checkPermission('testModelScope', 'testModel', 'name', ACL.READ,
function(err, perm) { function(err, perm) {
assert(perm.permission === ACL.ALLOW); assert(perm.permission === ACL.ALLOW);
}); });
Scope.checkPermission('testModelScope', 'testModel', 'name', ACL.WRITE, Scope.checkPermission('testModelScope', 'testModel', 'name', ACL.WRITE,
function(err, perm) { function(err, perm) {
assert(perm.permission === ACL.DENY); assert(perm.permission === ACL.DENY);
}); });
});
}); });
}); });
});
}); });
}); });
@ -108,12 +108,12 @@ describe('security ACLs', function() {
accessType: ACL.ALL, accessType: ACL.ALL,
permission: ACL.ALLOW, permission: ACL.ALLOW,
}) })
.then(function() { .then(function() {
return ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.ALL); return ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.ALL);
}) })
.then(function(access) { .then(function(access) {
assert(access.permission === ACL.ALLOW); assert(access.permission === ACL.ALLOW);
}); });
}); });
it('supports checkAccessForContext() returning a promise', function() { it('supports checkAccessForContext() returning a promise', function() {
@ -129,9 +129,9 @@ describe('security ACLs', function() {
model: 'testModel', model: 'testModel',
accessType: ACL.ALL, accessType: ACL.ALL,
}) })
.then(function(access) { .then(function(access) {
assert(access.permission === ACL.ALLOW); assert(access.permission === ACL.ALLOW);
}); });
}); });
it('should order ACL entries based on the matching score', function() { it('should order ACL entries based on the matching score', function() {
@ -256,34 +256,34 @@ describe('security ACLs', function() {
accessType: ACL.EXECUTE, permission: ACL.ALLOW, accessType: ACL.EXECUTE, permission: ACL.ALLOW,
}, function(err, acl) { }, function(err, acl) {
ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.READ, ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.READ,
function(err, perm) { function(err, perm) {
assert(perm.permission === ACL.ALLOW); assert(perm.permission === ACL.ALLOW);
}); });
ACL.checkPermission(ACL.USER, 'u001', 'testModel', ACL.ALL, ACL.READ, ACL.checkPermission(ACL.USER, 'u001', 'testModel', ACL.ALL, ACL.READ,
function(err, perm) { function(err, perm) {
assert(perm.permission === ACL.ALLOW); assert(perm.permission === ACL.ALLOW);
}); });
ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.WRITE, ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.WRITE,
function(err, perm) { function(err, perm) {
assert(perm.permission === ACL.DENY); assert(perm.permission === ACL.DENY);
}); });
ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.ALL, ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.ALL,
function(err, perm) { function(err, perm) {
assert(perm.permission === ACL.DENY); assert(perm.permission === ACL.DENY);
}); });
ACL.checkPermission(ACL.USER, 'u002', 'testModel', 'name', ACL.WRITE, ACL.checkPermission(ACL.USER, 'u002', 'testModel', 'name', ACL.WRITE,
function(err, perm) { function(err, perm) {
assert(perm.permission === ACL.ALLOW); assert(perm.permission === ACL.ALLOW);
}); });
ACL.checkPermission(ACL.USER, 'u002', 'testModel', 'name', ACL.READ, ACL.checkPermission(ACL.USER, 'u002', 'testModel', 'name', ACL.READ,
function(err, perm) { function(err, perm) {
assert(perm.permission === ACL.ALLOW); assert(perm.permission === ACL.ALLOW);
}); });
}); });
}); });
}); });
@ -311,9 +311,9 @@ describe('security ACLs', function() {
Customer.settings.defaultPermission = ACL.DENY; Customer.settings.defaultPermission = ACL.DENY;
ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.WRITE, ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.WRITE,
function(err, perm) { function(err, perm) {
assert(perm.permission === ACL.DENY); assert(perm.permission === ACL.DENY);
}); });
ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.READ, function(err, perm) { ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.READ, function(err, perm) {
assert(perm.permission === ACL.ALLOW); assert(perm.permission === ACL.ALLOW);
@ -353,29 +353,29 @@ describe('security ACLs', function() {
*/ */
ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.WRITE, ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.WRITE,
function(err, perm) { function(err, perm) {
assert(perm.permission === ACL.DENY); assert(perm.permission === ACL.DENY);
}); });
ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.READ, ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.READ,
function(err, perm) { function(err, perm) {
assert(perm.permission === ACL.ALLOW); assert(perm.permission === ACL.ALLOW);
}); });
ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.ALL, ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.ALL,
function(err, perm) { function(err, perm) {
assert(perm.permission === ACL.ALLOW); assert(perm.permission === ACL.ALLOW);
}); });
ACL.checkPermission(ACL.USER, 'u002', 'Customer', 'name', ACL.READ, ACL.checkPermission(ACL.USER, 'u002', 'Customer', 'name', ACL.READ,
function(err, perm) { function(err, perm) {
assert(perm.permission === ACL.ALLOW); assert(perm.permission === ACL.ALLOW);
}); });
ACL.checkPermission(ACL.USER, 'u003', 'Customer', 'name', ACL.WRITE, ACL.checkPermission(ACL.USER, 'u003', 'Customer', 'name', ACL.WRITE,
function(err, perm) { function(err, perm) {
assert(perm.permission === ACL.DENY); assert(perm.permission === ACL.DENY);
}); });
}); });
it('should filter static ACLs by model/property', function() { it('should filter static ACLs by model/property', function() {
@ -451,39 +451,39 @@ describe('security ACLs', function() {
log('Role: ', myRole.toObject()); log('Role: ', myRole.toObject());
myRole.principals.create({principalType: RoleMapping.USER, principalId: userId}, myRole.principals.create({principalType: RoleMapping.USER, principalId: userId},
function(err, p) { function(err, p) {
log('Principal added to role: ', p.toObject()); log('Principal added to role: ', p.toObject());
ACL.create({ ACL.create({
principalType: ACL.ROLE, principalId: 'MyRole', principalType: ACL.ROLE, principalId: 'MyRole',
model: 'Customer', property: ACL.ALL, model: 'Customer', property: ACL.ALL,
accessType: ACL.READ, permission: ACL.DENY, accessType: ACL.READ, permission: ACL.DENY,
}, function(err, acl) { }, function(err, acl) {
log('ACL 2: ', acl.toObject()); log('ACL 2: ', acl.toObject());
ACL.checkAccessForContext({ ACL.checkAccessForContext({
principals: [ principals: [
{type: ACL.USER, id: userId}, {type: ACL.USER, id: userId},
], ],
model: 'Customer', model: 'Customer',
property: 'name', property: 'name',
accessType: ACL.READ, accessType: ACL.READ,
}, function(err, access) { }, function(err, access) {
assert(!err && access.permission === ACL.ALLOW); assert(!err && access.permission === ACL.ALLOW);
}); });
ACL.checkAccessForContext({ ACL.checkAccessForContext({
principals: [ principals: [
{type: ACL.ROLE, id: Role.EVERYONE}, {type: ACL.ROLE, id: Role.EVERYONE},
], ],
model: 'Customer', model: 'Customer',
property: 'name', property: 'name',
accessType: ACL.READ, accessType: ACL.READ,
}, function(err, access) { }, function(err, access) {
assert(!err && access.permission === ACL.DENY); assert(!err && access.permission === ACL.DENY);
});
}); });
}); });
});
}); });
}); });
}); });
@ -543,35 +543,35 @@ describe('authorized roles propagation in RemotingContext', function() {
{permission: ACL.ALLOW, principalId: '$authenticated'}, {permission: ACL.ALLOW, principalId: '$authenticated'},
{permission: ACL.ALLOW, principalId: 'myRole'}, {permission: ACL.ALLOW, principalId: 'myRole'},
]) ])
.then(makeAuthorizedHttpRequestOnMyTestModel) .then(makeAuthorizedHttpRequestOnMyTestModel)
.then(function() { .then(function() {
var ctx = models.MyTestModel.lastRemotingContext; var ctx = models.MyTestModel.lastRemotingContext;
expect(ctx.args.options.authorizedRoles).to.eql( expect(ctx.args.options.authorizedRoles).to.eql(
{ {
$everyone: true, $everyone: true,
$authenticated: true, $authenticated: true,
myRole: true, myRole: true,
} }
); );
}); });
}); });
it('does not contain any denied role even if query is allowed', function() { it('does not contain any denied role even if query is allowed', function() {
return createACLs('MyTestModel', [ return createACLs('MyTestModel', [
{permission: ACL.ALLOW, principalId: '$everyone'}, {permission: ACL.ALLOW, principalId: '$everyone'},
{permission: ACL.DENY, principalId: '$authenticated'}, {permission: ACL.DENY, principalId: '$authenticated'},
{permission: ACL.ALLOW, principalId: 'myRole'}, {permission: ACL.ALLOW, principalId: 'myRole'},
]) ])
.then(makeAuthorizedHttpRequestOnMyTestModel) .then(makeAuthorizedHttpRequestOnMyTestModel)
.then(function() { .then(function() {
var ctx = models.MyTestModel.lastRemotingContext; var ctx = models.MyTestModel.lastRemotingContext;
expect(ctx.args.options.authorizedRoles).to.eql( expect(ctx.args.options.authorizedRoles).to.eql(
{ {
$everyone: true, $everyone: true,
myRole: true, myRole: true,
} }
); );
}); });
}); });
it('honors default permission setting', function() { it('honors default permission setting', function() {
@ -580,17 +580,17 @@ describe('authorized roles propagation in RemotingContext', function() {
return createACLs('MyTestModel', [ return createACLs('MyTestModel', [
{permission: ACL.DEFAULT, principalId: '$everyone'}, {permission: ACL.DEFAULT, principalId: '$everyone'},
{permission: ACL.DENY, principalId: '$authenticated'}, {permission: ACL.DENY, principalId: '$authenticated'},
{permission: ACL.ALLOW, principalId: 'myRole'}, {permission: ACL.ALLOW, principalId: 'myRole'},
]) ])
.then(makeAuthorizedHttpRequestOnMyTestModel) .then(makeAuthorizedHttpRequestOnMyTestModel)
.then(function() { .then(function() {
var ctx = models.MyTestModel.lastRemotingContext; var ctx = models.MyTestModel.lastRemotingContext;
expect(ctx.args.options.authorizedRoles).to.eql( expect(ctx.args.options.authorizedRoles).to.eql(
// '$everyone' is not expected as default permission is DENY // '$everyone' is not expected as default permission is DENY
{myRole: true} {myRole: true}
); );
}); });
}); });
// helpers // helpers
@ -619,15 +619,15 @@ describe('authorized roles propagation in RemotingContext', function() {
models.User.create({username: 'myUser', email: 'myuser@example.com', password: 'pass'}), models.User.create({username: 'myUser', email: 'myuser@example.com', password: 'pass'}),
models.Role.create({name: 'myRole'}), models.Role.create({name: 'myRole'}),
]) ])
.spread(function(myUser, myRole) { .spread(function(myUser, myRole) {
return Promise.all([ return Promise.all([
myRole.principals.create({principalType: 'USER', principalId: myUser.id}), myRole.principals.create({principalType: 'USER', principalId: myUser.id}),
models.User.login({username: 'myUser', password: 'pass'}), models.User.login({username: 'myUser', password: 'pass'}),
]); ]);
}) })
.spread(function(role, token) { .spread(function(role, token) {
accessToken = token; accessToken = token;
}); });
} }
function createACLs(model, acls) { function createACLs(model, acls) {

View File

@ -523,7 +523,7 @@ describe('app', function() {
}); });
}); });
it('scopes middleware to a list of scopes', function(done) { it('scopes middleware from config to a list of scopes', function(done) {
var steps = []; var steps = [];
app.middlewareFromConfig( app.middlewareFromConfig(
function factory() { function factory() {

View File

@ -157,12 +157,12 @@ describe('Change', function() {
beforeEach(function(done) { beforeEach(function(done) {
var test = this; var test = this;
Change.findOrCreateChange(this.modelName, this.modelId) Change.findOrCreateChange(this.modelName, this.modelId)
.then(function(result) { .then(function(result) {
test.result = result; test.result = result;
done(); done();
}) })
.catch(done); .catch(done);
}); });
it('should create an entry', function(done) { it('should create an entry', function(done) {
@ -352,12 +352,12 @@ describe('Change', function() {
}); });
change.currentRevision() change.currentRevision()
.then(function(rev) { .then(function(rev) {
assert.equal(rev, test.revisionForModel); assert.equal(rev, test.revisionForModel);
done(); done();
}) })
.catch(done); .catch(done);
}); });
}); });

View File

@ -78,13 +78,13 @@ describe('Checkpoint', function() {
}); });
it('Checkpoint.current() for non existing checkpoint should initialize checkpoint', it('Checkpoint.current() for non existing checkpoint should initialize checkpoint',
function(done) { function(done) {
Checkpoint.current(function(err, seq) { Checkpoint.current(function(err, seq) {
expect(seq).to.equal(1); expect(seq).to.equal(1);
done(err); done(err);
});
}); });
});
it('bumpLastSeq() works when singleton instance does not exists yet', function(done) { it('bumpLastSeq() works when singleton instance does not exists yet', function(done) {
Checkpoint.bumpLastSeq(function(err, cp) { Checkpoint.bumpLastSeq(function(err, cp) {

View File

@ -44,17 +44,17 @@ describe('hidden properties', function() {
it('should hide a property remotely', function(done) { it('should hide a property remotely', function(done) {
request(this.app) request(this.app)
.get('/products') .get('/products')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.end(function(err, res) { .end(function(err, res) {
if (err) return done(err); if (err) return done(err);
var product = res.body[0]; var product = res.body[0];
assert.equal(product.secret, undefined); assert.equal(product.secret, undefined);
done(); done();
}); });
}); });
it('should hide a property of nested models', function(done) { it('should hide a property of nested models', function(done) {

View File

@ -58,23 +58,23 @@ describe('KeyValueModel', function() {
}); });
it('provides "expire(key, ttl)" at "PUT /key/expire"', it('provides "expire(key, ttl)" at "PUT /key/expire"',
function(done) { function(done) {
CacheItem.set('expire-key', AN_OBJECT_VALUE, function(err) { CacheItem.set('expire-key', AN_OBJECT_VALUE, function(err) {
if (err) return done(err); if (err) return done(err);
request.put('/CacheItems/expire-key/expire') request.put('/CacheItems/expire-key/expire')
.send({ttl: 10}) .send({ttl: 10})
.end(function(err, res) { .end(function(err, res) {
if (err) return done(err); if (err) return done(err);
setTimeout(function() { setTimeout(function() {
CacheItem.get('set-key-ttl', function(err, value) { CacheItem.get('set-key-ttl', function(err, value) {
if (err) return done(err); if (err) return done(err);
expect(value).to.equal(null); expect(value).to.equal(null);
done(); done();
}); });
}, 20); }, 20);
}); });
});
}); });
});
it('returns 404 when expiring a key that does not exist', function(done) { it('returns 404 when expiring a key that does not exist', function(done) {
request.put('/CacheItems/key-does-not-exist/expire') request.put('/CacheItems/key-does-not-exist/expire')
@ -96,32 +96,32 @@ describe('KeyValueModel', function() {
}); });
it('returns 204 when getting TTL for a key that does not have TTL set', it('returns 204 when getting TTL for a key that does not have TTL set',
function(done) { function(done) {
request.put('/CacheItems/ttl-key') request.put('/CacheItems/ttl-key')
.end(function(err, res) { .end(function(err, res) {
if (err) return done(err);
request.get('/CacheItems/ttl-key/ttl')
.expect(204, done);
});
});
it('returns 404 when getting TTL for a key when TTL has expired',
function(done) {
request.put('/CacheItems/ttl-key?ttl=10')
.end(function(err, res) {
setTimeout(function() {
if (err) return done(err); if (err) return done(err);
request.get('/CacheItems/ttl-key/ttl') request.get('/CacheItems/ttl-key/ttl')
.expect(404, done); .expect(204, done);
}, 20); });
}); });
});
it('returns 404 when getting TTL for a key when TTL has expired',
function(done) {
request.put('/CacheItems/ttl-key?ttl=10')
.end(function(err, res) {
setTimeout(function() {
if (err) return done(err);
request.get('/CacheItems/ttl-key/ttl')
.expect(404, done);
}, 20);
});
});
it('returns 404 when getting TTL for a key that does not exist', it('returns 404 when getting TTL for a key that does not exist',
function(done) { function(done) {
request.get('/CacheItems/key-does-not-exist/ttl') request.get('/CacheItems/key-does-not-exist/ttl')
.expect(404, done); .expect(404, done);
}); });
it('provides "keys(filter)" at "GET /keys"', function(done) { it('provides "keys(filter)" at "GET /keys"', function(done) {
CacheItem.set('list-key', AN_OBJECT_VALUE, function(err) { CacheItem.set('list-key', AN_OBJECT_VALUE, function(err) {

View File

@ -15,7 +15,7 @@ describe('Application', function() {
Application.attachTo(loopback.memory()); Application.attachTo(loopback.memory());
}); });
it('honors `application.register` - promise variant', function(done) { it('honors `application.register` - callback variant', function(done) {
Application.register('rfeng', 'MyTestApp', Application.register('rfeng', 'MyTestApp',
{description: 'My test application'}, function(err, result) { {description: 'My test application'}, function(err, result) {
var app = result; var app = result;
@ -89,31 +89,31 @@ describe('Application', function() {
serverApiKey: 'serverKey', serverApiKey: 'serverKey',
}, },
}}, }},
function(err, result) { function(err, result) {
var app = result; var app = result;
assert.deepEqual(app.pushSettings.toObject(), { assert.deepEqual(app.pushSettings.toObject(), {
apns: { apns: {
production: false, production: false,
certData: 'cert', certData: 'cert',
keyData: 'key', keyData: 'key',
pushOptions: { pushOptions: {
gateway: 'gateway.sandbox.push.apple.com', gateway: 'gateway.sandbox.push.apple.com',
port: 2195, port: 2195,
},
feedbackOptions: {
gateway: 'feedback.sandbox.push.apple.com',
port: 2196,
interval: 300,
batchFeedback: true,
},
}, },
gcm: { feedbackOptions: {
serverApiKey: 'serverKey', gateway: 'feedback.sandbox.push.apple.com',
port: 2196,
interval: 300,
batchFeedback: true,
}, },
}); },
gcm: {
done(err, result); serverApiKey: 'serverKey',
},
}); });
done(err, result);
});
}); });
beforeEach(function(done) { beforeEach(function(done) {
@ -164,32 +164,32 @@ describe('Application', function() {
it('Reset keys - promise variant', function(done) { it('Reset keys - promise variant', function(done) {
Application.resetKeys(registeredApp.id) Application.resetKeys(registeredApp.id)
.then(function(result) { .then(function(result) {
var app = result; var app = result;
assert.equal(app.owner, 'rfeng'); assert.equal(app.owner, 'rfeng');
assert.equal(app.name, 'MyApp2'); assert.equal(app.name, 'MyApp2');
assert.equal(app.description, 'My second mobile application'); assert.equal(app.description, 'My second mobile application');
assert(app.clientKey); assert(app.clientKey);
assert(app.javaScriptKey); assert(app.javaScriptKey);
assert(app.restApiKey); assert(app.restApiKey);
assert(app.windowsKey); assert(app.windowsKey);
assert(app.masterKey); assert(app.masterKey);
assert(app.clientKey !== registeredApp.clientKey); assert(app.clientKey !== registeredApp.clientKey);
assert(app.javaScriptKey !== registeredApp.javaScriptKey); assert(app.javaScriptKey !== registeredApp.javaScriptKey);
assert(app.restApiKey !== registeredApp.restApiKey); assert(app.restApiKey !== registeredApp.restApiKey);
assert(app.windowsKey !== registeredApp.windowsKey); assert(app.windowsKey !== registeredApp.windowsKey);
assert(app.masterKey !== registeredApp.masterKey); assert(app.masterKey !== registeredApp.masterKey);
assert(app.created); assert(app.created);
assert(app.modified); assert(app.modified);
registeredApp = app; registeredApp = app;
done(); done();
}) })
.catch(function(err) { .catch(function(err) {
done(err); done(err);
}); });
}); });
it('Reset keys without create a new instance', function(done) { it('Reset keys without create a new instance', function(done) {
@ -205,17 +205,17 @@ describe('Application', function() {
it('Reset keys without create a new instance - promise variant', function(done) { it('Reset keys without create a new instance - promise variant', function(done) {
Application.resetKeys(registeredApp.id) Application.resetKeys(registeredApp.id)
.then(function(result) { .then(function(result) {
var app = result; var app = result;
assert(app.id); assert(app.id);
assert(app.id === registeredApp.id); assert(app.id === registeredApp.id);
registeredApp = app; registeredApp = app;
done(); done();
}) })
.catch(function(err) { .catch(function(err) {
done(err); done(err);
}); });
}); });
it('Authenticate with application id & clientKey', function(done) { it('Authenticate with application id & clientKey', function(done) {
@ -231,15 +231,15 @@ describe('Application', function() {
it('Authenticate with application id & clientKey - promise variant', it('Authenticate with application id & clientKey - promise variant',
function(done) { function(done) {
Application.authenticate(registeredApp.id, registeredApp.clientKey) Application.authenticate(registeredApp.id, registeredApp.clientKey)
.then(function(result) { .then(function(result) {
assert.equal(result.application.id, registeredApp.id); assert.equal(result.application.id, registeredApp.id);
assert.equal(result.keyType, 'clientKey'); assert.equal(result.keyType, 'clientKey');
done(); done();
}) })
.catch(function(err) { .catch(function(err) {
done(err); done(err);
}); });
}); });
it('Authenticate with application id & javaScriptKey', function(done) { it('Authenticate with application id & javaScriptKey', function(done) {
@ -293,15 +293,15 @@ describe('Application', function() {
it('Fail to authenticate with application id - promise variant', function(done) { it('Fail to authenticate with application id - promise variant', function(done) {
Application.authenticate(registeredApp.id, 'invalid-key') Application.authenticate(registeredApp.id, 'invalid-key')
.then(function(result) { .then(function(result) {
assert(!result); assert(!result);
done(); done();
}) })
.catch(function(err) { .catch(function(err) {
done(err); done(err);
throw new Error('Error should NOT be thrown'); throw new Error('Error should NOT be thrown');
}); });
}); });
}); });

View File

@ -68,15 +68,15 @@ describe('Multiple users with custom principalType', function() {
AnotherUser.create(commonCredentials), AnotherUser.create(commonCredentials),
Role.create({name: 'userRole'}), Role.create({name: 'userRole'}),
]) ])
.spread(function(u1, u2, r) { .spread(function(u1, u2, r) {
userFromOneModel = u1; userFromOneModel = u1;
userFromAnotherModel = u2; userFromAnotherModel = u2;
userRole = r; userRole = r;
userOneBaseContext = { userOneBaseContext = {
principalType: OneUser.modelName, principalType: OneUser.modelName,
principalId: userFromOneModel.id, principalId: userFromOneModel.id,
}; };
}); });
}); });
describe('User.login', function() { describe('User.login', function() {
@ -118,29 +118,29 @@ describe('Multiple users with custom principalType', function() {
describe('User.logout', function() { describe('User.logout', function() {
it('logs out a user from user model 1 without logging out user from model 2', it('logs out a user from user model 1 without logging out user from model 2',
function() { function() {
var tokenOfOneUser; var tokenOfOneUser;
return Promise.all([ return Promise.all([
OneUser.login(commonCredentials), OneUser.login(commonCredentials),
AnotherUser.login(commonCredentials), AnotherUser.login(commonCredentials),
]) ])
.spread(function(t1, t2) { .spread(function(t1, t2) {
tokenOfOneUser = t1; tokenOfOneUser = t1;
return OneUser.logout(tokenOfOneUser.id); return OneUser.logout(tokenOfOneUser.id);
}) })
.then(function() { .then(function() {
return AccessToken.find({}); return AccessToken.find({});
}) })
.then(function(allTokens) { .then(function(allTokens) {
var data = allTokens.map(function(token) { var data = allTokens.map(function(token) {
return {userId: token.userId, principalType: token.principalType}; return {userId: token.userId, principalType: token.principalType};
}); });
expect(data).to.eql([ expect(data).to.eql([
// no token for userFromAnotherModel // no token for userFromAnotherModel
{userId: userFromAnotherModel.id, principalType: 'AnotherUser'}, {userId: userFromAnotherModel.id, principalType: 'AnotherUser'},
]); ]);
});
}); });
});
}); });
describe('Password Reset', function() { describe('Password Reset', function() {
@ -151,23 +151,23 @@ describe('Multiple users with custom principalType', function() {
}; };
it('creates a temp accessToken to allow a user to change password', it('creates a temp accessToken to allow a user to change password',
function() { function() {
return Promise.all([ return Promise.all([
OneUser.resetPassword({email: options.email}), OneUser.resetPassword({email: options.email}),
waitForResetRequestAndVerify, waitForResetRequestAndVerify,
]); ]);
}); });
function waitForResetRequestAndVerify() { function waitForResetRequestAndVerify() {
return waitForEvent(OneUser, 'resetPasswordRequest') return waitForEvent(OneUser, 'resetPasswordRequest')
.then(function(info) { .then(function(info) {
assertGoodToken(info.accessToken, userFromOneModel); assertGoodToken(info.accessToken, userFromOneModel);
return info.accessToken.user.getAsync(); return info.accessToken.user.getAsync();
}) })
.then(function(user) { .then(function(user) {
expect(user).to.have.property('id', userFromOneModel.id); expect(user).to.have.property('id', userFromOneModel.id);
expect(user).to.have.property('email', userFromOneModel.email); expect(user).to.have.property('email', userFromOneModel.email);
}); });
} }
}); });
}); });
@ -214,51 +214,51 @@ describe('Multiple users with custom principalType', function() {
describe('getUser()', function() { describe('getUser()', function() {
it('returns user although principals contain non USER principals', it('returns user although principals contain non USER principals',
function() { function() {
return Promise.try(function() { return Promise.try(function() {
addToAccessContext([ addToAccessContext([
{type: Principal.ROLE}, {type: Principal.ROLE},
{type: Principal.APP}, {type: Principal.APP},
{type: Principal.SCOPE}, {type: Principal.SCOPE},
{type: OneUser.modelName, id: userFromOneModel.id}, {type: OneUser.modelName, id: userFromOneModel.id},
]); ]);
var user = accessContext.getUser(); var user = accessContext.getUser();
expect(user).to.eql({ expect(user).to.eql({
id: userFromOneModel.id, id: userFromOneModel.id,
principalType: OneUser.modelName, principalType: OneUser.modelName,
});
}); });
}); });
});
it('returns user although principals contain invalid principals', it('returns user although principals contain invalid principals',
function() { function() {
return Promise.try(function() { return Promise.try(function() {
addToAccessContext([ addToAccessContext([
{type: 'AccessToken'}, {type: 'AccessToken'},
{type: 'invalidModelName'}, {type: 'invalidModelName'},
{type: OneUser.modelName, id: userFromOneModel.id}, {type: OneUser.modelName, id: userFromOneModel.id},
]); ]);
var user = accessContext.getUser(); var user = accessContext.getUser();
expect(user).to.eql({ expect(user).to.eql({
id: userFromOneModel.id, id: userFromOneModel.id,
principalType: OneUser.modelName, principalType: OneUser.modelName,
});
}); });
}); });
});
it('supports any level of built-in User model inheritance', it('supports any level of built-in User model inheritance',
function() { function() {
ThirdUser = createUserModel(app, 'ThirdUser', {base: 'OneUser'}); ThirdUser = createUserModel(app, 'ThirdUser', {base: 'OneUser'});
return ThirdUser.create(commonCredentials) return ThirdUser.create(commonCredentials)
.then(function(userFromThirdModel) { .then(function(userFromThirdModel) {
accessContext.addPrincipal(ThirdUser.modelName, userFromThirdModel.id); accessContext.addPrincipal(ThirdUser.modelName, userFromThirdModel.id);
var user = accessContext.getUser(); var user = accessContext.getUser();
expect(user).to.eql({ expect(user).to.eql({
id: userFromThirdModel.id, id: userFromThirdModel.id,
principalType: ThirdUser.modelName, principalType: ThirdUser.modelName,
}); });
});
}); });
});
}); });
// helper // helper
@ -347,7 +347,7 @@ describe('Multiple users with custom principalType', function() {
it('supports getRoles()', function() { it('supports getRoles()', function() {
return Role.getRoles( return Role.getRoles(
userOneBaseContext) userOneBaseContext)
.then(function(roles) { .then(function(roles) {
expect(roles).to.eql([ expect(roles).to.eql([
Role.AUTHENTICATED, Role.AUTHENTICATED,
@ -390,18 +390,18 @@ describe('Multiple users with custom principalType', function() {
app.model(Album, {dataSource: 'db'}); app.model(Album, {dataSource: 'db'});
return Album.create({name: 'album', userId: userFromOneModel.id}) return Album.create({name: 'album', userId: userFromOneModel.id})
.then(function(album) { .then(function(album) {
var validContext = { var validContext = {
principalType: OneUser.modelName, principalType: OneUser.modelName,
principalId: userFromOneModel.id, principalId: userFromOneModel.id,
model: Album, model: Album,
id: album.id, id: album.id,
}; };
return Role.isInRole(Role.OWNER, validContext); return Role.isInRole(Role.OWNER, validContext);
}) })
.then(function(isOwner) { .then(function(isOwner) {
expect(isOwner).to.be.true(); expect(isOwner).to.be.true();
}); });
}); });
// With multiple users config, we cannot resolve a user based just on // With multiple users config, we cannot resolve a user based just on
@ -419,18 +419,18 @@ describe('Multiple users with custom principalType', function() {
userId: userFromOneModel.id, userId: userFromOneModel.id,
owner: userFromOneModel.id, owner: userFromOneModel.id,
}) })
.then(function(album) { .then(function(album) {
var authContext = { var authContext = {
principalType: OneUser.modelName, principalType: OneUser.modelName,
principalId: userFromOneModel.id, principalId: userFromOneModel.id,
model: Album, model: Album,
id: album.id, id: album.id,
}; };
return Role.isInRole(Role.OWNER, authContext); return Role.isInRole(Role.OWNER, authContext);
}) })
.then(function(isOwner) { .then(function(isOwner) {
expect(isOwner).to.be.false(); expect(isOwner).to.be.false();
}); });
}); });
it('legacy behavior resolves false if owner has incorrect principalType', function() { it('legacy behavior resolves false if owner has incorrect principalType', function() {
@ -449,106 +449,106 @@ describe('Multiple users with custom principalType', function() {
app.model(Album, {dataSource: 'db'}); app.model(Album, {dataSource: 'db'});
return Album.create({name: 'album', userId: userFromOneModel.id}) return Album.create({name: 'album', userId: userFromOneModel.id})
.then(function(album) { .then(function(album) {
var invalidPrincipalTypes = [ var invalidPrincipalTypes = [
'invalidContextName', 'invalidContextName',
'USER', 'USER',
AnotherUser.modelName, AnotherUser.modelName,
]; ];
var invalidContexts = invalidPrincipalTypes.map(principalType => { var invalidContexts = invalidPrincipalTypes.map(principalType => {
return { return {
principalType, principalType,
principalId: userFromOneModel.id, principalId: userFromOneModel.id,
model: Album, model: Album,
id: album.id, id: album.id,
}; };
});
return Promise.map(invalidContexts, context => {
return Role.isInRole(Role.OWNER, context)
.then(isOwner => {
return {
principalType: context.principalType,
isOwner,
};
});
});
})
.then(result => {
expect(result).to.eql([
{principalType: 'invalidContextName', isOwner: false},
{principalType: 'USER', isOwner: false},
{principalType: AnotherUser.modelName, isOwner: false},
]);
}); });
return Promise.map(invalidContexts, context => {
return Role.isInRole(Role.OWNER, context)
.then(isOwner => {
return {
principalType: context.principalType,
isOwner,
};
});
});
})
.then(result => {
expect(result).to.eql([
{principalType: 'invalidContextName', isOwner: false},
{principalType: 'USER', isOwner: false},
{principalType: AnotherUser.modelName, isOwner: false},
]);
});
}); });
it.skip('resolves the owner using the corrent belongsTo relation', it.skip('resolves the owner using the corrent belongsTo relation',
function() { function() {
// passing {ownerRelations: true} will enable the new $owner role resolver // passing {ownerRelations: true} will enable the new $owner role resolver
// with any belongsTo relation allowing to resolve truthy // with any belongsTo relation allowing to resolve truthy
var Message = createModelWithOptions( var Message = createModelWithOptions(
'ModelWithAllRelations', 'ModelWithAllRelations',
{ownerRelations: true} {ownerRelations: true}
); );
var messages = [ var messages = [
{content: 'firstMessage', customerId: userFromOneModel.id}, {content: 'firstMessage', customerId: userFromOneModel.id},
{ {
content: 'secondMessage', content: 'secondMessage',
customerId: userFromOneModel.id, customerId: userFromOneModel.id,
shopKeeperId: userFromAnotherModel.id, shopKeeperId: userFromAnotherModel.id,
}, },
// this is the incriminated message where two foreignKeys have the // this is the incriminated message where two foreignKeys have the
// same id but points towards two different user models. Although // same id but points towards two different user models. Although
// customers should come from userFromOneModel and shopKeeperIds should // customers should come from userFromOneModel and shopKeeperIds should
// come from userFromAnotherModel. The inverted situation still resolves // come from userFromAnotherModel. The inverted situation still resolves
// isOwner true for both the customer and the shopKeeper // isOwner true for both the customer and the shopKeeper
{ {
content: 'thirdMessage', content: 'thirdMessage',
customerId: userFromAnotherModel.id, customerId: userFromAnotherModel.id,
shopKeeperId: userFromOneModel.id, shopKeeperId: userFromOneModel.id,
}, },
{content: 'fourthMessage', customerId: userFromAnotherModel.id}, {content: 'fourthMessage', customerId: userFromAnotherModel.id},
{content: 'fifthMessage'}, {content: 'fifthMessage'},
]; ];
return Promise.map(messages, msg => { return Promise.map(messages, msg => {
return Message.create(msg); return Message.create(msg);
}) })
.then(messages => { .then(messages => {
return Promise.all([ return Promise.all([
isOwnerForMessage(userFromOneModel, messages[0]), isOwnerForMessage(userFromOneModel, messages[0]),
isOwnerForMessage(userFromAnotherModel, messages[0]), isOwnerForMessage(userFromAnotherModel, messages[0]),
isOwnerForMessage(userFromOneModel, messages[1]), isOwnerForMessage(userFromOneModel, messages[1]),
isOwnerForMessage(userFromAnotherModel, messages[1]), isOwnerForMessage(userFromAnotherModel, messages[1]),
isOwnerForMessage(userFromOneModel, messages[2]), isOwnerForMessage(userFromOneModel, messages[2]),
isOwnerForMessage(userFromAnotherModel, messages[2]), isOwnerForMessage(userFromAnotherModel, messages[2]),
isOwnerForMessage(userFromAnotherModel, messages[3]), isOwnerForMessage(userFromAnotherModel, messages[3]),
isOwnerForMessage(userFromOneModel, messages[4]), isOwnerForMessage(userFromOneModel, messages[4]),
]); ]);
}) })
.then(result => { .then(result => {
expect(result).to.eql([ expect(result).to.eql([
{userFrom: 'OneUser', msg: 'firstMessage', isOwner: true}, {userFrom: 'OneUser', msg: 'firstMessage', isOwner: true},
{userFrom: 'AnotherUser', msg: 'firstMessage', isOwner: false}, {userFrom: 'AnotherUser', msg: 'firstMessage', isOwner: false},
{userFrom: 'OneUser', msg: 'secondMessage', isOwner: true}, {userFrom: 'OneUser', msg: 'secondMessage', isOwner: true},
{userFrom: 'AnotherUser', msg: 'secondMessage', isOwner: true}, {userFrom: 'AnotherUser', msg: 'secondMessage', isOwner: true},
// these 2 tests fail because we cannot resolve ownership with // these 2 tests fail because we cannot resolve ownership with
// multiple owners on a single model instance with a classic // multiple owners on a single model instance with a classic
// belongsTo relation, we need to use belongsTo with polymorphic // belongsTo relation, we need to use belongsTo with polymorphic
// discriminator to distinguish between the 2 models // discriminator to distinguish between the 2 models
{userFrom: 'OneUser', msg: 'thirdMessage', isOwner: false}, {userFrom: 'OneUser', msg: 'thirdMessage', isOwner: false},
{userFrom: 'AnotherUser', msg: 'thirdMessage', isOwner: false}, {userFrom: 'AnotherUser', msg: 'thirdMessage', isOwner: false},
{userFrom: 'AnotherUser', msg: 'fourthMessage', isOwner: false}, {userFrom: 'AnotherUser', msg: 'fourthMessage', isOwner: false},
{userFrom: 'OneUser', msg: 'fifthMessage', isOwner: false}, {userFrom: 'OneUser', msg: 'fifthMessage', isOwner: false},
]); ]);
});
}); });
});
}); });
// helpers // helpers
@ -608,26 +608,26 @@ describe('Multiple users with custom principalType', function() {
}); });
it('throws error with code \'INVALID_PRINCIPAL_TYPE\' when principalType is incorrect', it('throws error with code \'INVALID_PRINCIPAL_TYPE\' when principalType is incorrect',
function() { function() {
return ACL.resolvePrincipal('incorrectPrincipalType', userFromOneModel.id) return ACL.resolvePrincipal('incorrectPrincipalType', userFromOneModel.id)
.then( .then(
function onSuccess() { function onSuccess() {
throw new Error('ACL.resolvePrincipal() should have failed'); throw new Error('ACL.resolvePrincipal() should have failed');
}, },
function onError(err) { function onError(err) {
expect(err).to.have.property('statusCode', 400); expect(err).to.have.property('statusCode', 400);
expect(err).to.have.property('code', 'INVALID_PRINCIPAL_TYPE'); expect(err).to.have.property('code', 'INVALID_PRINCIPAL_TYPE');
} }
); );
}); });
it('reports isMappedToRole by user.username using custom user principalType', it('reports isMappedToRole by user.username using custom user principalType',
function() { function() {
return ACL.isMappedToRole(OneUser.modelName, userFromOneModel.username, 'userRole') return ACL.isMappedToRole(OneUser.modelName, userFromOneModel.username, 'userRole')
.then(function(isMappedToRole) { .then(function(isMappedToRole) {
expect(isMappedToRole).to.be.true(); expect(isMappedToRole).to.be.true();
}); });
}); });
}); });
}); });
@ -669,7 +669,7 @@ describe('Multiple users with custom principalType', function() {
OneUser.resetPassword({email: commonCredentials.email}), OneUser.resetPassword({email: commonCredentials.email}),
waitForEvent(OneUser, 'resetPasswordRequest'), waitForEvent(OneUser, 'resetPasswordRequest'),
]) ])
.spread((reset, info) => resetToken = info.accessToken); .spread((reset, info) => resetToken = info.accessToken);
} }
}); });

View File

@ -63,15 +63,15 @@ describe('relations - integration', function() {
test.team = team; test.team = team;
app.models.Reader.create({name: 'Reader 1'}, app.models.Reader.create({name: 'Reader 1'},
function(err, reader) { function(err, reader) {
if (err) return done(err); if (err) return done(err);
test.reader = reader; test.reader = reader;
reader.pictures.create({name: 'Picture 1'}); reader.pictures.create({name: 'Picture 1'});
reader.pictures.create({name: 'Picture 2'}); reader.pictures.create({name: 'Picture 2'});
reader.team(test.team); reader.team(test.team);
reader.save(done); reader.save(done);
}); });
} }
); );
}); });
@ -157,9 +157,9 @@ describe('relations - integration', function() {
beforeEach(function() { beforeEach(function() {
debug('GET /api/stores/:id/widgets response: %s' + debug('GET /api/stores/:id/widgets response: %s' +
'\nheaders: %j\nbody string: %s', '\nheaders: %j\nbody string: %s',
this.res.statusCode, this.res.statusCode,
this.res.headers, this.res.headers,
this.res.text); this.res.text);
this.widgets = this.res.body; this.widgets = this.res.body;
this.widget = this.res.body && this.res.body[0]; this.widget = this.res.body && this.res.body[0];
}); });
@ -661,8 +661,8 @@ describe('relations - integration', function() {
this.app.remotes()._typeRegistry._options.warnWhenOverridingType = false; this.app.remotes()._typeRegistry._options.warnWhenOverridingType = false;
var product = app.registry.createModel( var product = app.registry.createModel(
'product', 'product',
{id: 'string', name: 'string'} {id: 'string', name: 'string'}
); );
var category = app.registry.createModel( var category = app.registry.createModel(
'category', 'category',
@ -996,7 +996,7 @@ describe('relations - integration', function() {
}); });
}); });
it('returns the embedded models', function(done) { it('includes the created embedded model', function(done) {
var url = '/api/todo-lists/' + this.todoList.id + '/items'; var url = '/api/todo-lists/' + this.todoList.id + '/items';
this.get(url) this.get(url)
@ -1096,7 +1096,7 @@ describe('relations - integration', function() {
var ingredient = app.registry.createModel( var ingredient = app.registry.createModel(
'ingredient', 'ingredient',
{name: 'string'} {name: 'string'}
); );
app.model(ingredient, {dataSource: 'db'}); app.model(ingredient, {dataSource: 'db'});
@ -1311,7 +1311,7 @@ describe('relations - integration', function() {
}); });
}); });
it('returns the referenced models - verify', function(done) { it('returns the referenced models without the deleted one', function(done) {
var url = '/api/recipes/' + this.recipe.id + '/ingredients'; var url = '/api/recipes/' + this.recipe.id + '/ingredients';
var test = this; var test = this;
@ -1370,7 +1370,7 @@ describe('relations - integration', function() {
}); });
}); });
it('returns the referenced models - verify', function(done) { it('returns the referenced models without the unlinked one', function(done) {
var url = '/api/recipes/' + this.recipe.id + '/ingredients'; var url = '/api/recipes/' + this.recipe.id + '/ingredients';
var test = this; var test = this;
@ -1517,17 +1517,17 @@ describe('relations - integration', function() {
test.book = book; test.book = book;
book.pages.create({name: 'Page 1'}, book.pages.create({name: 'Page 1'},
function(err, page) { function(err, page) {
if (err) return done(err); if (err) return done(err);
test.page = page; test.page = page;
page.notes.create({text: 'Page Note 1'}, page.notes.create({text: 'Page Note 1'},
function(err, note) { function(err, note) {
test.note = note; test.note = note;
done(); done();
});
}); });
});
}); });
}); });

View File

@ -228,33 +228,33 @@ describe('With model.settings.replaceOnPUT false', function() {
}); });
it('should have expected remote methods', it('should have expected remote methods',
function() { function() {
var storeClass = findClass('storeWithReplaceOnPUTfalse'); var storeClass = findClass('storeWithReplaceOnPUTfalse');
var methods = getFormattedMethodsExcludingRelations(storeClass.methods); var methods = getFormattedMethodsExcludingRelations(storeClass.methods);
var expectedMethods = [ var expectedMethods = [
'create(data:object:storeWithReplaceOnPUTfalse):storeWithReplaceOnPUTfalse POST /stores-updating', 'create(data:object:storeWithReplaceOnPUTfalse):storeWithReplaceOnPUTfalse POST /stores-updating',
'patchOrCreate(data:object:storeWithReplaceOnPUTfalse):storeWithReplaceOnPUTfalse PUT /stores-updating', 'patchOrCreate(data:object:storeWithReplaceOnPUTfalse):storeWithReplaceOnPUTfalse PUT /stores-updating',
'patchOrCreate(data:object:storeWithReplaceOnPUTfalse):storeWithReplaceOnPUTfalse PATCH /stores-updating', 'patchOrCreate(data:object:storeWithReplaceOnPUTfalse):storeWithReplaceOnPUTfalse PATCH /stores-updating',
'replaceOrCreate(data:object:storeWithReplaceOnPUTfalse):storeWithReplaceOnPUTfalse POST /stores-updating/replaceOrCreate', 'replaceOrCreate(data:object:storeWithReplaceOnPUTfalse):storeWithReplaceOnPUTfalse POST /stores-updating/replaceOrCreate',
'upsertWithWhere(where:object,data:object:storeWithReplaceOnPUTfalse):storeWithReplaceOnPUTfalse POST /stores-updating/upsertWithWhere', 'upsertWithWhere(where:object,data:object:storeWithReplaceOnPUTfalse):storeWithReplaceOnPUTfalse POST /stores-updating/upsertWithWhere',
'exists(id:any):boolean GET /stores-updating/:id/exists', 'exists(id:any):boolean GET /stores-updating/:id/exists',
'exists(id:any):boolean HEAD /stores-updating/:id', 'exists(id:any):boolean HEAD /stores-updating/:id',
'findById(id:any,filter:object):storeWithReplaceOnPUTfalse GET /stores-updating/:id', 'findById(id:any,filter:object):storeWithReplaceOnPUTfalse GET /stores-updating/:id',
'replaceById(id:any,data:object:storeWithReplaceOnPUTfalse):storeWithReplaceOnPUTfalse POST /stores-updating/:id/replace', 'replaceById(id:any,data:object:storeWithReplaceOnPUTfalse):storeWithReplaceOnPUTfalse POST /stores-updating/:id/replace',
'find(filter:object):storeWithReplaceOnPUTfalse GET /stores-updating', 'find(filter:object):storeWithReplaceOnPUTfalse GET /stores-updating',
'findOne(filter:object):storeWithReplaceOnPUTfalse GET /stores-updating/findOne', 'findOne(filter:object):storeWithReplaceOnPUTfalse GET /stores-updating/findOne',
'updateAll(where:object,data:object:storeWithReplaceOnPUTfalse):object POST /stores-updating/update', 'updateAll(where:object,data:object:storeWithReplaceOnPUTfalse):object POST /stores-updating/update',
'deleteById(id:any):object DELETE /stores-updating/:id', 'deleteById(id:any):object DELETE /stores-updating/:id',
'count(where:object):number GET /stores-updating/count', 'count(where:object):number GET /stores-updating/count',
'prototype.patchAttributes(data:object:storeWithReplaceOnPUTfalse):storeWithReplaceOnPUTfalse PUT /stores-updating/:id', 'prototype.patchAttributes(data:object:storeWithReplaceOnPUTfalse):storeWithReplaceOnPUTfalse PUT /stores-updating/:id',
'prototype.patchAttributes(data:object:storeWithReplaceOnPUTfalse):storeWithReplaceOnPUTfalse PATCH /stores-updating/:id', 'prototype.patchAttributes(data:object:storeWithReplaceOnPUTfalse):storeWithReplaceOnPUTfalse PATCH /stores-updating/:id',
'createChangeStream(options:object):ReadableStream POST /stores-updating/change-stream', 'createChangeStream(options:object):ReadableStream POST /stores-updating/change-stream',
'createChangeStream(options:object):ReadableStream GET /stores-updating/change-stream', 'createChangeStream(options:object):ReadableStream GET /stores-updating/change-stream',
]; ];
expect(methods).to.eql(expectedMethods); expect(methods).to.eql(expectedMethods);
}); });
}); });
describe('With model.settings.replaceOnPUT true', function() { describe('With model.settings.replaceOnPUT true', function() {
@ -265,21 +265,21 @@ describe('With model.settings.replaceOnPUT true', function() {
}); });
it('should have expected remote methods', it('should have expected remote methods',
function() { function() {
var storeClass = findClass('storeWithReplaceOnPUTtrue'); var storeClass = findClass('storeWithReplaceOnPUTtrue');
var methods = getFormattedMethodsExcludingRelations(storeClass.methods); var methods = getFormattedMethodsExcludingRelations(storeClass.methods);
var expectedMethods = [ var expectedMethods = [
'patchOrCreate(data:object:storeWithReplaceOnPUTtrue):storeWithReplaceOnPUTtrue PATCH /stores-replacing', 'patchOrCreate(data:object:storeWithReplaceOnPUTtrue):storeWithReplaceOnPUTtrue PATCH /stores-replacing',
'replaceOrCreate(data:object:storeWithReplaceOnPUTtrue):storeWithReplaceOnPUTtrue POST /stores-replacing/replaceOrCreate', 'replaceOrCreate(data:object:storeWithReplaceOnPUTtrue):storeWithReplaceOnPUTtrue POST /stores-replacing/replaceOrCreate',
'replaceOrCreate(data:object:storeWithReplaceOnPUTtrue):storeWithReplaceOnPUTtrue PUT /stores-replacing', 'replaceOrCreate(data:object:storeWithReplaceOnPUTtrue):storeWithReplaceOnPUTtrue PUT /stores-replacing',
'replaceById(id:any,data:object:storeWithReplaceOnPUTtrue):storeWithReplaceOnPUTtrue POST /stores-replacing/:id/replace', 'replaceById(id:any,data:object:storeWithReplaceOnPUTtrue):storeWithReplaceOnPUTtrue POST /stores-replacing/:id/replace',
'replaceById(id:any,data:object:storeWithReplaceOnPUTtrue):storeWithReplaceOnPUTtrue PUT /stores-replacing/:id', 'replaceById(id:any,data:object:storeWithReplaceOnPUTtrue):storeWithReplaceOnPUTtrue PUT /stores-replacing/:id',
'prototype.patchAttributes(data:object:storeWithReplaceOnPUTtrue):storeWithReplaceOnPUTtrue PATCH /stores-replacing/:id', 'prototype.patchAttributes(data:object:storeWithReplaceOnPUTtrue):storeWithReplaceOnPUTtrue PATCH /stores-replacing/:id',
]; ];
expect(methods).to.include.members(expectedMethods); expect(methods).to.include.members(expectedMethods);
}); });
}); });
function formatReturns(m) { function formatReturns(m) {
@ -332,12 +332,12 @@ function getFormattedMethodsExcludingRelations(methods) {
return methods.filter(function(m) { return methods.filter(function(m) {
return m.name.indexOf('__') === -1; return m.name.indexOf('__') === -1;
}) })
.map(function(m) { .map(function(m) {
return formatMethod(m); return formatMethod(m);
}) })
.reduce(function(p, c) { .reduce(function(p, c) {
return p.concat(c); return p.concat(c);
}); });
} }
function getCreateMethod(methods) { function getCreateMethod(methods) {
@ -350,22 +350,22 @@ function getFormattedScopeMethods(methods) {
return methods.filter(function(m) { return methods.filter(function(m) {
return m.name.indexOf('__') === 0; return m.name.indexOf('__') === 0;
}) })
.map(function(m) { .map(function(m) {
return formatMethod(m); return formatMethod(m);
}) })
.reduce(function(p, c) { .reduce(function(p, c) {
return p.concat(c); return p.concat(c);
}); });
} }
function getFormattedPrototypeMethods(methods) { function getFormattedPrototypeMethods(methods) {
return methods.filter(function(m) { return methods.filter(function(m) {
return m.name.indexOf('prototype.__') === 0; return m.name.indexOf('prototype.__') === 0;
}) })
.map(function(m) { .map(function(m) {
return formatMethod(m); return formatMethod(m);
}) })
.reduce(function(p, c) { .reduce(function(p, c) {
return p.concat(c); return p.concat(c);
}); });
} }

View File

@ -72,8 +72,8 @@ describe('Replication over REST', function() {
it('allows PETER to WRITE', function(done) { it('allows PETER to WRITE', function(done) {
createCar() createCar()
.set('Authorization', peterToken) .set('Authorization', peterToken)
.expect(200, done); .expect(200, done);
}); });
function listCars() { function listCars() {
@ -597,7 +597,7 @@ describe('Replication over REST', function() {
function(next) { function(next) {
ServerCar.create( ServerCar.create(
[ [
{id: 'Ford-Mustang', maker: 'Ford', model: 'Mustang'}, {id: 'Ford-Mustang', maker: 'Ford', model: 'Mustang'},
{id: 'Audi-R8', maker: 'Audi', model: 'R8'}, {id: 'Audi-R8', maker: 'Audi', model: 'R8'},
], ],
function(err, cars) { function(err, cars) {

View File

@ -170,66 +170,66 @@ describe('Replication / Change APIs', function() {
}); });
it('rectifyOnDelete for Delete should call rectifyChange instead of rectifyAllChanges', it('rectifyOnDelete for Delete should call rectifyChange instead of rectifyAllChanges',
function(done) { function(done) {
var calls = mockTargetModelRectify(); var calls = mockTargetModelRectify();
async.waterfall([ async.waterfall([
function(callback) { function(callback) {
SourceModel.destroyAll({name: 'John'}, callback); SourceModel.destroyAll({name: 'John'}, callback);
}, },
function(data, callback) { function(data, callback) {
SourceModel.replicate(TargetModel, callback); SourceModel.replicate(TargetModel, callback);
// replicate should call `rectifyOnSave` and then `rectifyChange` not `rectifyAllChanges` through `after save` operation // replicate should call `rectifyOnSave` and then `rectifyChange` not `rectifyAllChanges` through `after save` operation
}, },
], function(err, results) { ], function(err, results) {
if (err) return done(err); if (err) return done(err);
expect(calls).to.eql(['rectifyChange']); expect(calls).to.eql(['rectifyChange']);
done(); done();
});
}); });
});
it('rectifyOnSave for Update should call rectifyChange instead of rectifyAllChanges', it('rectifyOnSave for Update should call rectifyChange instead of rectifyAllChanges',
function(done) { function(done) {
var calls = mockTargetModelRectify(); var calls = mockTargetModelRectify();
var newData = {'name': 'Janie'}; var newData = {'name': 'Janie'};
async.waterfall([ async.waterfall([
function(callback) { function(callback) {
SourceModel.update({name: 'Jane'}, newData, callback); SourceModel.update({name: 'Jane'}, newData, callback);
}, },
function(data, callback) { function(data, callback) {
SourceModel.replicate(TargetModel, callback); SourceModel.replicate(TargetModel, callback);
// replicate should call `rectifyOnSave` and then `rectifyChange` not `rectifyAllChanges` through `after save` operation // replicate should call `rectifyOnSave` and then `rectifyChange` not `rectifyAllChanges` through `after save` operation
}, },
], function(err, result) { ], function(err, result) {
if (err) return done(err); if (err) return done(err);
expect(calls).to.eql(['rectifyChange']); expect(calls).to.eql(['rectifyChange']);
done(); done();
});
}); });
});
it('rectifyOnSave for Create should call rectifyChange instead of rectifyAllChanges', it('rectifyOnSave for Create should call rectifyChange instead of rectifyAllChanges',
function(done) { function(done) {
var calls = mockTargetModelRectify(); var calls = mockTargetModelRectify();
var newData = [{name: 'Janie', surname: 'Doe'}]; var newData = [{name: 'Janie', surname: 'Doe'}];
async.waterfall([ async.waterfall([
function(callback) { function(callback) {
SourceModel.create(newData, callback); SourceModel.create(newData, callback);
}, },
function(data, callback) { function(data, callback) {
SourceModel.replicate(TargetModel, callback); SourceModel.replicate(TargetModel, callback);
// replicate should call `rectifyOnSave` and then `rectifyChange` not `rectifyAllChanges` through `after save` operation // replicate should call `rectifyOnSave` and then `rectifyChange` not `rectifyAllChanges` through `after save` operation
}, },
], function(err, result) { ], function(err, result) {
if (err) return done(err); if (err) return done(err);
expect(calls).to.eql(['rectifyChange']); expect(calls).to.eql(['rectifyChange']);
done(); done();
});
}); });
});
function mockSourceModelRectify() { function mockSourceModelRectify() {
var calls = []; var calls = [];
@ -305,12 +305,12 @@ describe('Replication / Change APIs', function() {
if (err) return done(err); if (err) return done(err);
test.SourceModel.replicate(test.startingCheckpoint, test.TargetModel, test.SourceModel.replicate(test.startingCheckpoint, test.TargetModel,
options, function(err, conflicts) { options, function(err, conflicts) {
if (err) return done(err); if (err) return done(err);
assertTargetModelEqualsSourceModel(conflicts, test.SourceModel, assertTargetModelEqualsSourceModel(conflicts, test.SourceModel,
test.TargetModel, done); test.TargetModel, done);
}); });
}); });
}); });
@ -322,7 +322,7 @@ describe('Replication / Change APIs', function() {
if (err) return done(err); if (err) return done(err);
test.SourceModel.replicate(test.startingCheckpoint, test.TargetModel, test.SourceModel.replicate(test.startingCheckpoint, test.TargetModel,
options) options)
.then(function(conflicts) { .then(function(conflicts) {
assertTargetModelEqualsSourceModel(conflicts, test.SourceModel, assertTargetModelEqualsSourceModel(conflicts, test.SourceModel,
test.TargetModel, done); test.TargetModel, done);
@ -1280,7 +1280,7 @@ describe('Replication / Change APIs', function() {
var ClientA, Server, ClientB; var ClientA, Server, ClientB;
beforeEach(function() { beforeEach(function() {
ClientA = SourceModel; ClientA = SourceModel;
Server = TargetModel; Server = TargetModel;
ClientB = AnotherModel; ClientB = AnotherModel;
@ -1625,13 +1625,13 @@ describe('Replication / Change APIs', function() {
updates[0].change = data; updates[0].change = data;
OptionsSourceModel.bulkUpdate(updates, options, callback); OptionsSourceModel.bulkUpdate(updates, options, callback);
}], }],
function(err, result) { function(err, result) {
if (err) return done(err); if (err) return done(err);
expect(syncPropertyExists).to.eql(true); expect(syncPropertyExists).to.eql(true);
done(); done();
} }
); );
}); });
}); });
@ -1875,7 +1875,7 @@ describe('Replication / Change APIs', function() {
} }
function assertTargetModelEqualsSourceModel(conflicts, sourceModel, function assertTargetModelEqualsSourceModel(conflicts, sourceModel,
targetModel, done) { targetModel, done) {
var sourceData, targetData; var sourceData, targetData;
assert(conflicts.length === 0); assert(conflicts.length === 0);

View File

@ -239,12 +239,12 @@ describe('loopback.rest', function() {
describe('with specific definitions in model-config.json', function() { describe('with specific definitions in model-config.json', function() {
it('should not be exposed when the definition value is false', it('should not be exposed when the definition value is false',
function(done) { function(done) {
var app = require(getFixturePath('model-config-defined-false')); var app = require(getFixturePath('model-config-defined-false'));
request(app) request(app)
.get('/todos') .get('/todos')
.expect(404, done); .expect(404, done);
}); });
it('should be exposed when the definition value is true', function(done) { it('should be exposed when the definition value is true', function(done) {
var app = require(getFixturePath('model-config-defined-true')); var app = require(getFixturePath('model-config-defined-true'));
@ -256,12 +256,12 @@ describe('loopback.rest', function() {
describe('with default definitions in model-config.json', function() { describe('with default definitions in model-config.json', function() {
it('should not be exposed when the definition value is false', it('should not be exposed when the definition value is false',
function(done) { function(done) {
var app = require(getFixturePath('model-config-default-false')); var app = require(getFixturePath('model-config-default-false'));
request(app) request(app)
.get('/todos') .get('/todos')
.expect(404, done); .expect(404, done);
}); });
it('should be exposed when the definition value is true', function(done) { it('should be exposed when the definition value is true', function(done) {
var app = require(getFixturePath('model-config-default-true')); var app = require(getFixturePath('model-config-default-true'));
@ -286,30 +286,30 @@ describe('loopback.rest', function() {
describe('with specific definitions in config.json', function() { describe('with specific definitions in config.json', function() {
it('should not be exposed when the definition value is false', it('should not be exposed when the definition value is false',
function(done) { function(done) {
var app = require(getFixturePath('config-defined-false')); var app = require(getFixturePath('config-defined-false'));
request(app) request(app)
.get('/todos') .get('/todos')
.expect(404, done); .expect(404, done);
}); });
it('should be exposed when the definition value is true', it('should be exposed when the definition value is true',
function(done) { function(done) {
var app = require(getFixturePath('config-defined-true')); var app = require(getFixturePath('config-defined-true'));
request(app) request(app)
.get('/todos') .get('/todos')
.expect(200, done); .expect(200, done);
}); });
}); });
describe('with default definitions in config.json', function() { describe('with default definitions in config.json', function() {
it('should not be exposed when the definition value is false', it('should not be exposed when the definition value is false',
function(done) { function(done) {
var app = require(getFixturePath('config-default-false')); var app = require(getFixturePath('config-default-false'));
request(app) request(app)
.get('/todos') .get('/todos')
.expect(404, done); .expect(404, done);
}); });
it('should be exposed when the definition value is true', function(done) { it('should be exposed when the definition value is true', function(done) {
var app = require(getFixturePath('config-default-true')); var app = require(getFixturePath('config-default-true'));
@ -337,21 +337,21 @@ describe('loopback.rest', function() {
// a side effect since tests share the same loopback instance. As a // a side effect since tests share the same loopback instance. As a
// consequence, this causes the tests in user.integration to fail. // consequence, this causes the tests in user.integration to fail.
describe.skip('with definitions in both config.json and model-config.json', describe.skip('with definitions in both config.json and model-config.json',
function() { function() {
it('should prioritize the settings in model-config.json', function(done) { it('should prioritize the settings in model-config.json', function(done) {
var app = require(getFixturePath('both-configs-set')); var app = require(getFixturePath('both-configs-set'));
request(app) request(app)
.del('/todos') .del('/todos')
.expect(404, done); .expect(404, done);
});
it('should fall back to config.json settings if setting is not found in' +
'model-config.json', function(done) {
var app = require(getFixturePath('both-configs-set'));
request(app)
.get('/todos')
.expect(404, done);
});
}); });
it('should fall back to config.json settings if setting is not found in' +
'model-config.json', function(done) {
var app = require(getFixturePath('both-configs-set'));
request(app)
.get('/todos')
.expect(404, done);
});
});
}); });
}); });

View File

@ -31,11 +31,11 @@ describe('role-mapping model', function() {
models.Application.create({name: 'anApp'}), models.Application.create({name: 'anApp'}),
models.Role.create({name: 'aRole'}), models.Role.create({name: 'aRole'}),
]) ])
.spread(function(u, a, r) { .spread(function(u, a, r) {
oneUser = u; oneUser = u;
anApp = a; anApp = a;
aRole = r; aRole = r;
}); });
// helper // helper
function setupModel(modelName) { function setupModel(modelName) {

View File

@ -111,36 +111,36 @@ describe('role model', function() {
Role.create({name: 'userRole'}, function(err, role) { Role.create({name: 'userRole'}, function(err, role) {
if (err) return done(err); if (err) return done(err);
role.principals.create({principalType: RoleMapping.USER, principalId: user.id}, role.principals.create({principalType: RoleMapping.USER, principalId: user.id},
function(err, p) { function(err, p) {
if (err) return done(err); if (err) return done(err);
async.parallel([ async.parallel([
function(next) { function(next) {
Role.find(function(err, roles) { Role.find(function(err, roles) {
if (err) return next(err); if (err) return next(err);
assert.equal(roles.length, 1); assert.equal(roles.length, 1);
assert.equal(roles[0].name, 'userRole'); assert.equal(roles[0].name, 'userRole');
next(); next();
}); });
}, },
function(next) { function(next) {
role.principals(function(err, principals) { role.principals(function(err, principals) {
if (err) return next(err); if (err) return next(err);
assert.equal(principals.length, 1); assert.equal(principals.length, 1);
assert.equal(principals[0].principalType, RoleMapping.USER); assert.equal(principals[0].principalType, RoleMapping.USER);
assert.equal(principals[0].principalId, user.id); assert.equal(principals[0].principalId, user.id);
next(); next();
}); });
}, },
function(next) { function(next) {
role.users(function(err, users) { role.users(function(err, users) {
if (err) return next(err); if (err) return next(err);
assert.equal(users.length, 1); assert.equal(users.length, 1);
assert.equal(users[0].id, user.id); assert.equal(users[0].id, user.id);
next(); next();
}); });
}, },
], done); ], done);
}); });
}); });
}); });
}); });
@ -168,38 +168,38 @@ describe('role model', function() {
if (err) return done(err); if (err) return done(err);
assert(role.id); assert(role.id);
role.principals.create({principalType: RoleMapping.USER, principalId: user.id}, role.principals.create({principalType: RoleMapping.USER, principalId: user.id},
function(err, p) { function(err, p) {
if (err) return done(err); if (err) return done(err);
assert(p.id); assert(p.id);
assert.equal(p.roleId, role.id); assert.equal(p.roleId, role.id);
async.parallel([ async.parallel([
function(next) { function(next) {
Role.find(function(err, roles) { Role.find(function(err, roles) {
if (err) return next(err); if (err) return next(err);
assert.equal(roles.length, 1); assert.equal(roles.length, 1);
assert.equal(roles[0].name, 'userRole'); assert.equal(roles[0].name, 'userRole');
next();
});
},
function(next) {
role.principals(function(err, principals) {
if (err) return next(err);
assert.equal(principals.length, 1);
assert.equal(principals[0].principalType, RoleMapping.USER);
assert.equal(principals[0].principalId, user.id);
next();
});
},
function(next) {
role.users(function(err, users) {
if (err) return next(err);
assert.equal(users.length, 1);
assert.equal(users[0].id, user.id);
});
next(); next();
}); },
}, ], done);
function(next) { });
role.principals(function(err, principals) {
if (err) return next(err);
assert.equal(principals.length, 1);
assert.equal(principals[0].principalType, RoleMapping.USER);
assert.equal(principals[0].principalId, user.id);
next();
});
},
function(next) {
role.users(function(err, users) {
if (err) return next(err);
assert.equal(users.length, 1);
assert.equal(users[0].id, user.id);
});
next();
},
], done);
});
}); });
}); });
}); });
@ -210,106 +210,106 @@ describe('role model', function() {
Role.create({name: 'userRole'}, function(err, role) { Role.create({name: 'userRole'}, function(err, role) {
if (err) return done(err); if (err) return done(err);
role.principals.create({principalType: RoleMapping.USER, principalId: user.id}, role.principals.create({principalType: RoleMapping.USER, principalId: user.id},
function(err, p) { function(err, p) {
if (err) return done(err); if (err) return done(err);
async.series([ async.series([
function(next) { function(next) {
Role.isInRole( Role.isInRole(
'userRole', 'userRole',
{principalType: RoleMapping.USER, principalId: user.id}, {principalType: RoleMapping.USER, principalId: user.id},
function(err, inRole) { function(err, inRole) {
if (err) return next(err); if (err) return next(err);
// NOTE(bajtos) Apparently isRole is not a boolean, // NOTE(bajtos) Apparently isRole is not a boolean,
// but the matchin role object instead // but the matchin role object instead
assert(!!inRole); assert(!!inRole);
next(); next();
}); });
}, },
function(next) { function(next) {
Role.isInRole( Role.isInRole(
'userRole', 'userRole',
{principalType: RoleMapping.APP, principalId: user.id}, {principalType: RoleMapping.APP, principalId: user.id},
function(err, inRole) { function(err, inRole) {
if (err) return next(err); if (err) return next(err);
assert(!inRole); assert(!inRole);
next(); next();
}); });
}, },
function(next) { function(next) {
Role.isInRole( Role.isInRole(
'userRole', 'userRole',
{principalType: RoleMapping.USER, principalId: 100}, {principalType: RoleMapping.USER, principalId: 100},
function(err, inRole) { function(err, inRole) {
if (err) return next(err); if (err) return next(err);
assert(!inRole); assert(!inRole);
next(); next();
}); });
}, },
function(next) { function(next) {
Role.getRoles( Role.getRoles(
{principalType: RoleMapping.USER, principalId: user.id}, {principalType: RoleMapping.USER, principalId: user.id},
function(err, roles) { function(err, roles) {
if (err) return next(err); if (err) return next(err);
expect(roles).to.eql([ expect(roles).to.eql([
Role.AUTHENTICATED, Role.AUTHENTICATED,
Role.EVERYONE, Role.EVERYONE,
role.id, role.id,
]); ]);
next(); next();
}); });
}, },
function(next) { function(next) {
Role.getRoles( Role.getRoles(
{principalType: RoleMapping.USER, principalId: user.id}, {principalType: RoleMapping.USER, principalId: user.id},
{returnOnlyRoleNames: true}, {returnOnlyRoleNames: true},
function(err, roles) { function(err, roles) {
if (err) return next(err); if (err) return next(err);
expect(roles).to.eql([ expect(roles).to.eql([
Role.AUTHENTICATED, Role.AUTHENTICATED,
Role.EVERYONE, Role.EVERYONE,
role.name, role.name,
]); ]);
next(); next();
}); });
}, },
function(next) { function(next) {
Role.getRoles( Role.getRoles(
{principalType: RoleMapping.APP, principalId: user.id}, {principalType: RoleMapping.APP, principalId: user.id},
function(err, roles) { function(err, roles) {
if (err) return next(err); if (err) return next(err);
expect(roles).to.eql([ expect(roles).to.eql([
Role.AUTHENTICATED, Role.AUTHENTICATED,
Role.EVERYONE, Role.EVERYONE,
]); ]);
next(); next();
}); });
}, },
function(next) { function(next) {
Role.getRoles( Role.getRoles(
{principalType: RoleMapping.USER, principalId: 100}, {principalType: RoleMapping.USER, principalId: 100},
function(err, roles) { function(err, roles) {
if (err) return next(err); if (err) return next(err);
expect(roles).to.eql([ expect(roles).to.eql([
Role.AUTHENTICATED, Role.AUTHENTICATED,
Role.EVERYONE, Role.EVERYONE,
]); ]);
next(); next();
}); });
}, },
function(next) { function(next) {
Role.getRoles( Role.getRoles(
{principalType: RoleMapping.USER, principalId: null}, {principalType: RoleMapping.USER, principalId: null},
function(err, roles) { function(err, roles) {
if (err) return next(err); if (err) return next(err);
expect(roles).to.eql([ expect(roles).to.eql([
Role.UNAUTHENTICATED, Role.UNAUTHENTICATED,
Role.EVERYONE, Role.EVERYONE,
]); ]);
next(); next();
}); });
}, },
], done); ], done);
}); });
}); });
}); });
}); });
@ -380,44 +380,44 @@ describe('role model', function() {
Role.create({name: 'userRole'}, function(err, role) { Role.create({name: 'userRole'}, function(err, role) {
if (err) return done(err); if (err) return done(err);
role.principals.create({principalType: RoleMapping.USER, principalId: user.id}, role.principals.create({principalType: RoleMapping.USER, principalId: user.id},
function(err, p) { function(err, p) {
if (err) return done(err); if (err) return done(err);
async.series([ async.series([
function(next) { function(next) {
Role.isInRole( Role.isInRole(
'userRole', 'userRole',
{principalType: RoleMapping.USER, principalId: user.id}, {principalType: RoleMapping.USER, principalId: user.id},
function(err, inRole) { function(err, inRole) {
if (err) return next(err); if (err) return next(err);
assert(!!inRole); assert(!!inRole);
next(); next();
}); });
}, },
function(next) { function(next) {
Role.isInRole( Role.isInRole(
'userRole', 'userRole',
{principalType: RoleMapping.APP, principalId: user.id}, {principalType: RoleMapping.APP, principalId: user.id},
function(err, inRole) { function(err, inRole) {
if (err) return next(err); if (err) return next(err);
assert(!inRole); assert(!inRole);
next(); next();
}); });
}, },
function(next) { function(next) {
Role.getRoles( Role.getRoles(
{principalType: RoleMapping.USER, principalId: user.id}, {principalType: RoleMapping.USER, principalId: user.id},
function(err, roles) { function(err, roles) {
if (err) return next(err); if (err) return next(err);
expect(roles).to.eql([ expect(roles).to.eql([
Role.AUTHENTICATED, Role.AUTHENTICATED,
Role.EVERYONE, Role.EVERYONE,
role.id, role.id,
]); ]);
next(); next();
}); });
}, },
], done); ], done);
}); });
}); });
}); });
}); });
@ -566,19 +566,19 @@ describe('role model', function() {
app.model(Album, {dataSource: 'db'}); app.model(Album, {dataSource: 'db'});
return User.create({email: 'test@example.com', password: 'pass'}) return User.create({email: 'test@example.com', password: 'pass'})
.then(u => { .then(u => {
user = u; user = u;
return Album.create({name: 'Album 1', userId: user.id}); return Album.create({name: 'Album 1', userId: user.id});
}) })
.then(album => { .then(album => {
return Role.isInRole(Role.OWNER, { return Role.isInRole(Role.OWNER, {
principalType: ACL.USER, principalType: ACL.USER,
principalId: user.id, principalId: user.id,
model: Album, model: Album,
id: album.id, id: album.id,
}); });
}) })
.then(isInRole => expect(isInRole).to.be.true()); .then(isInRole => expect(isInRole).to.be.true());
}); });
it('resolves the owner via property "owner"', function() { it('resolves the owner via property "owner"', function() {
@ -590,9 +590,73 @@ describe('role model', function() {
app.model(Album, {dataSource: 'db'}); app.model(Album, {dataSource: 'db'});
return User.create({email: 'test@example.com', password: 'pass'}) return User.create({email: 'test@example.com', password: 'pass'})
.then(u => {
user = u;
return Album.create({name: 'Album 1', owner: user.id});
})
.then(album => {
return Role.isInRole(Role.OWNER, {
principalType: ACL.USER,
principalId: user.id,
model: Album,
id: album.id,
});
})
.then(isInRole => expect(isInRole).to.be.true());
});
it('resolves the owner via a belongsTo relation', function() {
// passing no options will result calling
// the legacy $owner role resolver behavior
var Message = givenModelWithSenderReceiverRelations('ModelWithNoOptions');
return givenUsers()
.then(() => {
var messages = [
{content: 'firstMessage', senderId: sender.id},
{content: 'secondMessage', receiverId: receiver.id},
{content: 'thirdMessage'},
];
return Promise.map(messages, msg => {
return Message.create(msg);
});
})
.then(messages => {
return Promise.all([
isOwnerForMessage(sender, messages[0]),
isOwnerForMessage(receiver, messages[1]),
isOwnerForMessage(receiver, messages[2]),
]);
})
.then(result => {
expect(result).to.eql([
{user: 'sender', msg: 'firstMessage', isOwner: true},
{user: 'receiver', msg: 'secondMessage', isOwner: false},
{user: 'receiver', msg: 'thirdMessage', isOwner: false},
]);
});
});
});
it('resolves as false without belongsTo relation', function() {
var user;
var Album = app.registry.createModel(
'Album',
{
name: String,
userId: Number,
owner: Number,
},
// passing {ownerRelations: true} will enable the new $owner role resolver
// and hence resolve false when no belongsTo relation is defined
{ownerRelations: true}
);
app.model(Album, {dataSource: 'db'});
return User.create({email: 'test@example.com', password: 'pass'})
.then(u => { .then(u => {
user = u; user = u;
return Album.create({name: 'Album 1', owner: user.id}); return Album.create({name: 'Album 1', userId: user.id, owner: user.id});
}) })
.then(album => { .then(album => {
return Role.isInRole(Role.OWNER, { return Role.isInRole(Role.OWNER, {
@ -602,15 +666,18 @@ describe('role model', function() {
id: album.id, id: album.id,
}); });
}) })
.then(isInRole => expect(isInRole).to.be.true()); .then(isInRole => expect(isInRole).to.be.false());
}); });
it('resolves the owner via a belongsTo relation', function() { it('resolves the owner using the corrent belongsTo relation', function() {
// passing no options will result calling // passing {ownerRelations: true} will enable the new $owner role resolver
// the legacy $owner role resolver behavior // with any belongsTo relation allowing to resolve truthy
var Message = givenModelWithSenderReceiverRelations('ModelWithNoOptions'); var Message = givenModelWithSenderReceiverRelations(
'ModelWithAllRelations',
{ownerRelations: true}
);
return givenUsers() return givenUsers()
.then(() => { .then(() => {
var messages = [ var messages = [
{content: 'firstMessage', senderId: sender.id}, {content: 'firstMessage', senderId: sender.id},
@ -631,124 +698,57 @@ describe('role model', function() {
.then(result => { .then(result => {
expect(result).to.eql([ expect(result).to.eql([
{user: 'sender', msg: 'firstMessage', isOwner: true}, {user: 'sender', msg: 'firstMessage', isOwner: true},
{user: 'receiver', msg: 'secondMessage', isOwner: false}, {user: 'receiver', msg: 'secondMessage', isOwner: true},
{user: 'receiver', msg: 'thirdMessage', isOwner: false}, {user: 'receiver', msg: 'thirdMessage', isOwner: false},
]); ]);
}); });
});
});
it('resolves as false without belongsTo relation', function() {
var user;
var Album = app.registry.createModel(
'Album',
{
name: String,
userId: Number,
owner: Number,
},
// passing {ownerRelations: true} will enable the new $owner role resolver
// and hence resolve false when no belongsTo relation is defined
{ownerRelations: true}
);
app.model(Album, {dataSource: 'db'});
return User.create({email: 'test@example.com', password: 'pass'})
.then(u => {
user = u;
return Album.create({name: 'Album 1', userId: user.id, owner: user.id});
})
.then(album => {
return Role.isInRole(Role.OWNER, {
principalType: ACL.USER,
principalId: user.id,
model: Album,
id: album.id,
});
})
.then(isInRole => expect(isInRole).to.be.false());
});
it('resolves the owner using the corrent belongsTo relation', function() {
// passing {ownerRelations: true} will enable the new $owner role resolver
// with any belongsTo relation allowing to resolve truthy
var Message = givenModelWithSenderReceiverRelations(
'ModelWithAllRelations',
{ownerRelations: true}
);
return givenUsers()
.then(() => {
var messages = [
{content: 'firstMessage', senderId: sender.id},
{content: 'secondMessage', receiverId: receiver.id},
{content: 'thirdMessage'},
];
return Promise.map(messages, msg => {
return Message.create(msg);
});
})
.then(messages => {
return Promise.all([
isOwnerForMessage(sender, messages[0]),
isOwnerForMessage(receiver, messages[1]),
isOwnerForMessage(receiver, messages[2]),
]);
})
.then(result => {
expect(result).to.eql([
{user: 'sender', msg: 'firstMessage', isOwner: true},
{user: 'receiver', msg: 'secondMessage', isOwner: true},
{user: 'receiver', msg: 'thirdMessage', isOwner: false},
]);
});
}); });
it('allows fine-grained control of which relations grant ownership', it('allows fine-grained control of which relations grant ownership',
function() { function() {
// passing {ownerRelations: true} will enable the new $owner role resolver // passing {ownerRelations: true} will enable the new $owner role resolver
// with a specified list of belongsTo relations allowing to resolve truthy // with a specified list of belongsTo relations allowing to resolve truthy
var Message = givenModelWithSenderReceiverRelations( var Message = givenModelWithSenderReceiverRelations(
'ModelWithCoercedRelations', 'ModelWithCoercedRelations',
{ownerRelations: ['receiver']} {ownerRelations: ['receiver']}
); );
return givenUsers() return givenUsers()
.then(() => { .then(() => {
var messages = [ var messages = [
{content: 'firstMessage', senderId: sender.id}, {content: 'firstMessage', senderId: sender.id},
{content: 'secondMessage', receiverId: receiver.id}, {content: 'secondMessage', receiverId: receiver.id},
{content: 'thirdMessage'}, {content: 'thirdMessage'},
]; ];
return Promise.map(messages, msg => { return Promise.map(messages, msg => {
return Message.create(msg); return Message.create(msg);
}); });
}) })
.then(messages => { .then(messages => {
return Promise.all([ return Promise.all([
isOwnerForMessage(sender, messages[0]), isOwnerForMessage(sender, messages[0]),
isOwnerForMessage(receiver, messages[1]), isOwnerForMessage(receiver, messages[1]),
isOwnerForMessage(receiver, messages[2]), isOwnerForMessage(receiver, messages[2]),
]); ]);
}) })
.then(result => { .then(result => {
expect(result).to.eql([ expect(result).to.eql([
{user: 'sender', msg: 'firstMessage', isOwner: false}, {user: 'sender', msg: 'firstMessage', isOwner: false},
{user: 'receiver', msg: 'secondMessage', isOwner: true}, {user: 'receiver', msg: 'secondMessage', isOwner: true},
{user: 'receiver', msg: 'thirdMessage', isOwner: false}, {user: 'receiver', msg: 'thirdMessage', isOwner: false},
]); ]);
});
}); });
});
// helpers // helpers
function givenUsers() { function givenUsers() {
return Promise.map(users, user => { return Promise.map(users, user => {
return User.create(user); return User.create(user);
}) })
.then(users => { .then(users => {
sender = users[0]; sender = users[0];
receiver = users[1]; receiver = users[1];
}); });
} }
function isOwnerForMessage(user, msg) { function isOwnerForMessage(user, msg) {
@ -977,16 +977,6 @@ describe('role model', function() {
done(); done();
}); });
}); });
it('should report isMappedToRole by app.name', function(done) {
ACL.isMappedToRole(ACL.APP, app.name, 'admin', function(err, flag) {
if (err) return done(err);
expect(flag).to.eql(true);
done();
});
});
}); });
describe('listByPrincipalType', function() { describe('listByPrincipalType', function() {
@ -1019,18 +1009,18 @@ describe('role model', function() {
Role.create({name: uniqueRoleName}, function(err, role) { Role.create({name: uniqueRoleName}, function(err, role) {
if (err) return done(err); if (err) return done(err);
role.principals.create({principalType: principalType, principalId: model.id}, role.principals.create({principalType: principalType, principalId: model.id},
function(err, p) { function(err, p) {
if (err) return done(err);
var pluralName = Model.pluralModelName.toLowerCase();
role[pluralName](function(err, models) {
if (err) return done(err); if (err) return done(err);
assert.equal(models.length, 1); var pluralName = Model.pluralModelName.toLowerCase();
role[pluralName](function(err, models) {
if (err) return done(err);
assert.equal(models.length, 1);
if (++runs === mappings.length) { if (++runs === mappings.length) {
done(); done();
} }
});
}); });
});
}); });
}); });
}); });
@ -1052,13 +1042,13 @@ describe('role model', function() {
// Create models // Create models
function(next) { function(next) {
Model.create([ Model.create([
{name: 'test', email: 'x@y.com', password: 'foobar'}, {name: 'test', email: 'x@y.com', password: 'foobar'},
{name: 'test2', email: 'f@v.com', password: 'bargoo'}, {name: 'test2', email: 'f@v.com', password: 'bargoo'},
{name: 'test3', email: 'd@t.com', password: 'bluegoo'}], {name: 'test3', email: 'd@t.com', password: 'bluegoo'}],
function(err, models) { function(err, models) {
if (err) return next(err); if (err) return next(err);
next(null, models); next(null, models);
}); });
}, },
// Create Roles // Create Roles
@ -1066,12 +1056,12 @@ describe('role model', function() {
var uniqueRoleName = 'testRoleFor' + principalType; var uniqueRoleName = 'testRoleFor' + principalType;
var otherUniqueRoleName = 'otherTestRoleFor' + principalType; var otherUniqueRoleName = 'otherTestRoleFor' + principalType;
Role.create([ Role.create([
{name: uniqueRoleName}, {name: uniqueRoleName},
{name: otherUniqueRoleName}], {name: otherUniqueRoleName}],
function(err, roles) { function(err, roles) {
if (err) return next(err); if (err) return next(err);
next(null, models, roles); next(null, models, roles);
}); });
}, },
// Create principles // Create principles
@ -1120,19 +1110,19 @@ describe('role model', function() {
Role.create({name: 'userRole'}, function(err, role) { Role.create({name: 'userRole'}, function(err, role) {
if (err) return done(err); if (err) return done(err);
role.principals.create({principalType: RoleMapping.USER, principalId: user.id}, role.principals.create({principalType: RoleMapping.USER, principalId: user.id},
function(err, p) { function(err, p) {
if (err) return done(err);
var query = {fields: ['id', 'name']};
sandbox.spy(User, 'find');
role.users(query, function(err, users) {
if (err) return done(err); if (err) return done(err);
assert.equal(users.length, 1); var query = {fields: ['id', 'name']};
assert.equal(users[0].id, user.id); sandbox.spy(User, 'find');
assert(User.find.calledWith(query)); role.users(query, function(err, users) {
if (err) return done(err);
assert.equal(users.length, 1);
assert.equal(users[0].id, user.id);
assert(User.find.calledWith(query));
done(); done();
});
}); });
});
}); });
}); });
}); });

View File

@ -207,7 +207,7 @@ describe('User.password', () => {
User.resetPassword({email: credentials.email}), User.resetPassword({email: credentials.email}),
waitForEvent(User, 'resetPasswordRequest'), waitForEvent(User, 'resetPasswordRequest'),
]) ])
.spread((reset, info) => resetToken = info.accessToken); .spread((reset, info) => resetToken = info.accessToken);
} }
function changeName(token) { function changeName(token) {

View File

@ -313,6 +313,6 @@ describe('users - integration', function() {
User.resetPassword({email: email}), User.resetPassword({email: email}),
waitForEvent(app.models.User, 'resetPasswordRequest'), waitForEvent(app.models.User, 'resetPasswordRequest'),
]) ])
.spread((reset, info) => info); .spread((reset, info) => info);
} }
}); });

View File

@ -216,7 +216,7 @@ describe('User', function() {
it('Requires a unique username', function(done) { it('Requires a unique username', function(done) {
User.create({email: 'a@b.com', username: 'abc', password: 'foobar'}, function() { User.create({email: 'a@b.com', username: 'abc', password: 'foobar'}, function() {
User.create({email: 'b@b.com', username: 'abc', password: 'batbaz'}, function(err) { User.create({email: 'b@b.com', username: 'abc', password: 'batbaz'}, function(err) {
assert(err, 'should error because the username is not unique!'); assert(err, 'should error because the username is not unique!');
done(); done();
@ -270,7 +270,7 @@ describe('User', function() {
}); });
}, },
function(next) { function(next) {
User.findById(usersId, function(err, userFound) { User.findById(usersId, function(err, userFound) {
if (err) return next(err); if (err) return next(err);
expect(userFound).to.equal(null); expect(userFound).to.equal(null);
AccessToken.find({where: {userId: usersId}}, function(err, tokens) { AccessToken.find({where: {userId: usersId}}, function(err, tokens) {
@ -322,7 +322,7 @@ describe('User', function() {
}); });
}, },
function(next) { function(next) {
User.find({where: {name: 'myname'}}, function(err, userFound) { User.find({where: {name: 'myname'}}, function(err, userFound) {
if (err) return next(err); if (err) return next(err);
expect(userFound.length).to.equal(0); expect(userFound.length).to.equal(0);
AccessToken.find({where: {userId: {inq: userIds}}}, function(err, tokens) { AccessToken.find({where: {userId: {inq: userIds}}}, function(err, tokens) {
@ -471,7 +471,7 @@ describe('User', function() {
it('accepts passwords that are exactly 72 characters long', function(done) { it('accepts passwords that are exactly 72 characters long', function(done) {
User.create({email: 'b@c.com', password: pass72Char}, function(err, user) { User.create({email: 'b@c.com', password: pass72Char}, function(err, user) {
if (err) return done(err); if (err) return done(err);
User.findById(user.pk, function(err, userFound) { User.findById(user.pk, function(err, userFound) {
if (err) return done(err); if (err) return done(err);
assert(userFound); assert(userFound);
done(); done();
@ -867,21 +867,21 @@ describe('User', function() {
}); });
it('allows login with password too long but created in old LB version', it('allows login with password too long but created in old LB version',
function(done) { function(done) {
var bcrypt = require('bcryptjs'); var bcrypt = require('bcryptjs');
var longPassword = new Array(80).join('a'); var longPassword = new Array(80).join('a');
var oldHash = bcrypt.hashSync(longPassword, bcrypt.genSaltSync(1)); var oldHash = bcrypt.hashSync(longPassword, bcrypt.genSaltSync(1));
User.create({email: 'b@c.com', password: oldHash}, function(err) { User.create({email: 'b@c.com', password: oldHash}, function(err) {
if (err) return done(err);
User.login({email: 'b@c.com', password: longPassword}, function(err, accessToken) {
if (err) return done(err); if (err) return done(err);
assert(accessToken.id); User.login({email: 'b@c.com', password: longPassword}, function(err, accessToken) {
// we are logged in, the test passed if (err) return done(err);
done(); assert(accessToken.id);
// we are logged in, the test passed
done();
});
}); });
}); });
});
}); });
function assertGoodToken(accessToken, user) { function assertGoodToken(accessToken, user) {
@ -919,21 +919,21 @@ describe('User', function() {
}); });
it('requires valid and complete credentials for email verification - promise variant', it('requires valid and complete credentials for email verification - promise variant',
function(done) { function(done) {
User.login({email: validCredentialsEmail}) User.login({email: validCredentialsEmail})
.then(function(accessToken) { .then(function(accessToken) {
done(); done();
}) })
.catch(function(err) { .catch(function(err) {
// strongloop/loopback#931 // strongloop/loopback#931
// error message should be "login failed" and not "login failed as the email has not been verified" // error message should be "login failed" and not "login failed as the email has not been verified"
assert(err && !/verified/.test(err.message), assert(err && !/verified/.test(err.message),
'expecting "login failed" error message, received: "' + err.message + '"'); 'expecting "login failed" error message, received: "' + err.message + '"');
assert.equal(err.code, 'LOGIN_FAILED'); assert.equal(err.code, 'LOGIN_FAILED');
assert.equal(err.details, undefined); assert.equal(err.details, undefined);
done(); done();
});
}); });
});
it('does not login a user with unverified email but provides userId', function() { it('does not login a user with unverified email but provides userId', function() {
return User.login(validCredentials).then( return User.login(validCredentials).then(
@ -1234,21 +1234,21 @@ describe('User', function() {
}); });
it('Logout a user by providing the current accessToken id (using node) - promise variant', it('Logout a user by providing the current accessToken id (using node) - promise variant',
function(done) { function(done) {
login(logout); login(logout);
function login(fn) { function login(fn) {
User.login(validCredentials, fn); User.login(validCredentials, fn);
} }
function logout(err, accessToken) { function logout(err, accessToken) {
User.logout(accessToken.id) User.logout(accessToken.id)
.then(function() { .then(function() {
verify(accessToken.id, done); verify(accessToken.id, done);
}) })
.catch(done(err)); .catch(done(err));
} }
}); });
it('Logout a user by providing the current accessToken id (over rest)', function(done) { it('Logout a user by providing the current accessToken id (over rest)', function(done) {
login(logout); login(logout);
@ -2056,23 +2056,23 @@ describe('User', function() {
}); });
it('verifies that verifyOptions.templateFn receives verifyOptions.verificationToken', it('verifies that verifyOptions.templateFn receives verifyOptions.verificationToken',
function() { function() {
let actualVerificationToken; let actualVerificationToken;
Object.assign(verifyOptions, { Object.assign(verifyOptions, {
redirect: '#/some-path?a=1&b=2', redirect: '#/some-path?a=1&b=2',
templateFn: (verifyOptions, cb) => { templateFn: (verifyOptions, cb) => {
actualVerificationToken = verifyOptions.verificationToken; actualVerificationToken = verifyOptions.verificationToken;
cb(null, 'dummy body'); cb(null, 'dummy body');
}, },
});
return user.verify(verifyOptions)
.then(() => actualVerificationToken)
.then(token => {
expect(token).to.exist();
}); });
});
return user.verify(verifyOptions)
.then(() => actualVerificationToken)
.then(token => {
expect(token).to.exist();
});
});
it('forwards the "options" argument to user.save() ' + it('forwards the "options" argument to user.save() ' +
'when adding verification token', function() { 'when adding verification token', function() {
@ -2722,10 +2722,10 @@ describe('User', function() {
function(err, accessToken2) { function(err, accessToken2) {
if (err) return next(err); if (err) return next(err);
User.login({email: 'user3@example.com', password: 'u3pass'}, User.login({email: 'user3@example.com', password: 'u3pass'},
function(err, accessToken3) { function(err, accessToken3) {
if (err) return next(err); if (err) return next(err);
next(); next();
}); });
}); });
}); });
}, },
@ -2760,12 +2760,12 @@ describe('User', function() {
async.series([ async.series([
function createSpecialUser(next) { function createSpecialUser(next) {
User.create( User.create(
{email: 'special@example.com', password: 'pass1', name: 'Special'}, {email: 'special@example.com', password: 'pass1', name: 'Special'},
function(err, specialInstance) { function(err, specialInstance) {
if (err) return next(err); if (err) return next(err);
userSpecial = specialInstance; userSpecial = specialInstance;
next(); next();
}); });
}, },
function loginSpecialUser(next) { function loginSpecialUser(next) {
User.login({email: 'special@example.com', password: 'pass1'}, function(err, ats) { User.login({email: 'special@example.com', password: 'pass1'}, function(err, ats) {
@ -2775,11 +2775,11 @@ describe('User', function() {
}, },
function updateSpecialUser(next) { function updateSpecialUser(next) {
User.updateAll( User.updateAll(
{name: 'Special'}, {name: 'Special'},
{email: 'superspecial@example.com'}, function(err, info) { {email: 'superspecial@example.com'}, function(err, info) {
if (err) return next(err); if (err) return next(err);
next(); next();
}); });
}, },
function verifyTokensOfSpecialUser(next) { function verifyTokensOfSpecialUser(next) {
AccessToken.find({where: {userId: userSpecial.pk}}, function(err, tokens1) { AccessToken.find({where: {userId: userSpecial.pk}}, function(err, tokens1) {
@ -2942,48 +2942,48 @@ describe('User', function() {
beforeEach(createOriginalUser); beforeEach(createOriginalUser);
it('sets verification to false after email update if verification is required', it('sets verification to false after email update if verification is required',
function(done) { function(done) {
User.settings.emailVerificationRequired = true; User.settings.emailVerificationRequired = true;
async.series([ async.series([
function updateUser(next) { function updateUser(next) {
userInstance.updateAttribute('email', NEW_EMAIL, function(err, info) { userInstance.updateAttribute('email', NEW_EMAIL, function(err, info) {
if (err) return next(err); if (err) return next(err);
assert.equal(info.email, NEW_EMAIL); assert.equal(info.email, NEW_EMAIL);
next(); next();
}); });
}, },
function findUser(next) { function findUser(next) {
User.findById(userInstance.pk, function(err, info) { User.findById(userInstance.pk, function(err, info) {
if (err) return next(err); if (err) return next(err);
assert.equal(info.email, NEW_EMAIL); assert.equal(info.email, NEW_EMAIL);
assert.equal(info.emailVerified, false); assert.equal(info.emailVerified, false);
next(); next();
}); });
}, },
], done); ], done);
}); });
it('leaves verification as is after email update if verification is not required', it('leaves verification as is after email update if verification is not required',
function(done) { function(done) {
User.settings.emailVerificationRequired = false; User.settings.emailVerificationRequired = false;
async.series([ async.series([
function updateUser(next) { function updateUser(next) {
userInstance.updateAttribute('email', NEW_EMAIL, function(err, info) { userInstance.updateAttribute('email', NEW_EMAIL, function(err, info) {
if (err) return next(err); if (err) return next(err);
assert.equal(info.email, NEW_EMAIL); assert.equal(info.email, NEW_EMAIL);
next(); next();
}); });
}, },
function findUser(next) { function findUser(next) {
User.findById(userInstance.pk, function(err, info) { User.findById(userInstance.pk, function(err, info) {
if (err) return next(err); if (err) return next(err);
assert.equal(info.email, NEW_EMAIL); assert.equal(info.email, NEW_EMAIL);
assert.equal(info.emailVerified, true); assert.equal(info.emailVerified, true);
next(); next();
}); });
}, },
], done); ], done);
}); });
it('should not set verification to false after something other than email is updated', it('should not set verification to false after something other than email is updated',
function(done) { function(done) {
@ -3023,39 +3023,39 @@ describe('User', function() {
describe('password reset with/without email verification', function() { describe('password reset with/without email verification', function() {
it('allows resetPassword by email if email verification is required and done', it('allows resetPassword by email if email verification is required and done',
function(done) { function(done) {
User.settings.emailVerificationRequired = true; User.settings.emailVerificationRequired = true;
var email = validCredentialsEmailVerified.email; var email = validCredentialsEmailVerified.email;
User.resetPassword({email: email}, function(err, info) { User.resetPassword({email: email}, function(err, info) {
if (err) return done(err); if (err) return done(err);
done(); done();
});
}); });
});
it('disallows resetPassword by email if email verification is required and not done', it('disallows resetPassword by email if email verification is required and not done',
function(done) { function(done) {
User.settings.emailVerificationRequired = true; User.settings.emailVerificationRequired = true;
var email = validCredentialsEmail; var email = validCredentialsEmail;
User.resetPassword({email: email}, function(err) { User.resetPassword({email: email}, function(err) {
assert(err); assert(err);
assert.equal(err.code, 'RESET_FAILED_EMAIL_NOT_VERIFIED'); assert.equal(err.code, 'RESET_FAILED_EMAIL_NOT_VERIFIED');
assert.equal(err.statusCode, 401); assert.equal(err.statusCode, 401);
done(); done();
});
}); });
});
it('allows resetPassword by email if email verification is not required', it('allows resetPassword by email if email verification is not required',
function(done) { function(done) {
User.settings.emailVerificationRequired = false; User.settings.emailVerificationRequired = false;
var email = validCredentialsEmail; var email = validCredentialsEmail;
User.resetPassword({email: email}, function(err) { User.resetPassword({email: email}, function(err) {
if (err) return done(err); if (err) return done(err);
done(); done();
});
}); });
});
}); });
describe('ctor', function() { describe('ctor', function() {
@ -3085,6 +3085,6 @@ describe('User', function() {
User.resetPassword({email: email}), User.resetPassword({email: email}),
waitForEvent(User, 'resetPasswordRequest'), waitForEvent(User, 'resetPasswordRequest'),
]) ])
.spread((reset, info) => info); .spread((reset, info) => info);
} }
}); });

View File

@ -96,7 +96,7 @@ module.exports = function defineModelTestsWithDataSource(options) {
var foo = new User({domain: 'www'}); var foo = new User({domain: 'www'});
var bar = new User({domain: 'billing'}); var bar = new User({domain: 'billing'});
var bat = new User({domain: 'admin'}); var bat = new User({domain: 'admin'});
assert(foo.isValid() === false); assert(foo.isValid() === false);
assert(bar.isValid() === false); assert(bar.isValid() === false);
assert(bat.isValid() === false); assert(bat.isValid() === false);
assert(foo.errors.domain, 'model should have a domain error'); assert(foo.errors.domain, 'model should have a domain error');
@ -139,13 +139,13 @@ module.exports = function defineModelTestsWithDataSource(options) {
describe('Model.create([data], [callback])', function() { describe('Model.create([data], [callback])', function() {
it('Create an instance of Model with given data and save to the attached data source', it('Create an instance of Model with given data and save to the attached data source',
function(done) { function(done) {
User.create({first: 'Joe', last: 'Bob'}, function(err, user) { User.create({first: 'Joe', last: 'Bob'}, function(err, user) {
assert(user instanceof User); assert(user instanceof User);
done(); done();
});
}); });
});
}); });
describe('model.save([options], [callback])', function() { describe('model.save([options], [callback])', function() {