common: coding style cleanup

This commit is contained in:
Miroslav Bajtoš 2014-11-04 13:52:49 +01:00
parent 2f1a1bcd7a
commit dc762d2514
12 changed files with 630 additions and 623 deletions

View File

@ -16,7 +16,7 @@
}, },
"validateJSDoc": { "validateJSDoc": {
"checkParamNames": false, "checkParamNames": false,
"checkRedundantParams": true, "checkRedundantParams": false,
"requireParamTypes": true "requireParamTypes": true
} }
} }

View File

@ -5,8 +5,6 @@
"indent": 2, "indent": 2,
"undef": true, "undef": true,
"quotmark": "single", "quotmark": "single",
"maxlen": 150,
"trailing": true,
"newcap": true, "newcap": true,
"nonew": true, "nonew": true,
"sub": true, "sub": true,

View File

@ -33,7 +33,9 @@ module.exports = function(grunt) {
lib: { lib: {
src: ['lib/**/*.js'] src: ['lib/**/*.js']
}, },
// TODO(bajtos) - common/**/*.js common: {
src: ['common/**/*.js']
},
// TODO tests don't pass yet // TODO tests don't pass yet
// test: { // test: {
// src: ['test/**/*.js'] // src: ['test/**/*.js']
@ -41,8 +43,9 @@ module.exports = function(grunt) {
}, },
jscs: { jscs: {
gruntfile: 'Gruntfile.js', gruntfile: 'Gruntfile.js',
lib: ['lib/**/*.js'] lib: ['lib/**/*.js'],
// TODO(bajtos) - common/**/*.js common: ['common/**/*.js']
// TODO(bajtos) - test/**/*.js
}, },
watch: { watch: {
gruntfile: { gruntfile: {

View File

@ -2,10 +2,10 @@
* Module Dependencies. * Module Dependencies.
*/ */
var loopback = require('../../lib/loopback') var loopback = require('../../lib/loopback');
, assert = require('assert') var assert = require('assert');
, uid = require('uid2') var uid = require('uid2');
, DEFAULT_TOKEN_LEN = 64; var DEFAULT_TOKEN_LEN = 64;
/** /**
* Token based authentication and access control. * Token based authentication and access control.
@ -57,7 +57,7 @@ module.exports = function(AccessToken) {
fn(null, guid); fn(null, guid);
} }
}); });
} };
/*! /*!
* Hook to create accessToken id. * Hook to create accessToken id.
@ -75,7 +75,7 @@ module.exports = function(AccessToken) {
next(); next();
} }
}); });
} };
/** /**
* Find a token for the given `ServerRequest`. * Find a token for the given `ServerRequest`.
@ -115,7 +115,7 @@ module.exports = function(AccessToken) {
cb(); cb();
}); });
} }
} };
/** /**
* Validate the token. * Validate the token.
@ -151,7 +151,7 @@ module.exports = function(AccessToken) {
} catch (e) { } catch (e) {
cb(e); cb(e);
} }
} };
function tokenIdForRequest(req, options) { function tokenIdForRequest(req, options) {
var params = options.params || []; var params = options.params || [];

View File

@ -179,7 +179,7 @@ module.exports = function(ACL) {
ACL.prototype.score = function(req) { ACL.prototype.score = function(req) {
return this.constructor.getMatchingScore(this, req); return this.constructor.getMatchingScore(this, req);
} };
/*! /*!
* Resolve permission from the ACLs * Resolve permission from the ACLs
@ -199,24 +199,26 @@ module.exports = function(ACL) {
var score = 0; var score = 0;
for (var i = 0; i < acls.length; i++) { for (var i = 0; i < acls.length; i++) {
score = ACL.getMatchingScore(acls[i], req); var candidate = acls[i];
score = ACL.getMatchingScore(candidate, req);
if (score < 0) { if (score < 0) {
// the highest scored ACL did not match // the highest scored ACL did not match
break; break;
} }
if (!req.isWildcard()) { if (!req.isWildcard()) {
// We should stop from the first match for non-wildcard // We should stop from the first match for non-wildcard
permission = acls[i].permission; permission = candidate.permission;
break; break;
} else { } else {
if (req.exactlyMatches(acls[i])) { if (req.exactlyMatches(candidate)) {
permission = acls[i].permission; permission = candidate.permission;
break; break;
} }
// For wildcard match, find the strongest permission // For wildcard match, find the strongest permission
if (AccessContext.permissionOrder[acls[i].permission] var candidateOrder = AccessContext.permissionOrder[candidate.permission];
> AccessContext.permissionOrder[permission]) { var permissionOrder = AccessContext.permissionOrder[permission];
permission = acls[i].permission; if (candidateOrder > permissionOrder) {
permission = candidate.permission;
} }
} }
} }
@ -246,8 +248,7 @@ module.exports = function(ACL) {
var staticACLs = []; var staticACLs = [];
if (modelClass && modelClass.settings.acls) { if (modelClass && modelClass.settings.acls) {
modelClass.settings.acls.forEach(function(acl) { modelClass.settings.acls.forEach(function(acl) {
if (!acl.property || acl.property === ACL.ALL if (!acl.property || acl.property === ACL.ALL || property === acl.property) {
|| property === acl.property) {
staticACLs.push(new ACL({ staticACLs.push(new ACL({
model: model, model: model,
property: acl.property || ACL.ALL, property: acl.property || ACL.ALL,
@ -259,11 +260,15 @@ module.exports = function(ACL) {
} }
}); });
} }
var prop = modelClass && var prop = modelClass && (
(modelClass.definition.properties[property] // regular property // regular property
|| (modelClass._scopeMeta && modelClass._scopeMeta[property]) // relation/scope modelClass.definition.properties[property] ||
|| modelClass[property] // static method // relation/scope
|| modelClass.prototype[property]); // prototype method (modelClass._scopeMeta && modelClass._scopeMeta[property]) ||
// static method
modelClass[property] ||
// prototype method
modelClass.prototype[property]);
if (prop && prop.acls) { if (prop && prop.acls) {
prop.acls.forEach(function(acl) { prop.acls.forEach(function(acl) {
staticACLs.push(new ACL({ staticACLs.push(new ACL({
@ -311,7 +316,7 @@ module.exports = function(ACL) {
debug('Permission denied by statically resolved permission'); debug('Permission denied by statically resolved permission');
debug(' Resolved Permission: %j', resolved); debug(' Resolved Permission: %j', resolved);
process.nextTick(function() { process.nextTick(function() {
callback && callback(null, resolved); if (callback) callback(null, resolved);
}); });
return; return;
} }
@ -321,7 +326,7 @@ module.exports = function(ACL) {
model: model, property: propertyQuery, accessType: accessTypeQuery}}, model: model, property: propertyQuery, accessType: accessTypeQuery}},
function(err, dynACLs) { function(err, dynACLs) {
if (err) { if (err) {
callback && callback(err); if (callback) callback(err);
return; return;
} }
acls = acls.concat(dynACLs); acls = acls.concat(dynACLs);
@ -330,7 +335,7 @@ module.exports = function(ACL) {
var modelClass = loopback.findModel(model); var modelClass = loopback.findModel(model);
resolved.permission = (modelClass && modelClass.settings.defaultPermission) || ACL.ALLOW; resolved.permission = (modelClass && modelClass.settings.defaultPermission) || ACL.ALLOW;
} }
callback && callback(null, resolved); if (callback) callback(null, resolved);
}); });
}; };
@ -344,7 +349,7 @@ module.exports = function(ACL) {
debug('accessType %s', this.accessType); debug('accessType %s', this.accessType);
debug('permission %s', this.permission); debug('permission %s', this.permission);
} }
} };
/** /**
* Check if the request has the permission to access. * Check if the request has the permission to access.
@ -381,7 +386,7 @@ module.exports = function(ACL) {
this.find({where: {model: model.modelName, property: propertyQuery, this.find({where: {model: model.modelName, property: propertyQuery,
accessType: accessTypeQuery}}, function(err, acls) { accessType: accessTypeQuery}}, function(err, acls) {
if (err) { if (err) {
callback && callback(err); if (callback) callback(err);
return; return;
} }
var inRoleTasks = []; var inRoleTasks = [];
@ -392,8 +397,9 @@ module.exports = function(ACL) {
// Check exact matches // Check exact matches
for (var i = 0; i < context.principals.length; i++) { for (var i = 0; i < context.principals.length; i++) {
var p = context.principals[i]; var p = context.principals[i];
if (p.type === acl.principalType var typeMatch = p.type === acl.principalType;
&& String(p.id) === String(acl.principalId)) { var idMatch = String(p.id) === String(acl.principalId);
if (typeMatch && idMatch) {
effectiveACLs.push(acl); effectiveACLs.push(acl);
return; return;
} }
@ -415,7 +421,7 @@ module.exports = function(ACL) {
async.parallel(inRoleTasks, function(err, results) { async.parallel(inRoleTasks, function(err, results) {
if (err) { if (err) {
callback && callback(err, null); if (callback) callback(err, null);
return; return;
} }
var resolved = self.resolvePermission(effectiveACLs, req); var resolved = self.resolvePermission(effectiveACLs, req);
@ -424,7 +430,7 @@ module.exports = function(ACL) {
} }
debug('---Resolved---'); debug('---Resolved---');
resolved.debug(); resolved.debug();
callback && callback(null, resolved); if (callback) callback(null, resolved);
}); });
}); });
}; };
@ -452,11 +458,10 @@ module.exports = function(ACL) {
this.checkAccessForContext(context, function(err, access) { this.checkAccessForContext(context, function(err, access) {
if (err) { if (err) {
callback && callback(err); if (callback) callback(err);
return; return;
} }
callback && callback(null, access.permission !== ACL.DENY); if (callback) callback(null, access.permission !== ACL.DENY);
}); });
}; };
};
}

View File

@ -141,7 +141,7 @@ module.exports = function(Application) {
Application.resetKeys = function(appId, cb) { Application.resetKeys = function(appId, cb) {
this.findById(appId, function(err, app) { this.findById(appId, function(err, app) {
if (err) { if (err) {
cb && cb(err, app); if (cb) cb(err, app);
return; return;
} }
app.resetKeys(cb); app.resetKeys(cb);
@ -166,7 +166,7 @@ module.exports = function(Application) {
Application.authenticate = function(appId, key, cb) { Application.authenticate = function(appId, key, cb) {
this.findById(appId, function(err, app) { this.findById(appId, function(err, app) {
if (err || !app) { if (err || !app) {
cb && cb(err, null); if (cb) cb(err, null);
return; return;
} }
var result = null; var result = null;
@ -180,7 +180,7 @@ module.exports = function(Application) {
break; break;
} }
} }
cb && cb(null, result); if (cb) cb(null, result);
}); });
}; };
}; };

View File

@ -2,14 +2,13 @@
* Module Dependencies. * Module Dependencies.
*/ */
var PersistedModel = require('../../lib/loopback').PersistedModel var PersistedModel = require('../../lib/loopback').PersistedModel;
, loopback = require('../../lib/loopback') var loopback = require('../../lib/loopback');
, crypto = require('crypto') var crypto = require('crypto');
, CJSON = {stringify: require('canonical-json')} var CJSON = {stringify: require('canonical-json')};
, async = require('async') var async = require('async');
, assert = require('assert') var assert = require('assert');
, debug = require('debug')('loopback:change'); var debug = require('debug')('loopback:change');
/** /**
* Change list entry. * Change list entry.
@ -55,8 +54,8 @@ module.exports = function(Change) {
if (!hasModel) return null; if (!hasModel) return null;
return Change.idForModel(this.modelName, this.modelId); return Change.idForModel(this.modelName, this.modelId);
} };
} };
Change.setup(); Change.setup();
/** /**
@ -82,7 +81,7 @@ module.exports = function(Change) {
}); });
}); });
async.parallel(tasks, callback); async.parallel(tasks, callback);
} };
/** /**
* Get an identifier for a given model. * Get an identifier for a given model.
@ -94,7 +93,7 @@ module.exports = function(Change) {
Change.idForModel = function(modelName, modelId) { Change.idForModel = function(modelName, modelId) {
return this.hash([modelName, modelId].join('-')); return this.hash([modelName, modelId].join('-'));
} };
/** /**
* Find or create a change for the given model. * Find or create a change for the given model.
@ -126,7 +125,7 @@ module.exports = function(Change) {
ch.save(callback); ch.save(callback);
} }
}); });
} };
/** /**
* Update (or create) the change with the current revision. * Update (or create) the change with the current revision.
@ -148,7 +147,7 @@ module.exports = function(Change) {
cb = cb || function(err) { cb = cb || function(err) {
if (err) throw new Error(err); if (err) throw new Error(err);
} };
async.parallel(tasks, function(err) { async.parallel(tasks, function(err) {
if (err) return cb(err); if (err) return cb(err);
@ -194,7 +193,7 @@ module.exports = function(Change) {
cb(); cb();
}); });
} }
} };
/** /**
* Get a change's current revision based on current data. * Get a change's current revision based on current data.
@ -214,7 +213,7 @@ module.exports = function(Change) {
cb(null, null); cb(null, null);
} }
}); });
} };
/** /**
* Create a hash of the given `string` with the `options.hashAlgorithm`. * Create a hash of the given `string` with the `options.hashAlgorithm`.
@ -229,7 +228,7 @@ module.exports = function(Change) {
.createHash(Change.settings.hashAlgorithm || 'sha1') .createHash(Change.settings.hashAlgorithm || 'sha1')
.update(str) .update(str)
.digest('hex'); .digest('hex');
} };
/** /**
* Get the revision string for the given object * Get the revision string for the given object
@ -239,7 +238,7 @@ module.exports = function(Change) {
Change.revisionForInst = function(inst) { Change.revisionForInst = function(inst) {
return this.hash(CJSON.stringify(inst)); return this.hash(CJSON.stringify(inst));
} };
/** /**
* Get a change's type. Returns one of: * Get a change's type. Returns one of:
@ -263,7 +262,7 @@ module.exports = function(Change) {
return Change.DELETE; return Change.DELETE;
} }
return Change.UNKNOWN; return Change.UNKNOWN;
} };
/** /**
* Compare two changes. * Compare two changes.
@ -276,7 +275,7 @@ module.exports = function(Change) {
var thisRev = this.rev || null; var thisRev = this.rev || null;
var thatRev = change.rev || null; var thatRev = change.rev || null;
return thisRev === thatRev; return thisRev === thatRev;
} };
/** /**
* Does this change conflict with the given change. * Does this change conflict with the given change.
@ -290,7 +289,7 @@ module.exports = function(Change) {
if (Change.bothDeleted(this, change)) return false; if (Change.bothDeleted(this, change)) return false;
if (this.isBasedOn(change)) return false; if (this.isBasedOn(change)) return false;
return true; return true;
} };
/** /**
* Are both changes deletes? * Are both changes deletes?
@ -300,9 +299,9 @@ module.exports = function(Change) {
*/ */
Change.bothDeleted = function(a, b) { Change.bothDeleted = function(a, b) {
return a.type() === Change.DELETE return a.type() === Change.DELETE &&
&& b.type() === Change.DELETE; b.type() === Change.DELETE;
} };
/** /**
* Determine if the change is based on the given change. * Determine if the change is based on the given change.
@ -312,7 +311,7 @@ module.exports = function(Change) {
Change.prototype.isBasedOn = function(change) { Change.prototype.isBasedOn = function(change) {
return this.prev === change.rev; return this.prev === change.rev;
} };
/** /**
* Determine the differences for a given model since a given checkpoint. * Determine the differences for a given model since a given checkpoint.
@ -393,11 +392,11 @@ module.exports = function(Change) {
conflicts: conflicts conflicts: conflicts
}); });
}); });
} };
/** /**
* Correct all change list entries. * Correct all change list entries.
* @param {Function} callback * @param {Function} cb
*/ */
Change.rectifyAll = function(cb) { Change.rectifyAll = function(cb) {
@ -411,7 +410,7 @@ module.exports = function(Change) {
change.rectify(); change.rectify();
}); });
}); });
} };
/** /**
* Get the checkpoint model. * Get the checkpoint model.
@ -426,13 +425,13 @@ module.exports = function(Change) {
+ ' is not attached to a dataSource'); + ' is not attached to a dataSource');
checkpointModel.attachTo(this.dataSource); checkpointModel.attachTo(this.dataSource);
return checkpointModel; return checkpointModel;
} };
Change.handleError = function(err) { Change.handleError = function(err) {
if (!this.settings.ignoreErrors) { if (!this.settings.ignoreErrors) {
throw err; throw err;
} }
} };
Change.prototype.debug = function() { Change.prototype.debug = function() {
if (debug.enabled) { if (debug.enabled) {
@ -445,7 +444,7 @@ module.exports = function(Change) {
debug('\tmodelId', this.modelId); debug('\tmodelId', this.modelId);
debug('\ttype', this.type()); debug('\ttype', this.type());
} }
} };
/** /**
* Get the `Model` class for `change.modelName`. * Get the `Model` class for `change.modelName`.
@ -454,7 +453,7 @@ module.exports = function(Change) {
Change.prototype.getModelCtor = function() { Change.prototype.getModelCtor = function() {
return this.constructor.settings.trackModel; return this.constructor.settings.trackModel;
} };
Change.prototype.getModelId = function() { Change.prototype.getModelId = function() {
// TODO(ritch) get rid of the need to create an instance // TODO(ritch) get rid of the need to create an instance
@ -463,13 +462,13 @@ module.exports = function(Change) {
var m = new Model(); var m = new Model();
m.setId(id); m.setId(id);
return m.getId(); return m.getId();
} };
Change.prototype.getModel = function(callback) { Change.prototype.getModel = function(callback) {
var Model = this.constructor.settings.trackModel; var Model = this.constructor.settings.trackModel;
var id = this.getModelId(); var id = this.getModelId();
Model.findById(id, callback); Model.findById(id, callback);
} };
/** /**
* When two changes conflict a conflict is created. * When two changes conflict a conflict is created.
@ -533,7 +532,7 @@ module.exports = function(Change) {
if (err) return cb(err); if (err) return cb(err);
cb(null, source, target); cb(null, source, target);
} }
} };
/** /**
* Get the conflicting changes. * Get the conflicting changes.
@ -578,7 +577,7 @@ module.exports = function(Change) {
if (err) return cb(err); if (err) return cb(err);
cb(null, sourceChange, targetChange); cb(null, sourceChange, targetChange);
} }
} };
/** /**
* Resolve the conflict. * Resolve the conflict.
@ -594,7 +593,7 @@ module.exports = function(Change) {
sourceChange.prev = targetChange.rev; sourceChange.prev = targetChange.rev;
sourceChange.save(cb); sourceChange.save(cb);
}); });
} };
/** /**
* Determine the conflict type. * Determine the conflict type.
@ -624,5 +623,5 @@ module.exports = function(Change) {
} }
return cb(null, Change.UNKNOWN); return cb(null, Change.UNKNOWN);
}); });
} };
}; };

View File

@ -47,7 +47,7 @@ module.exports = function(Checkpoint) {
}); });
} }
}); });
} };
Checkpoint.beforeSave = function(next, model) { Checkpoint.beforeSave = function(next, model) {
if (!model.getId() && model.seq === undefined) { if (!model.getId() && model.seq === undefined) {
@ -59,5 +59,5 @@ module.exports = function(Checkpoint) {
} else { } else {
next(); next();
} }
} };
}; };

View File

@ -23,14 +23,14 @@ module.exports = function(RoleMapping) {
* @param {Error} err * @param {Error} err
* @param {Application} application * @param {Application} application
*/ */
RoleMapping.prototype.application = function (callback) { RoleMapping.prototype.application = function(callback) {
if (this.principalType === RoleMapping.APPLICATION) { if (this.principalType === RoleMapping.APPLICATION) {
var applicationModel = this.constructor.Application var applicationModel = this.constructor.Application ||
|| loopback.getModelByType(loopback.Application); loopback.getModelByType(loopback.Application);
applicationModel.findById(this.principalId, callback); applicationModel.findById(this.principalId, callback);
} else { } else {
process.nextTick(function () { process.nextTick(function() {
callback && callback(null, null); if (callback) callback(null, null);
}); });
} }
}; };
@ -41,14 +41,14 @@ module.exports = function(RoleMapping) {
* @param {Error} err * @param {Error} err
* @param {User} user * @param {User} user
*/ */
RoleMapping.prototype.user = function (callback) { RoleMapping.prototype.user = function(callback) {
if (this.principalType === RoleMapping.USER) { if (this.principalType === RoleMapping.USER) {
var userModel = this.constructor.User var userModel = this.constructor.User ||
|| loopback.getModelByType(loopback.User); loopback.getModelByType(loopback.User);
userModel.findById(this.principalId, callback); userModel.findById(this.principalId, callback);
} else { } else {
process.nextTick(function () { process.nextTick(function() {
callback && callback(null, null); if (callback) callback(null, null);
}); });
} }
}; };
@ -59,14 +59,14 @@ module.exports = function(RoleMapping) {
* @param {Error} err * @param {Error} err
* @param {User} childUser * @param {User} childUser
*/ */
RoleMapping.prototype.childRole = function (callback) { RoleMapping.prototype.childRole = function(callback) {
if (this.principalType === RoleMapping.ROLE) { if (this.principalType === RoleMapping.ROLE) {
var roleModel = this.constructor.Role || var roleModel = this.constructor.Role ||
loopback.getModelByType(loopback.Role); loopback.getModelByType(loopback.Role);
roleModel.findById(this.principalId, callback); roleModel.findById(this.principalId, callback);
} else { } else {
process.nextTick(function () { process.nextTick(function() {
callback && callback(null, null); if (callback) callback(null, null);
}); });
} }
}; };

View File

@ -33,7 +33,7 @@ module.exports = function(Role) {
roleMappingModel.find({where: {roleId: this.id, roleMappingModel.find({where: {roleId: this.id,
principalType: RoleMapping.USER}}, function(err, mappings) { principalType: RoleMapping.USER}}, function(err, mappings) {
if (err) { if (err) {
callback && callback(err); if (callback) callback(err);
return; return;
} }
return mappings.map(function(m) { return mappings.map(function(m) {
@ -46,7 +46,7 @@ module.exports = function(Role) {
roleMappingModel.find({where: {roleId: this.id, roleMappingModel.find({where: {roleId: this.id,
principalType: RoleMapping.APPLICATION}}, function(err, mappings) { principalType: RoleMapping.APPLICATION}}, function(err, mappings) {
if (err) { if (err) {
callback && callback(err); if (callback) callback(err);
return; return;
} }
return mappings.map(function(m) { return mappings.map(function(m) {
@ -59,7 +59,7 @@ module.exports = function(Role) {
roleMappingModel.find({where: {roleId: this.id, roleMappingModel.find({where: {roleId: this.id,
principalType: RoleMapping.ROLE}}, function(err, mappings) { principalType: RoleMapping.ROLE}}, function(err, mappings) {
if (err) { if (err) {
callback && callback(err); if (callback) callback(err);
return; return;
} }
return mappings.map(function(m) { return mappings.map(function(m) {
@ -72,10 +72,10 @@ module.exports = function(Role) {
// Special roles // Special roles
Role.OWNER = '$owner'; // owner of the object Role.OWNER = '$owner'; // owner of the object
Role.RELATED = "$related"; // any User with a relationship to the object Role.RELATED = '$related'; // any User with a relationship to the object
Role.AUTHENTICATED = "$authenticated"; // authenticated user Role.AUTHENTICATED = '$authenticated'; // authenticated user
Role.UNAUTHENTICATED = "$unauthenticated"; // authenticated user Role.UNAUTHENTICATED = '$unauthenticated'; // authenticated user
Role.EVERYONE = "$everyone"; // everyone Role.EVERYONE = '$everyone'; // everyone
/** /**
* Add custom handler for roles. * Add custom handler for roles.
@ -93,7 +93,7 @@ module.exports = function(Role) {
Role.registerResolver(Role.OWNER, function(role, context, callback) { Role.registerResolver(Role.OWNER, function(role, context, callback) {
if (!context || !context.model || !context.modelId) { if (!context || !context.model || !context.modelId) {
process.nextTick(function() { process.nextTick(function() {
callback && callback(null, false); if (callback) callback(null, false);
}); });
return; return;
} }
@ -152,13 +152,13 @@ module.exports = function(Role) {
modelClass.findById(modelId, function(err, inst) { modelClass.findById(modelId, function(err, inst) {
if (err || !inst) { if (err || !inst) {
debug('Model not found for id %j', modelId); debug('Model not found for id %j', modelId);
callback && callback(err, false); if (callback) callback(err, false);
return; return;
} }
debug('Model found: %j', inst); debug('Model found: %j', inst);
var ownerId = inst.userId || inst.owner; var ownerId = inst.userId || inst.owner;
if (ownerId) { if (ownerId) {
callback && callback(null, matches(ownerId, userId)); if (callback) callback(null, matches(ownerId, userId));
return; return;
} else { } else {
// Try to follow belongsTo // Try to follow belongsTo
@ -166,19 +166,21 @@ module.exports = function(Role) {
var rel = modelClass.relations[r]; var rel = modelClass.relations[r];
if (rel.type === 'belongsTo' && isUserClass(rel.modelTo)) { if (rel.type === 'belongsTo' && isUserClass(rel.modelTo)) {
debug('Checking relation %s to %s: %j', r, rel.modelTo.modelName, rel); debug('Checking relation %s to %s: %j', r, rel.modelTo.modelName, rel);
inst[r](function(err, user) { inst[r](processRelatedUser);
if (!err && user) {
debug('User found: %j', user.id);
callback && callback(null, matches(user.id, userId));
} else {
callback && callback(err, false);
}
});
return; return;
} }
} }
debug('No matching belongsTo relation found for model %j and user: %j', modelId, userId); debug('No matching belongsTo relation found for model %j and user: %j', modelId, userId);
callback && callback(null, false); if (callback) callback(null, false);
}
function processRelatedUser(err, user) {
if (!err && user) {
debug('User found: %j', user.id);
if (callback) callback(null, matches(user.id, userId));
} else {
if (callback) callback(err, false);
}
} }
}); });
}; };
@ -186,7 +188,7 @@ module.exports = function(Role) {
Role.registerResolver(Role.AUTHENTICATED, function(role, context, callback) { Role.registerResolver(Role.AUTHENTICATED, function(role, context, callback) {
if (!context) { if (!context) {
process.nextTick(function() { process.nextTick(function() {
callback && callback(null, false); if (callback) callback(null, false);
}); });
return; return;
} }
@ -202,19 +204,19 @@ module.exports = function(Role) {
*/ */
Role.isAuthenticated = function isAuthenticated(context, callback) { Role.isAuthenticated = function isAuthenticated(context, callback) {
process.nextTick(function() { process.nextTick(function() {
callback && callback(null, context.isAuthenticated()); if (callback) callback(null, context.isAuthenticated());
}); });
}; };
Role.registerResolver(Role.UNAUTHENTICATED, function(role, context, callback) { Role.registerResolver(Role.UNAUTHENTICATED, function(role, context, callback) {
process.nextTick(function() { process.nextTick(function() {
callback && callback(null, !context || !context.isAuthenticated()); if (callback) callback(null, !context || !context.isAuthenticated());
}); });
}); });
Role.registerResolver(Role.EVERYONE, function(role, context, callback) { Role.registerResolver(Role.EVERYONE, function(role, context, callback) {
process.nextTick(function() { process.nextTick(function() {
callback && callback(null, true); // Always true if (callback) callback(null, true); // Always true
}); });
}); });
@ -245,7 +247,7 @@ module.exports = function(Role) {
if (context.principals.length === 0) { if (context.principals.length === 0) {
debug('isInRole() returns: false'); debug('isInRole() returns: false');
process.nextTick(function() { process.nextTick(function() {
callback && callback(null, false); if (callback) callback(null, false);
}); });
return; return;
} }
@ -262,7 +264,7 @@ module.exports = function(Role) {
if (inRole) { if (inRole) {
debug('isInRole() returns: %j', inRole); debug('isInRole() returns: %j', inRole);
process.nextTick(function() { process.nextTick(function() {
callback && callback(null, true); if (callback) callback(null, true);
}); });
return; return;
} }
@ -270,11 +272,11 @@ module.exports = function(Role) {
var roleMappingModel = this.RoleMapping || loopback.getModelByType(RoleMapping); var roleMappingModel = this.RoleMapping || loopback.getModelByType(RoleMapping);
this.findOne({where: {name: role}}, function(err, result) { this.findOne({where: {name: role}}, function(err, result) {
if (err) { if (err) {
callback && callback(err); if (callback) callback(err);
return; return;
} }
if (!result) { if (!result) {
callback && callback(null, false); if (callback) callback(null, false);
return; return;
} }
debug('Role found: %j', result); debug('Role found: %j', result);
@ -303,7 +305,7 @@ module.exports = function(Role) {
} }
}, function(inRole) { }, function(inRole) {
debug('isInRole() returns: %j', inRole); debug('isInRole() returns: %j', inRole);
callback && callback(null, inRole); if (callback) callback(null, inRole);
}); });
}); });
@ -315,8 +317,8 @@ module.exports = function(Role) {
* @param {Function} callback * @param {Function} callback
* *
* @callback {Function} callback * @callback {Function} callback
* @param err * @param {Error=} err
* @param {String[]} An array of role ids * @param {String[]} roles An array of role ids
*/ */
Role.getRoles = function(context, callback) { Role.getRoles = function(context, callback) {
if (!(context instanceof AccessContext)) { if (!(context instanceof AccessContext)) {
@ -355,7 +357,7 @@ module.exports = function(Role) {
var principalType = p.type || undefined; var principalType = p.type || undefined;
var principalId = p.id == null ? undefined : p.id; var principalId = p.id == null ? undefined : p.id;
if(typeof principalId !== 'string' && principalId != null) { if (typeof principalId !== 'string' && principalId != null) {
principalId = principalId.toString(); principalId = principalId.toString();
} }
@ -371,13 +373,13 @@ module.exports = function(Role) {
principalId: principalId}}, function(err, mappings) { principalId: principalId}}, function(err, mappings) {
debug('Role mappings found: %s %j', err, mappings); debug('Role mappings found: %s %j', err, mappings);
if (err) { if (err) {
done && done(err); if (done) done(err);
return; return;
} }
mappings.forEach(function(m) { mappings.forEach(function(m) {
addRole(m.roleId); addRole(m.roleId);
}); });
done && done(); if (done) done();
}); });
}); });
} }
@ -385,7 +387,7 @@ module.exports = function(Role) {
async.parallel(inRoleTasks, function(err, results) { async.parallel(inRoleTasks, function(err, results) {
debug('getRoles() returns: %j %j', err, roles); debug('getRoles() returns: %j %j', err, roles);
callback && callback(err, roles); if (callback) callback(err, roles);
}); });
}; };
}; };

View File

@ -23,14 +23,14 @@ module.exports = function(Scope) {
* @param {String|Error} err The error object * @param {String|Error} err The error object
* @param {AccessRequest} result The access permission * @param {AccessRequest} result The access permission
*/ */
Scope.checkPermission = function (scope, model, property, accessType, callback) { Scope.checkPermission = function(scope, model, property, accessType, callback) {
var ACL = loopback.ACL; var ACL = loopback.ACL;
assert(ACL, assert(ACL,
'ACL model must be defined before Scope.checkPermission is called'); 'ACL model must be defined before Scope.checkPermission is called');
this.findOne({where: {name: scope}}, function (err, scope) { this.findOne({where: {name: scope}}, function(err, scope) {
if (err) { if (err) {
callback && callback(err); if (callback) callback(err);
} else { } else {
var aclModel = loopback.getModelByType(ACL); var aclModel = loopback.getModelByType(ACL);
aclModel.checkPermission(ACL.SCOPE, scope.id, model, property, accessType, callback); aclModel.checkPermission(ACL.SCOPE, scope.id, model, property, accessType, callback);

View File

@ -2,15 +2,15 @@
* Module Dependencies. * Module Dependencies.
*/ */
var loopback = require('../../lib/loopback') var loopback = require('../../lib/loopback');
, path = require('path') var path = require('path');
, SALT_WORK_FACTOR = 10 var SALT_WORK_FACTOR = 10;
, crypto = require('crypto') var crypto = require('crypto');
, bcrypt = require('bcryptjs') var bcrypt = require('bcryptjs');
, DEFAULT_TTL = 1209600 // 2 weeks in seconds var DEFAULT_TTL = 1209600; // 2 weeks in seconds
, DEFAULT_RESET_PW_TTL = 15 * 60 // 15 mins in seconds var DEFAULT_RESET_PW_TTL = 15 * 60; // 15 mins in seconds
, DEFAULT_MAX_TTL = 31556926 // 1 year in seconds var DEFAULT_MAX_TTL = 31556926; // 1 year in seconds
, assert = require('assert'); var assert = require('assert');
var debug = require('debug')('loopback:user'); var debug = require('debug')('loopback:user');
@ -40,26 +40,26 @@ var debug = require('debug')('loopback:user');
module.exports = function(User) { module.exports = function(User) {
/** /**
* Create access token for the logged in user. This method can be overridden to * Create access token for the logged in user. This method can be overridden to
* customize how access tokens are generated * customize how access tokens are generated
* *
* @param [Number} ttl The requested ttl * @param {Number} ttl The requested ttl
* @callack {Function} cb The callback function * @callack {Function} cb The callback function
* @param {String|Error} err The error string or object * @param {String|Error} err The error string or object
* @param {AccessToken} token The generated access token object * @param {AccessToken} token The generated access token object
*/ */
User.prototype.createAccessToken = function(ttl, cb) { User.prototype.createAccessToken = function(ttl, cb) {
var userModel = this.constructor; var userModel = this.constructor;
ttl = Math.min(ttl || userModel.settings.ttl, userModel.settings.maxTTL); ttl = Math.min(ttl || userModel.settings.ttl, userModel.settings.maxTTL);
this.accessTokens.create({ this.accessTokens.create({
ttl: ttl ttl: ttl
}, cb); }, cb);
}; };
function splitPrincipal(name, realmDelimiter) { function splitPrincipal(name, realmDelimiter) {
var parts = [null, name]; var parts = [null, name];
if(!realmDelimiter) { if (!realmDelimiter) {
return parts; return parts;
} }
var index = name.indexOf(realmDelimiter); var index = name.indexOf(realmDelimiter);
@ -68,19 +68,19 @@ function splitPrincipal(name, realmDelimiter) {
parts[1] = name.substring(index + realmDelimiter.length); parts[1] = name.substring(index + realmDelimiter.length);
} }
return parts; return parts;
} }
/** /**
* Normalize the credentials * Normalize the credentials
* @param {Object} credentials The credential object * @param {Object} credentials The credential object
* @param {Boolean} realmRequired * @param {Boolean} realmRequired
* @param {String} realmDelimiter The realm delimiter, if not set, no realm is needed * @param {String} realmDelimiter The realm delimiter, if not set, no realm is needed
* @returns {Object} The normalized credential object * @returns {Object} The normalized credential object
*/ */
User.normalizeCredentials = function(credentials, realmRequired, realmDelimiter) { User.normalizeCredentials = function(credentials, realmRequired, realmDelimiter) {
var query = {}; var query = {};
credentials = credentials || {}; credentials = credentials || {};
if(!realmRequired) { if (!realmRequired) {
if (credentials.email) { if (credentials.email) {
query.email = credentials.email; query.email = credentials.email;
} else if (credentials.username) { } else if (credentials.username) {
@ -106,15 +106,15 @@ User.normalizeCredentials = function(credentials, realmRequired, realmDelimiter)
} }
} }
return query; return query;
} };
/** /**
* Login a user by with the given `credentials`. * Login a user by with the given `credentials`.
* *
* ```js * ```js
* User.login({username: 'foo', password: 'bar'}, function (err, token) { * User.login({username: 'foo', password: 'bar'}, function (err, token) {
* console.log(token.id); * console.log(token.id);
* }); * });
* ``` * ```
* *
* @param {Object} credentials username/password or email/password * @param {Object} credentials username/password or email/password
@ -125,7 +125,7 @@ User.normalizeCredentials = function(credentials, realmRequired, realmDelimiter)
* @param {AccessToken} token Access token if login is successful * @param {AccessToken} token Access token if login is successful
*/ */
User.login = function(credentials, include, fn) { User.login = function(credentials, include, fn) {
var self = this; var self = this;
if (typeof include === 'function') { if (typeof include === 'function') {
fn = include; fn = include;
@ -151,7 +151,7 @@ User.login = function(credentials, include, fn) {
var query = self.normalizeCredentials(credentials, realmRequired, var query = self.normalizeCredentials(credentials, realmRequired,
realmDelimiter); realmDelimiter);
if(realmRequired && !query.realm) { if (realmRequired && !query.realm) {
var err1 = new Error('realm is required'); var err1 = new Error('realm is required');
err1.statusCode = 400; err1.statusCode = 400;
return fn(err1); return fn(err1);
@ -207,15 +207,15 @@ User.login = function(credentials, include, fn) {
fn(defaultError); fn(defaultError);
} }
}); });
}; };
/** /**
* Logout a user with the given accessToken id. * Logout a user with the given accessToken id.
* *
* ```js * ```js
* User.logout('asd0a9f8dsj9s0s3223mk', function (err) { * User.logout('asd0a9f8dsj9s0s3223mk', function (err) {
* console.log(err || 'Logged out'); * console.log(err || 'Logged out');
* }); * });
* ``` * ```
* *
* @param {String} accessTokenID * @param {String} accessTokenID
@ -223,7 +223,7 @@ User.login = function(credentials, include, fn) {
* @param {Error} err * @param {Error} err
*/ */
User.logout = function(tokenId, fn) { User.logout = function(tokenId, fn) {
this.relations.accessTokens.modelTo.findById(tokenId, function(err, accessToken) { this.relations.accessTokens.modelTo.findById(tokenId, function(err, accessToken) {
if (err) { if (err) {
fn(err); fn(err);
@ -233,16 +233,16 @@ User.logout = function(tokenId, fn) {
fn(new Error('could not find accessToken')); fn(new Error('could not find accessToken'));
} }
}); });
} };
/** /**
* Compare the given `password` with the users hashed password. * Compare the given `password` with the users hashed password.
* *
* @param {String} password The plain text password * @param {String} password The plain text password
* @returns {Boolean} * @returns {Boolean}
*/ */
User.prototype.hasPassword = function(plain, fn) { User.prototype.hasPassword = function(plain, fn) {
if (this.password && plain) { if (this.password && plain) {
bcrypt.compare(plain, this.password, function(err, isMatch) { bcrypt.compare(plain, this.password, function(err, isMatch) {
if (err) return fn(err); if (err) return fn(err);
@ -251,18 +251,18 @@ User.prototype.hasPassword = function(plain, fn) {
} else { } else {
fn(null, false); fn(null, false);
} }
} };
/** /**
* Verify a user's identity by sending them a confirmation email. * Verify a user's identity by sending them a confirmation email.
* *
* ```js * ```js
* var options = { * var options = {
* type: 'email', * type: 'email',
* to: user.email, * to: user.email,
* template: 'verify.ejs', * template: 'verify.ejs',
* redirect: '/' * redirect: '/'
* }; * };
* *
* user.verify(options, next); * user.verify(options, next);
* ``` * ```
@ -280,7 +280,7 @@ User.prototype.hasPassword = function(plain, fn) {
* they verify their email, for example `'/'` for root URI. * they verify their email, for example `'/'` for root URI.
*/ */
User.prototype.verify = function(options, fn) { User.prototype.verify = function(options, fn) {
var user = this; var user = this;
var userModel = this.constructor; var userModel = this.constructor;
assert(typeof options === 'object', 'options required when calling user.verify()'); assert(typeof options === 'object', 'options required when calling user.verify()');
@ -299,19 +299,18 @@ User.prototype.verify = function(options, fn) {
options.port = options.port || (app && app.get('port')) || 3000; options.port = options.port || (app && app.get('port')) || 3000;
options.restApiRoot = options.restApiRoot || (app && app.get('restApiRoot')) || '/api'; options.restApiRoot = options.restApiRoot || (app && app.get('restApiRoot')) || '/api';
options.verifyHref = options.verifyHref || options.verifyHref = options.verifyHref ||
options.protocol options.protocol +
+ '://' '://' +
+ options.host options.host +
+ ':' ':' +
+ options.port options.port +
+ options.restApiRoot options.restApiRoot +
+ userModel.http.path userModel.http.path +
+ userModel.confirm.http.path userModel.confirm.http.path +
+ '?uid=' '?uid=' +
+ options.user.id options.user.id +
+ '&redirect=' '&redirect=' +
+ options.redirect; options.redirect;
// Email model // Email model
var Email = options.mailer || this.constructor.email || loopback.getModelByType(loopback.Email); var Email = options.mailer || this.constructor.email || loopback.getModelByType(loopback.Email);
@ -347,18 +346,17 @@ User.prototype.verify = function(options, fn) {
text: options.text, text: options.text,
html: template(options), html: template(options),
headers: options.headers || {} headers: options.headers || {}
}, function (err, email) { }, function(err, email) {
if(err) { if (err) {
fn(err); fn(err);
} else { } else {
fn(null, {email: email, token: user.verificationToken, uid: user.id}); fn(null, {email: email, token: user.verificationToken, uid: user.id});
} }
}); });
} }
} };
/**
/**
* Confirm the user's identity. * Confirm the user's identity.
* *
* @param {Any} userId * @param {Any} userId
@ -367,7 +365,7 @@ User.prototype.verify = function(options, fn) {
* @callback {Function} callback * @callback {Function} callback
* @param {Error} err * @param {Error} err
*/ */
User.confirm = function(uid, token, redirect, fn) { User.confirm = function(uid, token, redirect, fn) {
this.findById(uid, function(err, user) { this.findById(uid, function(err, user) {
if (err) { if (err) {
fn(err); fn(err);
@ -394,9 +392,9 @@ User.confirm = function(uid, token, redirect, fn) {
} }
} }
}); });
} };
/** /**
* Create a short lived acess token for temporary login. Allows users * Create a short lived acess token for temporary login. Allows users
* to change passwords if forgotten. * to change passwords if forgotten.
* *
@ -406,7 +404,7 @@ User.confirm = function(uid, token, redirect, fn) {
* @param {Error} err * @param {Error} err
*/ */
User.resetPassword = function(options, cb) { User.resetPassword = function(options, cb) {
var UserModel = this; var UserModel = this;
var ttl = UserModel.settings.resetPasswordTokenTTL || DEFAULT_RESET_PW_TTL; var ttl = UserModel.settings.resetPasswordTokenTTL || DEFAULT_RESET_PW_TTL;
@ -429,7 +427,7 @@ User.resetPassword = function(options, cb) {
user: user user: user
}); });
} }
}) });
} else { } else {
cb(); cb();
} }
@ -440,13 +438,13 @@ User.resetPassword = function(options, cb) {
cb(err); cb(err);
} }
} };
/*! /*!
* Setup an extended user model. * Setup an extended user model.
*/ */
User.setup = function() { User.setup = function() {
// We need to call the base class's setup method // We need to call the base class's setup method
User.base.setup.call(this); User.base.setup.call(this);
var UserModel = this; var UserModel = this;
@ -458,7 +456,7 @@ User.setup = function() {
UserModel.setter.password = function(plain) { UserModel.setter.password = function(plain) {
var salt = bcrypt.genSaltSync(this.constructor.settings.saltWorkFactor || SALT_WORK_FACTOR); var salt = bcrypt.genSaltSync(this.constructor.settings.saltWorkFactor || SALT_WORK_FACTOR);
this.$password = bcrypt.hashSync(plain, salt); this.$password = bcrypt.hashSync(plain, salt);
} };
// Make sure emailVerified is not set by creation // Make sure emailVerified is not set by creation
UserModel.beforeRemote('create', function(ctx, user, next) { UserModel.beforeRemote('create', function(ctx, user, next) {
@ -475,11 +473,14 @@ User.setup = function() {
description: 'Login a user with username/email and password', description: 'Login a user with username/email and password',
accepts: [ accepts: [
{arg: 'credentials', type: 'object', required: true, http: {source: 'body'}}, {arg: 'credentials', type: 'object', required: true, http: {source: 'body'}},
{arg: 'include', type: 'string', http: {source: 'query' }, description: 'Related objects to include in the response. ' + {arg: 'include', type: 'string', http: {source: 'query' },
description: 'Related objects to include in the response. ' +
'See the description of return value for more details.'} 'See the description of return value for more details.'}
], ],
returns: { returns: {
arg: 'accessToken', type: 'object', root: true, description: 'The response body contains properties of the AccessToken created on login.\n' + arg: 'accessToken', type: 'object', root: true,
description:
'The response body contains properties of the AccessToken created on login.\n' +
'Depending on the value of `include` parameter, the body may contain ' + 'Depending on the value of `include` parameter, the body may contain ' +
'additional properties:\n\n' + 'additional properties:\n\n' +
' - `user` - `{User}` - Data of the currently logged in user. (`include=user`)\n\n' ' - `user` - `{User}` - Data of the currently logged in user. (`include=user`)\n\n'
@ -551,7 +552,6 @@ User.setup = function() {
// email validation regex // email validation regex
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
UserModel.validatesFormatOf('email', {with: re, message: 'Must provide a valid email'}); UserModel.validatesFormatOf('email', {with: re, message: 'Must provide a valid email'});
// FIXME: We need to add support for uniqueness of composite keys in juggler // FIXME: We need to add support for uniqueness of composite keys in juggler
@ -561,12 +561,12 @@ User.setup = function() {
} }
return UserModel; return UserModel;
} };
/*! /*!
* Setup the base user. * Setup the base user.
*/ */
User.setup(); User.setup();
}; };