From 7fcee42be9a0299b4367c43c54ab27b5dc4bfbda Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 4 Aug 2022 16:51:53 -0400 Subject: [PATCH] feat: present a password challenge on email update flow --- public/language/en-GB/user.json | 3 ++- src/user/interstitials.js | 19 ++++++++++++++++--- src/views/partials/email_update.tpl | 10 +++++++++- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/public/language/en-GB/user.json b/public/language/en-GB/user.json index 14f68b35db..c5bebf4b37 100644 --- a/public/language/en-GB/user.json +++ b/public/language/en-GB/user.json @@ -216,5 +216,6 @@ "emailUpdate.intro": "Please enter your email address below. This forum uses your email address for scheduled digest and notifications, as well as for account recovery in the event of a lost password.", "emailUpdate.optional": "This field is optional. You are not obligated to provide your email address, but without a validated email you will not be able to recover your account or login with your email.", "emailUpdate.required": "This field is required.", - "emailUpdate.change-instructions": "A confirmation email will be sent to the entered email address with a unique link. Accessing that link will confirm your ownership of the email address and it will become active on your account. At any time, you are able to update your email on file from within your account page." + "emailUpdate.change-instructions": "A confirmation email will be sent to the entered email address with a unique link. Accessing that link will confirm your ownership of the email address and it will become active on your account. At any time, you are able to update your email on file from within your account page.", + "emailUpdate.password-challenge": "Please enter your password in order to verify account ownership." } diff --git a/src/user/interstitials.js b/src/user/interstitials.js index 6f641336fa..9674ac3144 100644 --- a/src/user/interstitials.js +++ b/src/user/interstitials.js @@ -1,6 +1,7 @@ 'use strict'; const winston = require('winston'); +const util = require('util'); const user = require('.'); const db = require('../database'); @@ -9,6 +10,8 @@ const privileges = require('../privileges'); const plugins = require('../plugins'); const utils = require('../utils'); +const sleep = util.promisify(setTimeout); + const Interstitials = module.exports; Interstitials.email = async (data) => { @@ -19,6 +22,7 @@ Interstitials.email = async (data) => { return data; } + const isAdminOrGlobalMod = await user.isAdminOrGlobalMod(data.req.uid); let email; if (data.userData.uid) { email = await user.getUserField(data.userData.uid, 'email'); @@ -29,12 +33,13 @@ Interstitials.email = async (data) => { data: { email, requireEmailAddress: meta.config.requireEmailAddress, + update: !!data.userData.uid, }, callback: async (userData, formData) => { // Validate and send email confirmation if (userData.uid) { - const [isAdminOrGlobalMod, canEdit, current, { allowed, error }] = await Promise.all([ - user.isAdminOrGlobalMod(data.req.uid), + const [isPasswordCorrect, canEdit, current, { allowed, error }] = await Promise.all([ + user.isPasswordCorrect(userData.uid, formData.password, data.req.ip), privileges.users.canEdit(data.req.uid, userData.uid), user.getUserField(userData.uid, 'email'), plugins.hooks.fire('filter:user.saveEmail', { @@ -46,6 +51,10 @@ Interstitials.email = async (data) => { }), ]); + if (!isAdminOrGlobalMod && !isPasswordCorrect) { + await sleep(2000); + } + if (formData.email && formData.email.length) { if (!allowed || !utils.isEmailValid(formData.email)) { throw new Error(error); @@ -60,6 +69,10 @@ Interstitials.email = async (data) => { await user.setUserField(userData.uid, 'email', formData.email); await user.email.confirmByUid(userData.uid); } else if (canEdit) { + if (!isPasswordCorrect) { + throw new Error('[[error:invalid-password]]'); + } + await user.email.sendValidationEmail(userData.uid, { email: formData.email, force: true, @@ -76,7 +89,7 @@ Interstitials.email = async (data) => { throw new Error('[[error:invalid-email]]'); } - if (current) { + if (current.length && (isPasswordCorrect || isAdminOrGlobalMod)) { // User explicitly clearing their email await user.email.remove(userData.uid, data.req.session.id); } diff --git a/src/views/partials/email_update.tpl b/src/views/partials/email_update.tpl index 96444dc49c..bdf70d6ac0 100644 --- a/src/views/partials/email_update.tpl +++ b/src/views/partials/email_update.tpl @@ -8,6 +8,14 @@
+

[[user:emailUpdate.change-instructions]]

-

[[user:emailUpdate.change-instructions]]

+ + {{{ if update }}} +
+ + +

[[user:emailUpdate.password-challenge]]

+
+ {{{ end }}} \ No newline at end of file