diff --git a/services/web/app/src/Features/PasswordReset/PasswordResetController.js b/services/web/app/src/Features/PasswordReset/PasswordResetController.js index 44dc70ee65..87c10d258e 100644 --- a/services/web/app/src/Features/PasswordReset/PasswordResetController.js +++ b/services/web/app/src/Features/PasswordReset/PasswordResetController.js @@ -168,6 +168,9 @@ async function renderSetPasswordForm(req, res, next) { return res.redirect('/user/password/set' + emailQuery) } catch (err) { + if (err.name === 'ForbiddenError') { + return next(err) + } return res.redirect('/user/password/reset?error=token_expired') } } diff --git a/services/web/app/src/Features/PasswordReset/PasswordResetHandler.js b/services/web/app/src/Features/PasswordReset/PasswordResetHandler.js index 07c59a2f0e..dbc2cf79a1 100644 --- a/services/web/app/src/Features/PasswordReset/PasswordResetHandler.js +++ b/services/web/app/src/Features/PasswordReset/PasswordResetHandler.js @@ -71,6 +71,9 @@ async function getUserForPasswordResetToken(token) { 'overleaf.id': 1, email: 1, }) + + await checkUserPermissions(user, ['change-password']) + if (user == null) { return { user: null, remainingPeeks: 0 } } else if (data.user_id != null && data.user_id === user._id.toString()) { diff --git a/services/web/test/unit/src/PasswordReset/PasswordResetHandlerTests.js b/services/web/test/unit/src/PasswordReset/PasswordResetHandlerTests.js index 0a4801b188..4dc9583ddc 100644 --- a/services/web/test/unit/src/PasswordReset/PasswordResetHandlerTests.js +++ b/services/web/test/unit/src/PasswordReset/PasswordResetHandlerTests.js @@ -45,6 +45,11 @@ describe('PasswordResetHandler', function () { '../Email/EmailHandler': this.EmailHandler, '../Authentication/AuthenticationManager': this.AuthenticationManager, '@overleaf/settings': this.settings, + '../Authorization/PermissionsManager': (this.PermissionsManager = { + promises: { + checkUserPermissions: sinon.stub(), + }, + }), }, }) this.token = '12312321i' @@ -512,4 +517,46 @@ describe('PasswordResetHandler', function () { }) }) }) + + describe('getUserForPasswordResetToken', function () { + beforeEach(function () { + this.OneTimeTokenHandler.promises.peekValueFromToken.resolves({ + data: { + user_id: this.user._id, + email: this.email, + }, + remainingPeeks: 1, + }) + + this.UserGetter.promises.getUserByMainEmail.resolves({ + _id: this.user._id, + email: this.email, + }) + }) + + it('should returns errors from user permissions', async function () { + let error + const err = new Error('nope') + this.PermissionsManager.promises.checkUserPermissions.rejects(err) + try { + await this.PasswordResetHandler.promises.getUserForPasswordResetToken( + 'abc123' + ) + } catch (e) { + error = e + } + expect(error).to.deep.equal(error) + }) + + it('returns user when user has permissions and remaining peaks', async function () { + const result = + await this.PasswordResetHandler.promises.getUserForPasswordResetToken( + 'abc123' + ) + expect(result).to.deep.equal({ + user: { _id: this.user._id, email: this.email }, + remainingPeeks: 1, + }) + }) + }) })