diff --git a/config/routes/auth.js b/config/routes/auth.js index 15f57f7..9f00565 100755 --- a/config/routes/auth.js +++ b/config/routes/auth.js @@ -80,14 +80,14 @@ module.exports = (app, passport) => { subject: 'Complete your Tracman registration', text: mail.text( `Welcome to Tracman! \n\nTo complete your registration, follow \ - this link and set your password:\n${env.url}/settings/password/${token}\n\n\ + this link and set your password:\n${env.url}/account/password/${token}\n\n\ This link will expire at ${expiration_time_string}. ` ), html: mail.html( `

Welcome to Tracman!

To complete your registration, \ follow this link and set your password:\ -
\ - ${env.url}/settings/password/${token}

\ +
\ + ${env.url}/account/password/${token}

\

This link will expire at ${expiration_time_string}.

` ) }) @@ -291,14 +291,14 @@ module.exports = (app, passport) => { text: mail.text( `Hi, \n\nDid you request to reset your Tracman password? \ If so, follow this link to do so:\ - \n${env.url}/settings/password/${token}\n\n\ + \n${env.url}/account/password/${token}\n\n\ If you didn't initiate this request, just ignore this email. ` ), html: mail.html( `

Hi,

Did you request to reset your Tracman password? \ If so, follow this link to do so:
\ - \ - ${env.url}/settings/password/${token}

\ + \ + ${env.url}/account/password/${token}

\

If you didn't initiate this request, just ignore this email.

` ) }) @@ -355,7 +355,7 @@ module.exports = (app, passport) => { if (!req.user.auth.password && service === 'google') { req.flash( 'warning', - `Hey, you need to set a password \ + `Hey, you need to set a password \ before you can disconnect your google account. Otherwise, you \ won't be able to log in! ` ) diff --git a/config/routes/settings.js b/config/routes/settings.js index 89e3b3d..fabf379 100755 --- a/config/routes/settings.js +++ b/config/routes/settings.js @@ -2,8 +2,6 @@ const slug = require('slug') const xss = require('xss') -const zxcvbn = require('zxcvbn') -const moment = require('moment') const mw = require('../middleware.js') const User = require('../models.js').user const mail = require('../mail.js') @@ -65,14 +63,14 @@ router.route('/') text: mail.text( `A request has been made to change your Tracman email address. \ If you did not initiate this request, please disregard it. \n\n\ - To confirm your email, follow this link:\n${env.url}/settings/email/${token}. ` + To confirm your email, follow this link:\n${env.url}/account/email/${token}. ` ), html: mail.html( `

A request has been made to change your Tracman email address. \ If you did not initiate this request, please disregard it.

\

To confirm your email, follow this link:\ -
\ - ${env.url}/settings/email/${token}.

` +
\ + ${env.url}/account/email/${token}.

` ) }) @@ -142,6 +140,7 @@ router.route('/') finally { res.redirect('/settings') } }) + // Delete account router.get('/delete', async (req, res) => { try { @@ -154,160 +153,6 @@ router.get('/delete', async (req, res) => { } }) -// Confirm email address -router.get('/email/:token', mw.ensureAuth, async (req, res, next) => { - // Check token - if (req.user.emailToken === req.params.token) { - try { - // Set new email - req.user.email = req.user.newEmail - - // Delete token and newEmail - req.user.emailToken = undefined - req.user.newEmail = undefined - - await req.user.save() - - // Report success - req.flash('success', `Your email has been set to ${req.user.email}. `) - res.redirect('/settings') - - } catch (err) { - mw.throwErr(err, req) - res.redirect('/settings') - } - - // Invalid token - } else { - req.flash('danger', 'Email confirmation token is invalid. ') - res.redirect('/settings') - } -}) - -// Set password -router.route('/password') - .all(mw.ensureAuth, (req, res, next) => { - next() - }) - - // Email user a token, proceed at /password/:token - .get( async (req, res, next) => { - // Create token for password change - try { - let [token, expires] = await req.user.createPassToken() - // Figure out expiration time - let expirationTimeString = (req.query.tz) - ? moment(expires).utcOffset(req.query.tz).toDate().toLocaleTimeString(req.acceptsLanguages[0]) - : moment(expires).toDate().toLocaleTimeString(req.acceptsLanguages[0]) + ' UTC' - - // Confirm password change request by email. - return await mail.send({ - to: mail.to(req.user), - from: mail.noReply, - subject: 'Request to change your Tracman password', - text: mail.text( - `A request has been made to change your tracman password. \ - If you did not initiate this request, please contact support at keith@tracman.org. \ - \n\nTo change your password, follow this link:\n\ - ${env.url}/settings/password/${token}. \n\n\ - This request will expire at ${expirationTimeString}. ` - ), - html: mail.html( - `

A request has been made to change your tracman password. \ - If you did not initiate this request, please contact support at \ - keith@tracman.org.

\ -

To change your password, follow this link:\ -
\ - ${env.url}/settings/password/${token}.

\ -

This request will expire at ${expirationTimeString}.

