2018-02-07 18:34:01 -07:00
'use strict'
const mw = require ( '../middleware.js' )
2018-03-04 13:39:45 -07:00
const sanitize = require ( 'mongo-sanitize' )
2018-02-07 18:34:01 -07:00
const User = require ( '../models.js' ) . user
const mail = require ( '../mail.js' )
const env = require ( '../env/env.js' )
const zxcvbn = require ( 'zxcvbn' )
const moment = require ( 'moment' )
const debug = require ( 'debug' ) ( 'tracman-routes-account' )
const router = require ( 'express' ) . Router ( )
// 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'
// 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 } ) . `
)
// 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 } / account / 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}/account/password/${token}" > \
$ { env . url } / account / password / $ { token } < / a > . < / p > \
< p > This request will expire at $ { expirationTimeString } . < / p > `
)
} )
} 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 ( '/account/password/:token .all() called' )
try {
let user = await User
2018-03-04 13:39:45 -07:00
. findOne ( { 'auth.passToken' : sanitize ( req . params . token ) } )
2018-02-07 18:34:01 -07:00
. 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 ( '/account/password/:token .get() called' )
res . render ( 'password' )
} )
// Set new password
. post ( async ( req , res , next ) => {
debug ( '/account/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 ( ` /account/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 ( ` /account/password/ ${ req . params . token } ` )
}
}
} )
2018-03-08 20:31:04 -07:00
2018-02-07 18:34:01 -07:00
module . exports = router