From 24bb15233d763a50ee42aaf5c2bf22ea205fb41d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Fri, 13 Jan 2017 10:40:48 +0100 Subject: [PATCH 1/2] Clean up access-token-invalidation tests --- test/user.test.js | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/test/user.test.js b/test/user.test.js index 5a8e3bf7..165a7799 100644 --- a/test/user.test.js +++ b/test/user.test.js @@ -2000,7 +2000,7 @@ describe('User', function() { User.login(currentEmailCredentials, function(err, accessToken1) { if (err) return next(err); assert(accessToken1.userId); - originalUserToken1 = accessToken1.id; + originalUserToken1 = accessToken1; next(); }); }, @@ -2008,7 +2008,7 @@ describe('User', function() { User.login(currentEmailCredentials, function(err, accessToken2) { if (err) return next(err); assert(accessToken2.userId); - originalUserToken2 = accessToken2.id; + originalUserToken2 = accessToken2; next(); }); }, @@ -2068,7 +2068,7 @@ describe('User', function() { it('keeps sessions AS IS if firstName is added using `updateAttributes`', function(done) { user.updateAttributes({'firstName': 'Janny'}, function(err, userInstance) { if (err) return done(err); - assertUntouchedTokens(done); + assertPreservedTokens(done); }); }); @@ -2079,7 +2079,7 @@ describe('User', function() { email: currentEmailCredentials.email, }, function(err, userInstance) { if (err) return done(err); - assertUntouchedTokens(done); + assertPreservedTokens(done); }); }); @@ -2314,9 +2314,11 @@ describe('User', function() { function assertPreservedTokens(done) { AccessToken.find({where: {userId: user.id}}, function(err, tokens) { if (err) return done(err); - expect(tokens.length).to.equal(2); - expect([tokens[0].id, tokens[1].id]).to.have.members([originalUserToken1, - originalUserToken2]); + var actualIds = tokens.map(function(t) { return t.id; }); + actualIds.sort(); + var expectedIds = [originalUserToken1.id, originalUserToken2.id]; + expectedIds.sort(); + expect(actualIds).to.eql(expectedIds); done(); }); }; @@ -2328,14 +2330,6 @@ describe('User', function() { done(); }); } - - function assertUntouchedTokens(done) { - AccessToken.find({where: {userId: user.id}}, function(err, tokens) { - if (err) return done(err); - expect(tokens.length).to.equal(2); - done(); - }); - } }); describe('Verification after updating email', function() { From e17cc3d23a9fe40efe8516bf1d18d6ee1d1977cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Fri, 13 Jan 2017 11:03:06 +0100 Subject: [PATCH 2/2] Preserve current session when invalidating tokens Fix User model to preserve the current session (provided via "options.accessToken") when invalidating access tokens after a change of email or password property. --- common/models/user.js | 18 +++++++++++++++--- test/helpers/loopback-testing-helper.js | 1 + test/user.integration.js | 14 ++++++++++++++ test/user.test.js | 13 +++++++++++++ 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/common/models/user.js b/common/models/user.js index ca1f8fd8..0914802a 100644 --- a/common/models/user.js +++ b/common/models/user.js @@ -657,7 +657,12 @@ module.exports = function(User) { throw err; }; - User._invalidateAccessTokensOfUsers = function(userIds, cb) { + User._invalidateAccessTokensOfUsers = function(userIds, options, cb) { + if (typeof options === 'function' && cb === undefined) { + cb = options; + options = {}; + } + if (!Array.isArray(userIds) || !userIds.length) return process.nextTick(cb); @@ -666,7 +671,14 @@ module.exports = function(User) { return process.nextTick(cb); var AccessToken = accessTokenRelation.modelTo; - AccessToken.deleteAll({userId: {inq: userIds}}, cb); + + var query = {userId: {inq: userIds}}; + var tokenPK = AccessToken.definition.idName() || 'id'; + if (options.accessToken && tokenPK in options.accessToken) { + query[tokenPK] = {neq: options.accessToken[tokenPK]}; + } + + AccessToken.deleteAll(query, options, cb); }; /*! @@ -893,7 +905,7 @@ module.exports = function(User) { }).map(function(u) { return u.id; }); - ctx.Model._invalidateAccessTokensOfUsers(userIdsToExpire, next); + ctx.Model._invalidateAccessTokensOfUsers(userIdsToExpire, ctx.options, next); }); }; diff --git a/test/helpers/loopback-testing-helper.js b/test/helpers/loopback-testing-helper.js index 4645b1e8..8453f554 100644 --- a/test/helpers/loopback-testing-helper.js +++ b/test/helpers/loopback-testing-helper.js @@ -34,6 +34,7 @@ _beforeEach.withApp = function(app) { this.get = _request.get; this.put = _request.put; this.del = _request.del; + this.patch = _request.patch; if (app.booting) { return app.once('booted', done); diff --git a/test/user.integration.js b/test/user.integration.js index ae327250..3dfcaecc 100644 --- a/test/user.integration.js +++ b/test/user.integration.js @@ -92,6 +92,20 @@ describe('users - integration', function() { done(); }); }); + + it('should preserve current session when invalidating tokens', function(done) { + var url = '/api/users/' + userId; + var self = this; + this.patch(url) + .send({email: 'new@example.com'}) + .set('Authorization', accessToken) + .expect(200, function(err) { + if (err) return done(err); + self.get(url) + .set('Authorization', accessToken) + .expect(200, done); + }); + }); }); describe('sub-user', function() { diff --git a/test/user.test.js b/test/user.test.js index 165a7799..5a0cfad1 100644 --- a/test/user.test.js +++ b/test/user.test.js @@ -2263,6 +2263,19 @@ describe('User', function() { }); }); + it('preserves current session', function(done) { + var options = {accessToken: originalUserToken1}; + user.updateAttribute('email', 'new@example.com', options, function(err) { + if (err) return done(err); + AccessToken.find({where: {userId: user.id}}, function(err, tokens) { + if (err) return done(err); + var tokenIds = tokens.map(function(t) { return t.id; }); + expect(tokenIds).to.eql([originalUserToken1.id]); + done(); + }); + }); + }); + it('preserves other user sessions if their password is untouched', function(done) { var user1, user2, user1Token; async.series([