` - ) - }) - - // Alert user to check email. - req.flash('success', - `An link has been sent to ${req.user.email}. \ - Click on the link to complete your password change. \ - This link will expire in one hour (${expirationTimeString}). ` - ) - } catch (err) { - mw.throwErr(err, req) - } finally { - res.redirect((req.user) ? '/settings' : '/login') - } - }) - -router.route('/password/:token') - - // Check token - .all( async (req, res, next) => { - debug('/settings/password/:token .all() called') - try { - let user = await User - .findOne({'auth.passToken': req.params.token}) - .where('auth.passTokenExpires').gt(Date.now()) - - if (!user) { - debug('Bad token') - req.flash('danger', 'Password reset token is invalid or has expired. ') - res.redirect((req.isAuthenticated) ? '/settings' : '/login') - } else { - debug('setting passwordUser') - res.locals.passwordUser = user - next() - } - - } catch (err) { - mw.throwErr(err, req) - res.redirect('/password') - } - - }) - - // Show password change form - .get((req, res) => { - debug('/settings/password/:token .get() called') - res.render('password') - }) - - // Set new password - .post( async (req, res, next) => { - debug('/settings/password/:token .post() called') - - // Validate password strength - let zxcvbnResult = zxcvbn(req.body.password) - if (zxcvbnResult.crack_times_seconds.online_no_throttling_10_per_second < 864000) { // Less than ten days - req.flash( 'danger', - `That password could be cracked in ${zxcvbnResult.crack_times_display.online_no_throttling_10_per_second}! Come up with a more complex password that would take at least 10 days to crack. ` - ) - res.redirect(`/settings/password/${req.params.token}`) - } else { - - // Create hashed password and save to db - try { - await res.locals.passwordUser.generateHashedPassword(req.body.password) - - // User changed password - if (req.user) { - debug('User saved password') - req.flash('success', 'Your password has been changed. ') - res.redirect('/settings') - - // New user created password - } else { - debug('New user created password') - req.flash('success', 'Password set. You can use it to log in now. ') - res.redirect('/login') - } - - } catch (err) { - debug('Error creating hashed password and saving to db') - mw.throwErr(err, req) - res.redirect(`/settings/password/${req.params.token}`) - } - - } - }) - // Tracman pro router.route('/pro') .all(mw.ensureAuth, (req, res, next) => { @@ -322,7 +167,7 @@ router.route('/pro') // Join Tracman pro .post( async (req, res) => { try { - let user = await User.findByIdAndUpdate(req.user.id, + await User.findByIdAndUpdate(req.user.id, {$set: { isPro: true }}) req.flash('success', 'You have been signed up for pro. ') res.redirect('/settings') @@ -332,4 +177,18 @@ router.route('/pro') } }) +// Redirects for URLs that moved to /account +router.route('/password') + .all((req,res)=>{ + res.redirect(307, '/account/password') + }) +router.route('/password/:token') + .all((req,res)=>{ + res.redirect(307, `/account/password/${req.params.token}`) + }) +router.route('/email/:token') + .all((req,res)=>{ + res.redirect(307, `/account/email/${req.params.token}`) + }) + module.exports = router diff --git a/server.js b/server.js index 6ca111c..564bb53 100755 --- a/server.js +++ b/server.js @@ -104,6 +104,9 @@ let ready_promise_list = [] // Settings app.use('/settings', require('./config/routes/settings.js')) + + // Account settings + app.use('/account', require('./config/routes/account.js')) // Map app.use(['/map', '/trac'], require('./config/routes/map.js')) diff --git a/static/js/settings.js b/static/js/settings.js index 212c256..4a51421 100755 --- a/static/js/settings.js +++ b/static/js/settings.js @@ -21,7 +21,7 @@ $(function () { var slugNotUnique, emailNotUnique // Set timezone in password change link - $('#password').attr('href', '/settings/password?tz=' + new Date().getTimezoneOffset()) + $('#password').attr('href', '/account/password?tz=' + new Date().getTimezoneOffset()) // Delete account $('#delete').click(function () { diff --git a/test/auth.js b/test/auth.js index 125007d..4b6d779 100755 --- a/test/auth.js +++ b/test/auth.js @@ -89,15 +89,15 @@ describe('Authentication', () => { it('Loads password page', async () => { // Load password page chai.expect(await request - .get(`/settings/password/${passwordless_user.auth.passToken}`) + .get(`/account/password/${passwordless_user.auth.passToken}`) ).html.to.have.status(200) }) it('Fails to set a weak password', async () => { chai.expect( await request - .post(`/settings/password/${passwordless_user.auth.passToken}`) + .post(`/account/password/${passwordless_user.auth.passToken}`) .type('form').send({ 'password':BAD_PASSWORD }) - ).to.redirectTo(`/settings/password/${passwordless_user.auth.passToken}`) + ).to.redirectTo(`/account/password/${passwordless_user.auth.passToken}`) }) it('Sets a strong password', async () => { @@ -105,7 +105,7 @@ describe('Authentication', () => { // Perform request let res = await request - .post(`/settings/password/${passwordless_user.auth.passToken}`) + .post(`/account/password/${passwordless_user.auth.passToken}`) .type('form').send({ 'password':TEST_PASSWORD }) // Expect redirect diff --git a/views/settings.html b/views/settings.html index 6107e60..a292eb1 100755 --- a/views/settings.html +++ b/views/settings.html @@ -50,7 +50,7 @@
- {% if user.auth.password %}Change{% else %}Set{% endif %} password + {% if user.auth.password %}Change{% else %}Set{% endif %} password Delete account