Merge branch 'release/1.6.1' into production
This commit is contained in:
commit
bdaaa7cb44
|
@ -17,7 +17,7 @@ var Model = require('../loopback').Model
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var properties = {
|
var properties = {
|
||||||
id: {type: String, generated: true, id: 1},
|
id: {type: String, id: true},
|
||||||
ttl: {type: Number, ttl: true, default: DEFAULT_TTL}, // time to live in seconds
|
ttl: {type: Number, ttl: true, default: DEFAULT_TTL}, // time to live in seconds
|
||||||
created: {type: Date, default: function() {
|
created: {type: Date, default: function() {
|
||||||
return new Date();
|
return new Date();
|
||||||
|
|
|
@ -18,6 +18,7 @@ var Model = require('../loopback').Model
|
||||||
, ACL = require('./acl').ACL
|
, ACL = require('./acl').ACL
|
||||||
, assert = require('assert');
|
, assert = require('assert');
|
||||||
|
|
||||||
|
var debug = require('debug')('loopback:user');
|
||||||
/**
|
/**
|
||||||
* Default User properties.
|
* Default User properties.
|
||||||
*/
|
*/
|
||||||
|
@ -127,10 +128,15 @@ var User = module.exports = Model.extend('User', properties, options);
|
||||||
* @param {AccessToken} token
|
* @param {AccessToken} token
|
||||||
*/
|
*/
|
||||||
|
|
||||||
User.login = function (credentials, fn) {
|
User.login = function (credentials, include, fn) {
|
||||||
var UserCtor = this;
|
if (typeof include === 'function') {
|
||||||
|
fn = include;
|
||||||
|
include = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
include = (include || '').toLowerCase();
|
||||||
|
|
||||||
var query = {};
|
var query = {};
|
||||||
|
|
||||||
if(credentials.email) {
|
if(credentials.email) {
|
||||||
query.email = credentials.email;
|
query.email = credentials.email;
|
||||||
} else if(credentials.username) {
|
} else if(credentials.username) {
|
||||||
|
@ -143,20 +149,36 @@ User.login = function (credentials, fn) {
|
||||||
var defaultError = new Error('login failed');
|
var defaultError = new Error('login failed');
|
||||||
|
|
||||||
if(err) {
|
if(err) {
|
||||||
|
debug('An error is reported from User.findOne: %j', err);
|
||||||
fn(defaultError);
|
fn(defaultError);
|
||||||
} else if(user) {
|
} else if(user) {
|
||||||
user.hasPassword(credentials.password, function(err, isMatch) {
|
user.hasPassword(credentials.password, function(err, isMatch) {
|
||||||
if(err) {
|
if(err) {
|
||||||
|
debug('An error is reported from User.hasPassword: %j', err);
|
||||||
fn(defaultError);
|
fn(defaultError);
|
||||||
} else if(isMatch) {
|
} else if(isMatch) {
|
||||||
user.accessTokens.create({
|
user.accessTokens.create({
|
||||||
ttl: Math.min(credentials.ttl || User.settings.ttl, User.settings.maxTTL)
|
ttl: Math.min(credentials.ttl || User.settings.ttl, User.settings.maxTTL)
|
||||||
}, fn);
|
}, function(err, token) {
|
||||||
|
if (err) return fn(err);
|
||||||
|
if (include === 'user') {
|
||||||
|
// NOTE(bajtos) We can't set token.user here:
|
||||||
|
// 1. token.user already exists, it's a function injected by
|
||||||
|
// "AccessToken belongsTo User" relation
|
||||||
|
// 2. ModelBaseClass.toJSON() ignores own properties, thus
|
||||||
|
// the value won't be included in the HTTP response
|
||||||
|
// See also loopback#161 and loopback#162
|
||||||
|
token.__data.user = user;
|
||||||
|
}
|
||||||
|
fn(err, token);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
|
debug('The password is invalid for user %s', query.email || query.username);
|
||||||
fn(defaultError);
|
fn(defaultError);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
debug('No matching record is found for user %s', query.email || query.username);
|
||||||
fn(defaultError);
|
fn(defaultError);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -386,9 +408,18 @@ User.setup = function () {
|
||||||
UserModel.login,
|
UserModel.login,
|
||||||
{
|
{
|
||||||
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. ' +
|
||||||
|
'See the description of return value for more details.'}
|
||||||
],
|
],
|
||||||
returns: {arg: 'accessToken', type: 'object', root: true},
|
returns: {
|
||||||
|
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 ' +
|
||||||
|
'additional properties:\n\n' +
|
||||||
|
' - `user` - `{User}` - Data of the currently logged in user. (`include=user`)\n\n'
|
||||||
|
},
|
||||||
http: {verb: 'post'}
|
http: {verb: 'post'}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -403,7 +434,10 @@ User.setup = function () {
|
||||||
var tokenID = accessToken && accessToken.id;
|
var tokenID = accessToken && accessToken.id;
|
||||||
|
|
||||||
return tokenID;
|
return tokenID;
|
||||||
}}
|
}, description:
|
||||||
|
'Do not supply this argument, it is automatically extracted ' +
|
||||||
|
'from request headers.'
|
||||||
|
}
|
||||||
],
|
],
|
||||||
http: {verb: 'all'}
|
http: {verb: 'all'}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
"Platform",
|
"Platform",
|
||||||
"mBaaS"
|
"mBaaS"
|
||||||
],
|
],
|
||||||
"version": "1.6.0",
|
"version": "1.6.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "mocha -R spec"
|
"test": "mocha -R spec"
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,6 +8,7 @@ var userMemory = loopback.createDataSource({
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('User', function(){
|
describe('User', function(){
|
||||||
|
var validCredentials = {email: 'foo@bar.com', password: 'bar'};
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
User = loopback.User.extend('user');
|
User = loopback.User.extend('user');
|
||||||
User.email = loopback.Email.extend('email');
|
User.email = loopback.Email.extend('email');
|
||||||
|
@ -25,7 +26,7 @@ describe('User', function(){
|
||||||
app.use(loopback.rest());
|
app.use(loopback.rest());
|
||||||
app.model(User);
|
app.model(User);
|
||||||
|
|
||||||
User.create({email: 'foo@bar.com', password: 'bar'}, done);
|
User.create(validCredentials, done);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function (done) {
|
afterEach(function (done) {
|
||||||
|
@ -105,7 +106,7 @@ describe('User', function(){
|
||||||
|
|
||||||
describe('User.login', function() {
|
describe('User.login', function() {
|
||||||
it('Login a user by providing credentials', function(done) {
|
it('Login a user by providing credentials', function(done) {
|
||||||
User.login({email: 'foo@bar.com', password: 'bar'}, function (err, accessToken) {
|
User.login(validCredentials, function (err, accessToken) {
|
||||||
assert(accessToken.userId);
|
assert(accessToken.userId);
|
||||||
assert(accessToken.id);
|
assert(accessToken.id);
|
||||||
assert.equal(accessToken.id.length, 64);
|
assert.equal(accessToken.id.length, 64);
|
||||||
|
@ -119,7 +120,7 @@ describe('User', function(){
|
||||||
.post('/users/login')
|
.post('/users/login')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.send({email: 'foo@bar.com', password: 'bar'})
|
.send(validCredentials)
|
||||||
.end(function(err, res){
|
.end(function(err, res){
|
||||||
if(err) return done(err);
|
if(err) return done(err);
|
||||||
var accessToken = res.body;
|
var accessToken = res.body;
|
||||||
|
@ -127,11 +128,28 @@ describe('User', function(){
|
||||||
assert(accessToken.userId);
|
assert(accessToken.userId);
|
||||||
assert(accessToken.id);
|
assert(accessToken.id);
|
||||||
assert.equal(accessToken.id.length, 64);
|
assert.equal(accessToken.id.length, 64);
|
||||||
|
assert(accessToken.user === undefined);
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Returns current user when `include` is `USER`', function(done) {
|
||||||
|
request(app)
|
||||||
|
.post('/users/login?include=USER')
|
||||||
|
.send(validCredentials)
|
||||||
|
.expect(200)
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.end(function(err, res) {
|
||||||
|
if (err) return done(err);
|
||||||
|
var token = res.body;
|
||||||
|
expect(token.user, 'body.user').to.not.equal(undefined);
|
||||||
|
expect(token.user, 'body.user')
|
||||||
|
.to.have.property('email', validCredentials.email);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Login should only allow correct credentials', function(done) {
|
it('Login should only allow correct credentials', function(done) {
|
||||||
User.create({email: 'foo22@bar.com', password: 'bar'}, function(user, err) {
|
User.create({email: 'foo22@bar.com', password: 'bar'}, function(user, err) {
|
||||||
User.login({email: 'foo44@bar.com', password: 'bar'}, function(err, accessToken) {
|
User.login({email: 'foo44@bar.com', password: 'bar'}, function(err, accessToken) {
|
||||||
|
|
Loading…
Reference in New Issue