From 03cb2f0556f9b7fa6745b44513e22fe38f988534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Thu, 30 Jan 2014 14:35:49 +0100 Subject: [PATCH 1/2] Describe `access_token` param of `User.logout` Add an explicit note that clients are not supposed to send the `access_token` parameter, since it is extracted from request headers. --- lib/models/user.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/models/user.js b/lib/models/user.js index 4ad2fde7..cdaa8703 100644 --- a/lib/models/user.js +++ b/lib/models/user.js @@ -403,7 +403,10 @@ User.setup = function () { var tokenID = accessToken && accessToken.id; return tokenID; - }} + }, description: + 'Do not supply this argument, it is automatically extracted ' + + 'from request headers.' + } ], http: {verb: 'all'} } From d6f0b5f5a66869af97beb21e428e5d3f5b047c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Thu, 30 Jan 2014 14:33:45 +0100 Subject: [PATCH 2/2] Add `include=user` param to `User.login` Allow LB clients to get details of the currently logged-in user as part of the login response. Improve method's `description` to mention this new option. --- lib/models/user.js | 38 ++++++++++++++++++++++++++++++++------ test/user.test.js | 26 ++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/lib/models/user.js b/lib/models/user.js index cdaa8703..bdb656c0 100644 --- a/lib/models/user.js +++ b/lib/models/user.js @@ -127,10 +127,15 @@ var User = module.exports = Model.extend('User', properties, options); * @param {AccessToken} token */ -User.login = function (credentials, fn) { - var UserCtor = this; +User.login = function (credentials, include, fn) { + if (typeof include === 'function') { + fn = include; + include = undefined; + } + + include = (include || '').toLowerCase(); + var query = {}; - if(credentials.email) { query.email = credentials.email; } else if(credentials.username) { @@ -151,7 +156,19 @@ User.login = function (credentials, fn) { } else if(isMatch) { user.accessTokens.create({ 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 { fn(defaultError); } @@ -386,9 +403,18 @@ User.setup = function () { UserModel.login, { 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'} } ); diff --git a/test/user.test.js b/test/user.test.js index 20271335..9a056180 100644 --- a/test/user.test.js +++ b/test/user.test.js @@ -8,6 +8,7 @@ var userMemory = loopback.createDataSource({ }); describe('User', function(){ + var validCredentials = {email: 'foo@bar.com', password: 'bar'}; beforeEach(function() { User = loopback.User.extend('user'); User.email = loopback.Email.extend('email'); @@ -25,7 +26,7 @@ describe('User', function(){ app.use(loopback.rest()); app.model(User); - User.create({email: 'foo@bar.com', password: 'bar'}, done); + User.create(validCredentials, done); }); afterEach(function (done) { @@ -105,7 +106,7 @@ describe('User', function(){ describe('User.login', function() { 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.id); assert.equal(accessToken.id.length, 64); @@ -119,7 +120,7 @@ describe('User', function(){ .post('/users/login') .expect('Content-Type', /json/) .expect(200) - .send({email: 'foo@bar.com', password: 'bar'}) + .send(validCredentials) .end(function(err, res){ if(err) return done(err); var accessToken = res.body; @@ -127,11 +128,28 @@ describe('User', function(){ assert(accessToken.userId); assert(accessToken.id); assert.equal(accessToken.id.length, 64); + assert(accessToken.user === undefined); 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) { User.create({email: 'foo22@bar.com', password: 'bar'}, function(user, err) { User.login({email: 'foo44@bar.com', password: 'bar'}, function(err, accessToken) {