#120 Moved some stuff from config/routes/settings.js to config/routes/account.js
parent
488f176774
commit
25bbd5536d
|
@ -80,14 +80,14 @@ module.exports = (app, passport) => {
|
||||||
subject: 'Complete your Tracman registration',
|
subject: 'Complete your Tracman registration',
|
||||||
text: mail.text(
|
text: mail.text(
|
||||||
`Welcome to Tracman! \n\nTo complete your registration, follow \
|
`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}. `
|
This link will expire at ${expiration_time_string}. `
|
||||||
),
|
),
|
||||||
html: mail.html(
|
html: mail.html(
|
||||||
`<p>Welcome to Tracman! </p><p>To complete your registration, \
|
`<p>Welcome to Tracman! </p><p>To complete your registration, \
|
||||||
follow this link and set your password:\
|
follow this link and set your password:\
|
||||||
<br><a href="${env.url}/settings/password/${token}">\
|
<br><a href="${env.url}/account/password/${token}">\
|
||||||
${env.url}/settings/password/${token}</a></p>\
|
${env.url}/account/password/${token}</a></p>\
|
||||||
<p>This link will expire at ${expiration_time_string}. </p>`
|
<p>This link will expire at ${expiration_time_string}. </p>`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -291,14 +291,14 @@ module.exports = (app, passport) => {
|
||||||
text: mail.text(
|
text: mail.text(
|
||||||
`Hi, \n\nDid you request to reset your Tracman password? \
|
`Hi, \n\nDid you request to reset your Tracman password? \
|
||||||
If so, follow this link to do so:\
|
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. `
|
If you didn't initiate this request, just ignore this email. `
|
||||||
),
|
),
|
||||||
html: mail.html(
|
html: mail.html(
|
||||||
`<p>Hi, </p><p>Did you request to reset your Tracman password? \
|
`<p>Hi, </p><p>Did you request to reset your Tracman password? \
|
||||||
If so, follow this link to do so:<br>\
|
If so, follow this link to do so:<br>\
|
||||||
<a href="${env.url}/settings/password/${token}">\
|
<a href="${env.url}/account/password/${token}">\
|
||||||
${env.url}/settings/password/${token}</a></p>\
|
${env.url}/account/password/${token}</a></p>\
|
||||||
<p>If you didn't initiate this request, just ignore this email. </p>`
|
<p>If you didn't initiate this request, just ignore this email. </p>`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -355,7 +355,7 @@ module.exports = (app, passport) => {
|
||||||
if (!req.user.auth.password && service === 'google') {
|
if (!req.user.auth.password && service === 'google') {
|
||||||
req.flash(
|
req.flash(
|
||||||
'warning',
|
'warning',
|
||||||
`Hey, you need to <a href="/settings/password">set a password</a> \
|
`Hey, you need to <a href="/account/password">set a password</a> \
|
||||||
before you can disconnect your google account. Otherwise, you \
|
before you can disconnect your google account. Otherwise, you \
|
||||||
won't be able to log in! `
|
won't be able to log in! `
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
const slug = require('slug')
|
const slug = require('slug')
|
||||||
const xss = require('xss')
|
const xss = require('xss')
|
||||||
const zxcvbn = require('zxcvbn')
|
|
||||||
const moment = require('moment')
|
|
||||||
const mw = require('../middleware.js')
|
const mw = require('../middleware.js')
|
||||||
const User = require('../models.js').user
|
const User = require('../models.js').user
|
||||||
const mail = require('../mail.js')
|
const mail = require('../mail.js')
|
||||||
|
@ -65,14 +63,14 @@ router.route('/')
|
||||||
text: mail.text(
|
text: mail.text(
|
||||||
`A request has been made to change your Tracman email address. \
|
`A request has been made to change your Tracman email address. \
|
||||||
If you did not initiate this request, please disregard it. \n\n\
|
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(
|
html: mail.html(
|
||||||
`<p>A request has been made to change your Tracman email address. \
|
`<p>A request has been made to change your Tracman email address. \
|
||||||
If you did not initiate this request, please disregard it. </p>\
|
If you did not initiate this request, please disregard it. </p>\
|
||||||
<p>To confirm your email, follow this link:\
|
<p>To confirm your email, follow this link:\
|
||||||
<br><a href="${env.url}/settings/email/${token}">\
|
<br><a href="${env.url}/account/email/${token}">\
|
||||||
${env.url}/settings/email/${token}</a>. </p>`
|
${env.url}/account/email/${token}</a>. </p>`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -142,6 +140,7 @@ router.route('/')
|
||||||
finally { res.redirect('/settings') }
|
finally { res.redirect('/settings') }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// Delete account
|
// Delete account
|
||||||
router.get('/delete', async (req, res) => {
|
router.get('/delete', async (req, res) => {
|
||||||
try {
|
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 <u>${req.user.email}</u>. `)
|
|
||||||
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(
|
|
||||||
`<p>A request has been made to change your tracman password. \
|
|
||||||
If you did not initiate this request, please contact support at \
|
|
||||||
<a href="mailto:keith@tracman.org">keith@tracman.org</a>. </p>\
|
|
||||||
<p>To change your password, follow this link:\
|
|
||||||
<br><a href="${env.url}/settings/password/${token}">\
|
|
||||||
${env.url}/settings/password/${token}</a>. </p>\
|
|
||||||
<p>This request will expire at ${expirationTimeString}. </p>`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Alert user to check email.
|
|
||||||
req.flash('success',
|
|
||||||
`An link has been sent to <u>${req.user.email}</u>. \
|
|
||||||
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
|
// Tracman pro
|
||||||
router.route('/pro')
|
router.route('/pro')
|
||||||
.all(mw.ensureAuth, (req, res, next) => {
|
.all(mw.ensureAuth, (req, res, next) => {
|
||||||
|
@ -322,7 +167,7 @@ router.route('/pro')
|
||||||
// Join Tracman pro
|
// Join Tracman pro
|
||||||
.post( async (req, res) => {
|
.post( async (req, res) => {
|
||||||
try {
|
try {
|
||||||
let user = await User.findByIdAndUpdate(req.user.id,
|
await User.findByIdAndUpdate(req.user.id,
|
||||||
{$set: { isPro: true }})
|
{$set: { isPro: true }})
|
||||||
req.flash('success', 'You have been signed up for pro. ')
|
req.flash('success', 'You have been signed up for pro. ')
|
||||||
res.redirect('/settings')
|
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
|
module.exports = router
|
||||||
|
|
|
@ -104,6 +104,9 @@ let ready_promise_list = []
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
app.use('/settings', require('./config/routes/settings.js'))
|
app.use('/settings', require('./config/routes/settings.js'))
|
||||||
|
|
||||||
|
// Account settings
|
||||||
|
app.use('/account', require('./config/routes/account.js'))
|
||||||
|
|
||||||
// Map
|
// Map
|
||||||
app.use(['/map', '/trac'], require('./config/routes/map.js'))
|
app.use(['/map', '/trac'], require('./config/routes/map.js'))
|
||||||
|
|
|
@ -21,7 +21,7 @@ $(function () {
|
||||||
var slugNotUnique, emailNotUnique
|
var slugNotUnique, emailNotUnique
|
||||||
|
|
||||||
// Set timezone in password change link
|
// 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 account
|
||||||
$('#delete').click(function () {
|
$('#delete').click(function () {
|
||||||
|
|
|
@ -89,15 +89,15 @@ describe('Authentication', () => {
|
||||||
it('Loads password page', async () => {
|
it('Loads password page', async () => {
|
||||||
// Load password page
|
// Load password page
|
||||||
chai.expect(await request
|
chai.expect(await request
|
||||||
.get(`/settings/password/${passwordless_user.auth.passToken}`)
|
.get(`/account/password/${passwordless_user.auth.passToken}`)
|
||||||
).html.to.have.status(200)
|
).html.to.have.status(200)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Fails to set a weak password', async () => {
|
it('Fails to set a weak password', async () => {
|
||||||
chai.expect( await request
|
chai.expect( await request
|
||||||
.post(`/settings/password/${passwordless_user.auth.passToken}`)
|
.post(`/account/password/${passwordless_user.auth.passToken}`)
|
||||||
.type('form').send({ 'password':BAD_PASSWORD })
|
.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 () => {
|
it('Sets a strong password', async () => {
|
||||||
|
@ -105,7 +105,7 @@ describe('Authentication', () => {
|
||||||
|
|
||||||
// Perform request
|
// Perform request
|
||||||
let res = await 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 })
|
.type('form').send({ 'password':TEST_PASSWORD })
|
||||||
|
|
||||||
// Expect redirect
|
// Expect redirect
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id='password-delete' class='form-group'>
|
<div id='password-delete' class='form-group'>
|
||||||
<a id='password' class='underline' href="/settings/password" title="Click here to {% if user.auth.password %}change{% else %}set{% endif %} your password. ">{% if user.auth.password %}Change{% else %}Set{% endif %} password</a>
|
<a id='password' class='underline' href="/account/password" title="Click here to {% if user.auth.password %}change{% else %}set{% endif %} your password. ">{% if user.auth.password %}Change{% else %}Set{% endif %} password</a>
|
||||||
<a id='delete' class='red underline' style="text-align:right" href="#" title="Permently delete your Tracman account. ">Delete account</a>
|
<a id='delete' class='red underline' style="text-align:right" href="#" title="Permently delete your Tracman account. ">Delete account</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue