From 72e20be06dd65c2d7e4afae83fc2ef79ba806751 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sat, 11 Nov 2017 09:41:23 -0500 Subject: [PATCH] fix #3451 duplicate session upon login (#4337) * Adds failing test for #3451 (on multiple logins) * Factor sessionDestruction as part of Session creation flow in RestWrite * nits --- spec/ParseUser.spec.js | 30 ++++++++++++++++++++++++++++++ src/RestWrite.js | 32 ++++++++++++++++++++++++-------- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/spec/ParseUser.spec.js b/spec/ParseUser.spec.js index 9a6e2507e9..ab95768e83 100644 --- a/spec/ParseUser.spec.js +++ b/spec/ParseUser.spec.js @@ -3453,4 +3453,34 @@ describe('Parse.User testing', () => { done(); }); }); + + it('does not duplicate session when logging in multiple times #3451', (done) => { + const user = new Parse.User(); + user.signUp({ + username: 'yolo', + password: 'yolo', + email: 'yo@lo.com' + }).then(() => { + const promises = []; + while(promises.length != 5) { + Parse.User.logIn('yolo', 'yolo') + promises.push(Parse.User.logIn('yolo', 'yolo').then((res) => { + // ensure a new session token is generated at each login + expect(res.getSessionToken()).not.toBe(user.getSessionToken()); + })); + } + return Promise.all(promises); + }).then(() => { + // wait because session destruction is not synchronous + return new Promise((resolve) => { + setTimeout(resolve, 100); + }); + }).then(() => { + const query = new Parse.Query('_Session'); + return query.find({ useMasterKey: true }); + }).then((results) => { + // only one session in the end + expect(results.length).toBe(1); + }).then(done, done.fail); + }); }); diff --git a/src/RestWrite.js b/src/RestWrite.js index 61b8eea409..424284d5ba 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -81,6 +81,8 @@ RestWrite.prototype.execute = function() { return this.transformUser(); }).then(() => { return this.expandFilesForExistingObjects(); + }).then(() => { + return this.destroyDuplicatedSessions(); }).then(() => { return this.runDatabaseOperation(); }).then(() => { @@ -588,17 +590,31 @@ RestWrite.prototype.createSessionToken = function() { this.response.response.sessionToken = token; } + return new RestWrite(this.config, Auth.master(this.config), '_Session', null, sessionData).execute(); +} + +RestWrite.prototype.destroyDuplicatedSessions = function() { + // Only for _Session, and at creation time + if (this.className != '_Session' || this.query) { + return; + } // Destroy the sessions in 'Background' + const { + user, + installationId, + sessionToken, + } = this.data; + if (!user || !installationId) { + return; + } + if (!user.objectId) { + return; + } this.config.database.destroy('_Session', { - user: { - __type: 'Pointer', - className: '_User', - objectId: this.objectId() - }, - installationId: this.auth.installationId, - sessionToken: { '$ne': token }, + user, + installationId, + sessionToken: { '$ne': sessionToken }, }); - return new RestWrite(this.config, Auth.master(this.config), '_Session', null, sessionData).execute(); } // Handles any followup logic