diff --git a/spec/ValidationAndPasswordsReset.spec.js b/spec/ValidationAndPasswordsReset.spec.js index 62dab7f0..a5e02416 100644 --- a/spec/ValidationAndPasswordsReset.spec.js +++ b/spec/ValidationAndPasswordsReset.spec.js @@ -910,7 +910,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }); }); - it('deletes password reset token', done => { + it('deletes password reset token on email address change', done => { reconfigureServer({ appName: 'coolapp', publicServerURL: 'http://localhost:1337/1', @@ -929,13 +929,14 @@ describe('Custom Pages, Email Verification, Password Reset', () => { return user .signUp(null) .then(() => Parse.User.requestPasswordReset('test@parse.com')) - .then(() => config.database.adapter - .find( + .then(() => + config.database.adapter.find( '_User', { fields: {} }, { username: 'zxcv' }, { limit: 1 } - )) + ) + ) .then(results => { // validate that there is a token expect(results.length).toEqual(1); @@ -943,18 +944,19 @@ describe('Custom Pages, Email Verification, Password Reset', () => { user.set('email', 'test2@parse.com'); return user.save(); }) - .then(() => config.database.adapter - .find( + .then(() => + config.database.adapter.find( '_User', { fields: {} }, { username: 'zxcv' }, - { limit: 1 }) + { limit: 1 } + ) ) .then(results => { expect(results.length).toEqual(1); expect(results[0]['_perishable_token']).toBeUndefined(); done(); - }) + }); }) .catch(error => { fail(JSON.stringify(error)); diff --git a/src/Controllers/UserController.js b/src/Controllers/UserController.js index 05627c99..5970c435 100644 --- a/src/Controllers/UserController.js +++ b/src/Controllers/UserController.js @@ -9,6 +9,15 @@ var RestQuery = require('../RestQuery'); var Auth = require('../Auth'); export class UserController extends AdaptableController { + // Add token delete operations to a rest update object + static addClearPasswordResetTokenToRestObject(restObject) { + const addOps = { + _perishable_token: { __op: 'Delete' }, + _perishable_token_expires_at: { __op: 'Delete' }, + }; + return Object.assign({}, restObject, addOps); + } + constructor(adapter, appId, options = {}) { super(adapter, appId, options); } @@ -242,35 +251,17 @@ export class UserController extends AdaptableController { }); } - clearPasswordResetToken(objectId) { - return this.config.database.update( - '_User', - { objectId }, - { - _perishable_token: { __op: 'Delete' }, - _perishable_token_expires_at: { __op: 'Delete' }, - } - ) - } - updatePassword(username, token, password) { - return ( - this.checkResetTokenValidity(username, token) - .then(user => - Promise.all([ - updateUserPassword(user.objectId, password, this.config), - this.clearPasswordResetToken(user.objectId) - ])) - .then(results => results[0]) - .catch(error => { - if (error.message) { - // in case of Parse.Error, fail with the error message only - return Promise.reject(error.message); - } else { - return Promise.reject(error); - } - }) - ); + return this.checkResetTokenValidity(username, token) + .then(user => updateUserPassword(user.objectId, password, this.config)) + .catch(error => { + if (error.message) { + // in case of Parse.Error, fail with the error message only + return Promise.reject(error.message); + } else { + return Promise.reject(error); + } + }); } defaultVerificationEmail({ link, user, appName }) { @@ -314,9 +305,7 @@ function updateUserPassword(userId, password, config) { Auth.master(config), '_User', { objectId: userId }, - { - password: password, - } + UserController.addClearPasswordResetTokenToRestObject({ password }) ); } diff --git a/src/Routers/ClassesRouter.js b/src/Routers/ClassesRouter.js index 47392ba2..93ba78db 100644 --- a/src/Routers/ClassesRouter.js +++ b/src/Routers/ClassesRouter.js @@ -105,27 +105,29 @@ export class ClassesRouter extends PromiseRouter { ); } - afterUpdate(req, response) { - if (this.className(req) === '_User' && ('email' in req.body)) { - const userController = req.config.userController; - return userController.clearPasswordResetToken(req.params.objectId) - .then(() => - response - ); + // always clear password reset token on email address change + beforeUpdate(req) { + const { body } = req; + if (this.className(req) === '_User' && 'email' in body) { + const { userController } = req.config; + return userController.constructor.addClearPasswordResetTokenToRestObject( + body + ); } - return Promise.resolve(response); + return body; } handleUpdate(req) { + const body = this.beforeUpdate(req); const where = { objectId: req.params.objectId }; return rest.update( req.config, req.auth, this.className(req), where, - req.body, + body, req.info.clientSDK - ).then(this.afterUpdate.bind(this, req)); + ); } handleDelete(req) {