From e29a2a97169176703e8f43f418fc9f0cf6d26a0f Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Thu, 2 Jun 2016 15:23:49 -0700 Subject: [PATCH] feature(email): add ability to verify current email --- common/models/user.js | 36 ++++++++++++++++++++++---- common/models/user.json | 3 +++ server/views/account/update-email.jade | 3 +-- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/common/models/user.js b/common/models/user.js index f75dea051a..cb3ec80932 100644 --- a/common/models/user.js +++ b/common/models/user.js @@ -325,19 +325,42 @@ module.exports = function(User) { ); User.prototype.updateEmail = function updateEmail(email) { + const fiveMinutesAgo = moment().subtract(5, 'minutes'); + const lastEmailSentAt = moment(new Date(this.emailVerifyTTL || null)); + const ownEmail = email === this.email; + const isWaitPeriodOver = this.emailVerifyTTL ? + lastEmailSentAt.isBefore(fiveMinutesAgo) : + true; + if (!isEmail(email)) { return Promise.reject( - new Error('The submitted email not valid') + new Error('The submitted email not valid.') ); } - if (this.email && this.email === email) { + // email is already associated and verified with this account + if (ownEmail && this.emailVerified) { return Promise.reject(new Error( `${email} is already associated with this account.` )); } + + if (ownEmail && !isWaitPeriodOver) { + const minutesLeft = 5 - + (moment().minutes() - lastEmailSentAt.minutes()); + + const timeToWait = minutesLeft ? + `${minutesLeft} minute${minutesLeft > 1 ? 's' : ''}` : + 'a few seconds'; + + return Promise.reject(new Error( + `Please wait ${timeToWait} to resend email verification.` + )); + } + return User.doesExist(null, email) .then(exists => { - if (exists) { + // not associated with this account, but is associated with another + if (!ownEmail && exists) { return Promise.reject( new Error(`${email} is already associated with another account.`) ); @@ -345,11 +368,14 @@ module.exports = function(User) { const emailVerified = false; return this.update$({ - email, emailVerified + email, + emailVerified, + emailVerifyTTL: new Date() }) .do(() => { this.email = email; this.emailVerified = emailVerified; + this.emailVerifyTTL = new Date(); }) .flatMap(() => { var mailOptions = { @@ -379,7 +405,7 @@ module.exports = function(User) { .catch(error => { debug(error); return Observable.throw( - 'Oops, something went wrong, please try again later' + 'Oops, something went wrong, please try again later.' ); }) .toPromise(); diff --git a/common/models/user.json b/common/models/user.json index cdbff24c58..225f58a78c 100644 --- a/common/models/user.json +++ b/common/models/user.json @@ -16,6 +16,9 @@ } } }, + "emailVerifyTTL": { + "type": "date" + }, "password": { "type": "string" }, diff --git a/server/views/account/update-email.jade b/server/views/account/update-email.jade index c7dce563aa..cb45c9d937 100644 --- a/server/views/account/update-email.jade +++ b/server/views/account/update-email.jade @@ -15,8 +15,7 @@ block content .form-group input.input-lg.form-control(type='email', name='email', id='email', value=user.email || '', placeholder=user.email || 'Enter your new email', autofocus, required, autocomplete="off") .form-group - button.btn.btn-lg.btn-primary.btn-block(type='submit') - | Update my Email + button.btn.btn-lg.btn-primary.btn-block(type='submit')= !user.email || user.emailVerified ? 'Update my Email' : 'Verify Email' a.btn.btn-lg.btn-block.btn-primary.btn-link-social(href='/settings') | Go back to Settings