2017-12-12 17:40:07 -07:00
'use strict'
2017-07-04 10:09:28 -06:00
2017-12-12 17:40:07 -07:00
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' )
const env = require ( '../env/env.js' )
const debug = require ( 'debug' ) ( 'tracman-settings' )
const router = require ( 'express' ) . Router ( )
2017-04-11 19:38:07 -06:00
// Settings form
router . route ( '/' )
2017-12-12 17:40:07 -07:00
. all ( mw . ensureAuth , ( req , res , next ) => {
next ( )
} )
// Get settings form
. get ( ( req , res ) => {
res . render ( 'settings' , { active : 'settings' } )
} )
// Set new settings
. post ( ( req , res , next ) => {
// Validate email
const checkEmail = new Promise ( ( resolve , reject ) => {
// Check validity
if ( ! mw . validateEmail ( req . body . email ) ) {
req . flash ( 'warning' , ` <u> ${ req . body . email } </u> is not a valid email address. ` )
resolve ( )
// Check if unchanged
2017-12-13 12:52:01 -07:00
} else if ( req . user . email === req . body . email ) resolve ( )
2017-12-12 17:40:07 -07:00
// Check uniqueness
2017-12-13 12:52:01 -07:00
else {
2017-12-12 17:40:07 -07:00
User . findOne ( { email : req . body . email } )
. then ( ( existingUser ) => {
// Not unique!
if ( existingUser && existingUser . id !== req . user . id ) {
debug ( 'Email not unique!' )
2017-12-13 12:52:01 -07:00
req . flash ( 'warning' ,
` That email, <u> ${ req . body . email } </u>, is already in use by another user! `
)
2017-12-12 17:40:07 -07:00
resolve ( )
// It's unique
} else {
debug ( 'Email is unique' )
req . user . newEmail = req . body . email
// Create token
debug ( ` Creating email token... ` )
req . user . createEmailToken ( ( err , token ) => {
2017-12-13 12:52:01 -07:00
if ( err ) reject ( err )
2017-12-12 17:40:07 -07:00
// Send token to user by email
debug ( ` Mailing new email token to ${ req . body . email } ... ` )
mail . send ( {
to : ` " ${ req . user . name } " < ${ req . body . email } > ` ,
from : mail . noReply ,
subject : 'Confirm your new email address for Tracman' ,
2017-12-13 12:52:01 -07:00
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 } . `
) ,
html : mail . html (
` <p>A request has been made to change your Tracman email address. \
If you did not initiate this request , please disregard it . < / p > \
< p > To confirm your email , follow this link : \
< br > < a href = "${env.url}/settings/email/${token}" > \
$ { env . url } / settings / email / $ { token } < / a > . < / p > `
)
2017-12-12 17:40:07 -07:00
} )
. then ( ( ) => {
2017-12-13 12:52:01 -07:00
req . flash ( 'warning' ,
` An email has been sent to <u> ${ req . body . email } </u>. \
Check your inbox to confirm your new email address . `
)
2017-12-12 17:40:07 -07:00
resolve ( )
} )
. catch ( reject )
} )
}
} )
. catch ( reject )
}
} )
// Validate slug
const checkSlug = new Promise ( ( resolve , reject ) => {
// Check existence
if ( req . body . slug === '' ) {
req . flash ( 'warning' , ` You must supply a slug. ` )
resolve ( )
// Check if unchanged
2017-12-13 12:52:01 -07:00
} else if ( req . user . slug === slug ( xss ( req . body . slug ) ) ) resolve ( )
2017-12-18 23:42:43 -07:00
2017-12-12 17:40:07 -07:00
// Check uniqueness
2017-12-13 12:52:01 -07:00
else {
2017-12-12 17:40:07 -07:00
User . findOne ( { slug : req . body . slug } )
. then ( ( existingUser ) => {
// Not unique!
if ( existingUser && existingUser . id !== req . user . id ) {
2017-12-18 23:42:43 -07:00
req . flash ( 'warning' ,
2017-12-13 12:52:01 -07:00
` That slug, <u> ${ req . body . slug } </u>, is already in use by another user! `
)
2017-12-12 17:40:07 -07:00
// It's unique
2017-12-13 12:52:01 -07:00
} else req . user . slug = slug ( xss ( req . body . slug ) )
2017-12-12 17:40:07 -07:00
} )
. then ( resolve )
. catch ( reject )
}
} )
// Set settings when done
Promise . all ( [ checkEmail , checkSlug ] )
. then ( ( ) => {
debug ( 'Setting settings... ' )
// Set values
req . user . name = xss ( req . body . name )
req . user . settings = {
units : req . body . units ,
defaultMap : req . body . map ,
defaultZoom : req . body . zoom ,
showScale : ! ! ( req . body . showScale ) ,
showSpeed : ! ! ( req . body . showSpeed ) ,
showAlt : ! ! ( req . body . showAlt ) ,
showStreetview : ! ! ( req . body . showStreet ) ,
marker : req . body . marker
}
// Save user and send response
debug ( ` Saving new settings for user ${ req . user . name } ... ` )
req . user . save ( )
. then ( ( ) => {
debug ( ` DONE! Redirecting user... ` )
req . flash ( 'success' , 'Settings updated. ' )
res . redirect ( '/settings' )
} )
. catch ( ( err ) => {
mw . throwErr ( err , req )
res . redirect ( '/settings' )
} )
} )
. catch ( ( err ) => {
mw . throwErr ( err , req )
res . redirect ( '/settings' )
} )
} )
2017-04-11 19:38:07 -06:00
2017-05-06 23:59:21 -06:00
// Delete account
2017-12-12 17:40:07 -07:00
router . get ( '/delete' , ( req , res ) => {
User . findByIdAndRemove ( req . user )
. then ( ( ) => {
req . flash ( 'success' , 'Your account has been deleted. ' )
res . redirect ( '/' )
} )
. catch ( ( err ) => {
mw . throwErr ( err , req )
res . redirect ( '/settings' )
} )
} )
2017-05-06 23:59:21 -06:00
2017-04-18 01:08:57 -06:00
// Confirm email address
2017-12-12 17:40:07 -07:00
router . get ( '/email/:token' , mw . ensureAuth , ( req , res , next ) => {
// Check token
if ( req . user . emailToken === req . params . token ) {
// Set new email
req . user . email = req . user . newEmail
req . user . save ( )
// Delete token and newEmail
. then ( ( ) => {
req . user . emailToken = undefined
req . user . newEmail = undefined
req . user . save ( )
} )
// Report success
. then ( ( ) => {
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 ( req . session . next || '/settings' )
} )
// Invalid token
} else {
req . flash ( 'danger' , 'Email confirmation token is invalid. ' )
res . redirect ( '/settings' )
}
} )
2017-04-11 19:38:07 -06:00
// Set password
2017-04-14 20:10:52 -06:00
router . route ( '/password' )
2017-12-12 17:40:07 -07:00
. all ( mw . ensureAuth , ( req , res , next ) => {
next ( )
} )
// Email user a token, proceed at /password/:token
. get ( ( req , res , next ) => {
// Create token for password change
req . user . createPassToken ( ( err , token , expires ) => {
if ( err ) {
mw . throwErr ( err , req )
res . redirect ( ( req . user ) ? '/settings' : '/login' )
} else {
// 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.
mail . send ( {
to : mail . to ( req . user ) ,
from : mail . noReply ,
subject : 'Request to change your Tracman password' ,
2017-12-13 12:52:01 -07:00
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 > `
)
2017-12-12 17:40:07 -07:00
} )
. then ( ( ) => {
// Alert user to check email.
2017-12-13 12:52:01 -07:00
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 } ) . `
)
2017-12-12 17:40:07 -07:00
res . redirect ( ( req . user ) ? '/settings' : '/login' )
} )
. catch ( ( err ) => {
mw . throwErr ( err , req )
res . redirect ( ( req . user ) ? '/settings' : '/login' )
} )
}
} )
} )
2017-04-13 16:53:18 -06:00
2017-04-12 11:41:27 -06:00
router . route ( '/password/:token' )
2017-04-13 16:53:18 -06:00
2017-12-12 17:40:07 -07:00
// Check token
. all ( ( req , res , next ) => {
debug ( '/settings/password/:token .all() called' )
User
. findOne ( { 'auth.passToken' : req . params . token } )
. where ( 'auth.passTokenExpires' ) . gt ( Date . now ( ) )
. then ( ( user ) => {
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 ( ( req , res , next ) => {
2017-12-18 23:42:43 -07:00
debug ( '/settings/password/:token .post() called' )
// Validate password strength
2017-12-12 17:40:07 -07:00
let zxcvbnResult = zxcvbn ( req . body . password )
if ( zxcvbnResult . crack _times _seconds . online _no _throttling _10 _per _second < 864000 ) { // Less than ten days
2017-12-18 23:42:43 -07:00
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. `
)
2017-12-12 17:40:07 -07:00
res . redirect ( ` /settings/password/ ${ req . params . token } ` )
} else {
2017-12-18 23:42:43 -07:00
2017-12-12 17:40:07 -07:00
// Create hashed password and save to db
res . locals . passwordUser . generateHashedPassword ( req . body . password , ( err ) => {
if ( err ) {
2017-12-18 23:42:43 -07:00
debug ( 'Error creating hashed password and saving to db' )
2017-12-12 17:40:07 -07:00
mw . throwErr ( err , req )
2017-12-18 23:42:43 -07:00
res . redirect ( ` /settings/password/ ${ req . params . token } ` )
2017-12-12 17:40:07 -07:00
// User changed password
} else if ( req . user ) {
2017-12-18 23:42:43 -07:00
debug ( 'User saved password' )
2017-12-12 17:40:07 -07:00
req . flash ( 'success' , 'Your password has been changed. ' )
res . redirect ( '/settings' )
2017-04-11 19:38:07 -06:00
2017-12-12 17:40:07 -07:00
// New user created password
} else {
2017-12-18 23:42:43 -07:00
debug ( 'New user created password' )
2017-12-12 17:40:07 -07:00
req . flash ( 'success' , 'Password set. You can use it to log in now. ' )
res . redirect ( '/login?next=/map?new=1' )
}
2017-12-18 23:42:43 -07:00
2017-12-12 17:40:07 -07:00
} )
2017-12-18 23:42:43 -07:00
2017-12-12 17:40:07 -07:00
}
} )
2017-04-11 19:38:07 -06:00
// Tracman pro
2017-04-12 11:41:27 -06:00
router . route ( '/pro' )
2017-12-12 17:40:07 -07:00
. all ( mw . ensureAuth , ( req , res , next ) => {
next ( )
} )
// Get info about pro
. get ( ( req , res , next ) => {
res . render ( 'pro' )
} )
// Join Tracman pro
. post ( ( req , res ) => {
User . findByIdAndUpdate ( req . user . id ,
{ $set : { isPro : true } } )
. then ( ( user ) => {
req . flash ( 'success' , 'You have been signed up for pro. ' )
res . redirect ( '/settings' )
} )
. catch ( ( err ) => {
mw . throwErr ( err , req )
res . redirect ( '/settings/pro' )
} )
} )
module . exports = router