tracman-server/config/routes/settings.js

360 lines
9.6 KiB
JavaScript
Raw Normal View History

2017-04-11 19:38:07 -06:00
'use strict';
const slug = require('slug'),
xss = require('xss'),
2017-06-30 10:48:29 -06:00
zxcvbn = require('zxcvbn'),
moment = require('moment'),
2017-04-11 19:38:07 -06:00
mw = require('../middleware.js'),
User = require('../models.js').user,
mail = require('../mail.js'),
2017-04-26 21:13:14 -06:00
env = require('../env/env.js'),
2017-05-08 11:47:53 -06:00
debug = require('debug')('tracman-settings'),
2017-04-11 19:38:07 -06:00
router = require('express').Router();
2017-04-11 19:38:07 -06:00
// Settings form
router.route('/')
.all( mw.ensureAuth, (req,res,next)=>{
2017-04-11 19:38:07 -06:00
next();
} )
2017-04-11 19:38:07 -06:00
// Get settings form
2017-04-14 20:10:52 -06:00
.get( (req,res)=>{
2017-05-22 20:32:51 -06:00
res.render('settings', {active:'settings'});
} )
2017-04-11 19:38:07 -06:00
// Set new settings
.post( (req,res,next)=>{
2017-04-14 20:10:52 -06:00
2017-04-27 15:25:16 -06:00
// Validate email
const checkEmail = new Promise( (resolve,reject)=>{
// Check validity
if (!mw.validateEmail(req.body.email)) {
2017-04-27 15:25:16 -06:00
req.flash('warning', `<u>${req.body.email}</u> is not a valid email address. `);
resolve();
}
// Check if unchanged
else if (req.user.email===req.body.email) {
resolve();
}
// Check uniqueness
else {
User.findOne({ email: req.body.email })
.then( (existingUser)=>{
// Not unique!
if (existingUser && existingUser.id!==req.user.id) {
2017-05-08 11:47:53 -06:00
debug("Email not unique!");
2017-04-27 15:25:16 -06:00
req.flash('warning', `That email, <u>${req.body.email}</u>, is already in use by another user! `);
resolve();
}
// It's unique
else {
2017-05-08 11:47:53 -06:00
debug("Email is unique");
2017-04-27 15:25:16 -06:00
req.user.newEmail = req.body.email;
// Create token
2017-05-08 11:47:53 -06:00
debug(`Creating email token...`);
2017-04-27 15:25:16 -06:00
req.user.createEmailToken((err,token)=>{
if (err){ reject(err); }
// Send token to user by email
2017-05-08 11:47:53 -06:00
debug(`Mailing new email token to ${req.body.email}...`);
2017-04-27 15:25:16 -06:00
mail.send({
to: `"${req.user.name}" <${req.body.email}>`,
2017-05-08 15:45:06 -06:00
from: mail.noReply,
2017-04-27 15:25:16 -06:00
subject: 'Confirm your new email address for Tracman',
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\nTo 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>`)
})
.then( ()=>{
req.flash('warning',`An email has been sent to <u>${req.body.email}</u>. Check your inbox to confirm your new email address. `);
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
else if (req.user.slug===slug(xss(req.body.slug))) {
resolve();
}
// Check uniqueness
else {
User.findOne({ slug: req.body.slug })
.then( (existingUser)=>{
// Not unique!
if (existingUser && existingUser.id!==req.user.id) {
req.flash('warning', `That slug, <u>${req.body.slug}</u>, is already in use by another user! `);
}
// It's unique
else {
req.user.slug = slug(xss(req.body.slug));
}
})
.then(resolve)
.catch(reject);
}
});
// Set settings when done
Promise.all([checkEmail, checkSlug])
.then( ()=>{
2017-05-08 11:47:53 -06:00
debug('Setting settings... ');
2017-04-19 19:37:00 -06:00
// Set values
req.user.name = xss(req.body.name);
req.user.settings = {
2017-05-02 07:21:53 -06:00
units: req.body.units,
defaultMap: req.body.map,
defaultZoom: req.body.zoom,
showScale: (req.body.showScale)?true:false,
showSpeed: (req.body.showSpeed)?true:false,
showAlt: (req.body.showAlt)?true:false,
showStreetview: (req.body.showStreet)?true:false,
marker: req.body.marker
2017-05-02 07:21:53 -06:00
};
2017-04-26 20:47:23 -06:00
// Save user and send response
2017-05-08 11:47:53 -06:00
debug(`Saving new settings for user ${req.user.name}...`);
req.user.save()
.then( ()=>{
2017-05-08 11:47:53 -06:00
debug(`DONE! Redirecting user...`);
req.flash('success', 'Settings updated. ');
res.redirect('/settings');
})
.catch( (err)=>{
mw.throwErr(err,req);
res.redirect('/settings');
});
2017-04-27 15:25:16 -06:00
})
.catch( (err)=>{
mw.throwErr(err,req);
2017-04-18 11:10:43 -06:00
res.redirect('/settings');
2017-04-27 15:25:16 -06:00
});
} );
2017-04-11 19:38:07 -06:00
2017-05-06 23:59:21 -06:00
// Delete account
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-04-18 01:08:57 -06:00
// Confirm email address
router.get('/email/:token', mw.ensureAuth, (req,res,next)=>{
2017-05-06 23:59:21 -06:00
// Check token
if ( req.user.emailToken===req.params.token) {
2017-04-18 01:08:57 -06:00
2017-05-06 23:59:21 -06:00
// Set new email
req.user.email = req.user.newEmail;
req.user.save()
2017-07-04 09:57:56 -06:00
// Delete token and newEmail
2017-05-06 23:59:21 -06:00
.then( ()=>{
req.user.emailToken = undefined;
req.user.newEmail = undefined;
req.user.save();
})
2017-07-04 09:57:56 -06:00
// Report success
2017-05-06 23:59:21 -06:00
.then( ()=>{
req.flash('success',`Your email has been set to <u>${req.user.email}</u>. `);
2017-04-18 01:08:57 -06:00
res.redirect('/settings');
2017-05-06 23:59:21 -06:00
})
2017-07-04 09:57:56 -06:00
2017-05-06 23:59:21 -06:00
.catch( (err)=>{
mw.throwErr(err,req);
res.redirect(req.session.next||'/settings');
});
2017-04-18 01:08:57 -06:00
2017-05-06 23:59:21 -06:00
}
// 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')
.all( mw.ensureAuth, (req,res,next)=>{
2017-04-11 19:38:07 -06:00
next();
} )
2017-04-12 11:41:27 -06:00
// Email user a token, proceed at /password/:token
.get( (req,res,next)=>{
2017-04-12 11:41:27 -06:00
// Create token for password change
req.user.createPassToken( (err,token,expires)=>{
2017-04-18 01:08:57 -06:00
if (err){
mw.throwErr(err,req);
res.redirect((req.user)?'/settings':'/login');
2017-04-18 01:08:57 -06:00
}
else {
2017-04-18 01:08:57 -06:00
// 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),
2017-05-08 15:45:06 -06:00
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\nThis 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>`)
})
.then( ()=>{
// 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}). `);
res.redirect((req.user)?'/settings':'/login');
})
.catch( (err)=>{
mw.throwErr(err,req);
res.redirect((req.user)?'/settings':'/login');
});
2017-04-18 01:08:57 -06:00
}
2017-04-18 01:08:57 -06:00
});
} );
2017-04-12 11:41:27 -06:00
router.route('/password/:token')
2017-04-12 11:41:27 -06:00
// Check token
.all( (req,res,next)=>{
2017-05-08 11:47:53 -06:00
debug('/settings/password/:token .all() called');
2017-04-12 11:41:27 -06:00
User
.findOne({'auth.passToken': req.params.token})
2017-04-18 01:08:57 -06:00
.where('auth.passTokenExpires').gt(Date.now())
2017-04-13 16:59:46 -06:00
.then((user) => {
2017-04-12 11:41:27 -06:00
if (!user) {
2017-05-08 11:47:53 -06:00
debug('Bad token');
2017-04-12 11:41:27 -06:00
req.flash('danger', 'Password reset token is invalid or has expired. ');
res.redirect( (req.isAuthenticated)?'/settings':'/login' );
2017-07-04 09:57:56 -06:00
}
else {
2017-05-08 11:47:53 -06:00
debug('setting passwordUser');
2017-04-12 11:41:27 -06:00
res.locals.passwordUser = user;
next();
}
2017-04-14 20:10:52 -06:00
})
.catch((err)=>{
mw.throwErr(err,req);
res.redirect('/password');
2017-04-12 11:41:27 -06:00
});
} )
2017-04-12 11:41:27 -06:00
// Show password change form
.get( (req,res)=>{
2017-05-08 11:47:53 -06:00
debug('/settings/password/:token .get() called');
2017-04-12 11:41:27 -06:00
res.render('password');
} )
2017-04-15 08:22:13 -06:00
// Set new password
.post( (req,res,next)=>{
2017-04-14 20:10:52 -06:00
2017-04-15 08:22:13 -06:00
// Validate password
2017-06-30 11:14:59 -06:00
let zxcvbnResult = zxcvbn(req.body.password);
if (zxcvbnResult.crack_times_seconds.online_no_throttling_10_per_second < 864000) { // Less than ten days
mw.throwErr(new Error(`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-04-15 08:22:13 -06:00
res.redirect(`/settings/password/${req.params.token}`);
}
2017-04-25 15:22:23 -06:00
else {
2017-04-15 08:22:13 -06:00
// Create hashed password and save to db
res.locals.passwordUser.generateHashedPassword( req.body.password, (err)=>{
2017-04-15 08:22:13 -06:00
if (err){
mw.throwErr(err,req);
res.redirect(`/password/${req.params.token}`);
}
// User changed password
else if (req.user) {
req.flash('success', 'Your password has been changed. ');
res.redirect('/settings');
}
2017-05-08 18:12:58 -06:00
// New user created password
2017-04-15 08:22:13 -06:00
else {
req.flash('success', 'Password set. You can use it to log in now. ');
2017-05-08 18:12:58 -06:00
res.redirect('/login?next=/map?new=1');
2017-04-15 08:22:13 -06:00
}
2017-04-15 08:22:13 -06:00
} );
}
2017-04-14 20:10:52 -06:00
} );
2017-04-11 19:38:07 -06:00
// Tracman pro
2017-04-12 11:41:27 -06:00
router.route('/pro')
.all( mw.ensureAuth, (req,res,next)=>{
2017-04-11 19:38:07 -06:00
next();
} )
2017-04-11 19:38:07 -06:00
// Get info about pro
.get( (req,res,next)=>{
2017-04-12 11:41:27 -06:00
res.render('pro');
} )
2017-04-11 19:38:07 -06:00
// Join Tracman pro
.post( (req,res)=>{
2017-04-12 11:41:27 -06:00
User.findByIdAndUpdate(req.user.id,
2017-04-14 20:10:52 -06:00
{$set:{ isPro:true }})
.then( (user)=>{
req.flash('success','You have been signed up for pro. ');
res.redirect('/settings');
2017-04-14 20:10:52 -06:00
})
.catch( (err)=>{
mw.throwErr(err,req);
res.redirect('/settings/pro');
2017-04-14 20:10:52 -06:00
});
} );
module.exports = router;