Added ability to change password
parent
46db57abd5
commit
a15cf2c03f
|
@ -19,15 +19,15 @@ module.exports = function(app, passport) {
|
|||
failureFlash: true
|
||||
},
|
||||
loginCallback = function(req,res){
|
||||
res.redirect( req.session.returnTo || '/settings' );
|
||||
delete req.session.returnTo;
|
||||
res.redirect( req.session.next || '/settings' );
|
||||
delete req.session.next;
|
||||
};
|
||||
|
||||
// Login/-out
|
||||
app.route('/login')
|
||||
.get( function(req,res){
|
||||
if (req.isAuthenticated()){
|
||||
res.redirect('/account'); }
|
||||
res.redirect('/settings'); }
|
||||
else { res.render('login'); }
|
||||
})
|
||||
.post( passport.authenticate('local',loginOutcome), loginCallback );
|
||||
|
@ -75,7 +75,7 @@ module.exports = function(app, passport) {
|
|||
// Forgot password
|
||||
app.route('/login/forgot')
|
||||
.all( function(req,res,next){
|
||||
if (req.isAuthenticated()){ res.redirect('/account'); }
|
||||
if (req.isAuthenticated()){ res.redirect('/settings'); }
|
||||
else { next(); }
|
||||
})
|
||||
.get( function(req,res,next){
|
||||
|
@ -103,8 +103,8 @@ module.exports = function(app, passport) {
|
|||
from: '"Tracman" <NoReply@tracman.org>',
|
||||
to: `"${user.name}"" <${user.email}>`,
|
||||
subject: 'Reset your Tracman password',
|
||||
text: `Hi, \n\nDid you request to reset your Tracman password? If so, follow this link to do so:\n${env.url}/account/password/${token}\n\nIf you didn't initiate this request, just ignore this email. `,
|
||||
html: `<p>Hi, </p><p>Did you request to reset your Tracman password? If so, follow this link to do so:<br><a href="${env.url}/account/password/${token}">${env.url}/account/password/${token}</a></p><p>If you didn't initiate this request, just ignore this email. </p>`
|
||||
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\nIf you didn't initiate this request, just ignore this email. `,
|
||||
html: `<p>Hi, </p><p>Did you request to reset your Tracman password? If so, follow this link to do so:<br><a href="${env.url}/settings/password/${token}">${env.url}/settings/password/${token}</a></p><p>If you didn't initiate this request, just ignore this email. </p>`
|
||||
}).then(function(){
|
||||
req.flash('success', `An email has been sent to <u>${req.body.email}</u>. Check your email for instructions to reset your password. `);
|
||||
res.redirect('/');
|
||||
|
@ -131,11 +131,11 @@ module.exports = function(app, passport) {
|
|||
} else { // Disconnect social account
|
||||
req.user.auth[service] = undefined;
|
||||
req.user.save(function(err){
|
||||
if (err){ return next(err); }
|
||||
if (err){ mw.throwErr(err,req); }
|
||||
else {
|
||||
req.flash('success', `${mw.capitalize(service)} account disconnected. `);
|
||||
res.redirect('/account');
|
||||
}
|
||||
res.redirect('/settings');
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ module.exports = function(app, passport) {
|
|||
passport.authenticate(service, loginOutcome)(req,res,next);
|
||||
} else {
|
||||
req.flash('success', `${mw.capitalize(service)} account connected. `);
|
||||
req.session.returnTo = '/account';
|
||||
req.session.next = '/settings';
|
||||
passport.authenticate(service, connectOutcome)(req,res,next);
|
||||
}
|
||||
}, loginCallback);
|
||||
|
@ -190,7 +190,7 @@ module.exports = function(app, passport) {
|
|||
// if (!user.name) { user.name=profile.displayName; }
|
||||
// user.lastLogin = Date.now();
|
||||
// user.save(function (err, raw) {
|
||||
// if (err) { throwErr(req,err); }
|
||||
// if (err) { throwErr(err,req); }
|
||||
// }); done(null, user);
|
||||
// }
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ const env = require('./env.js');
|
|||
module.exports = {
|
||||
|
||||
// Throw error
|
||||
throwErr: function(req,err){
|
||||
throwErr: function(err,req=null){
|
||||
console.error('Middleware error:'+err.message+'\nfor request:\n'+req);
|
||||
if (env.mode==='production') {
|
||||
req.flash('danger', 'An error occured. <br>Would you like to <a href="https://github.com/Tracman-org/Server/issues/new">report it</a>?');
|
||||
|
|
|
@ -53,17 +53,28 @@ const userSchema = new mongoose.Schema({
|
|||
|
||||
// Create password reset token
|
||||
userSchema.methods.createToken = function(next){
|
||||
//TODO: Use the same token if there already is one that's not yet expired.
|
||||
var user = this;
|
||||
crypto.randomBytes(16, function(err,buf){
|
||||
if (err){ next(err,null); }
|
||||
else {
|
||||
user.auth.passToken = buf.toString('hex');
|
||||
user.auth.tokenExpires = Date.now() + 3600000; // 1 hour
|
||||
user.save();
|
||||
return next(null,user.auth.passToken);
|
||||
}
|
||||
});
|
||||
if ( user.auth.tokenExpires <= Date.now() ){
|
||||
|
||||
// Reuse old token, resetting clock
|
||||
user.auth.tokenExpires = Date.now() + 3600000; // 1 hour
|
||||
user.save();
|
||||
return next(null.user.auth.passToken);
|
||||
|
||||
} else {
|
||||
|
||||
// Create new token
|
||||
crypto.randomBytes(16, function(err,buf){
|
||||
if (err){ next(err,null); }
|
||||
else {
|
||||
user.auth.passToken = buf.toString('hex');
|
||||
user.auth.tokenExpires = Date.now() + 3600000; // 1 hour
|
||||
user.save();
|
||||
return next(null,user.auth.passToken);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// Check for valid password
|
||||
|
|
|
@ -105,13 +105,13 @@ module.exports = function(passport) {
|
|||
if (service==='google') {
|
||||
User.findOne({'googleID':parseInt(profileId)}, function(err,user){
|
||||
// console.log(`searched for user with googleID ${profileId}`);
|
||||
if (err){ mw.throwErr(err); }
|
||||
if (err){ mw.throwErr(err,req); }
|
||||
if (user) {
|
||||
// console.log(`Lazily updating schema for ${user.name}.`);
|
||||
user.auth.google = profileId;
|
||||
user.googleId = null;
|
||||
user.save(function(err){
|
||||
if (err){ mw.throwErr(err); }
|
||||
if (err){ mw.throwErr(err,req); }
|
||||
return done(null, user);
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -14,7 +14,7 @@ router.route('/')
|
|||
var checkCBC = function(req,res,err){
|
||||
if (err) {
|
||||
req.flash('error', err.message);
|
||||
console.log(err);
|
||||
console.error(err);
|
||||
}
|
||||
if (cbc<1){ cbc++; }
|
||||
else { // done
|
||||
|
@ -26,7 +26,7 @@ router.route('/')
|
|||
}
|
||||
};
|
||||
|
||||
User.findById(req.session.passport.user, function(err, found) {
|
||||
User.findById(req.user, function(err, found) {
|
||||
res.locals.user = found;
|
||||
checkCBC(req,res,err);
|
||||
});
|
||||
|
@ -62,7 +62,7 @@ router.route('/testmail').get(function(req,res,next){
|
|||
console.log("Test email should have sent...");
|
||||
res.sendStatus(200);
|
||||
}).catch(function(err){
|
||||
mw.throwErr(err);
|
||||
mw.throwErr(err,req);
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -40,7 +40,7 @@ router.get('/validate', function(req,res){
|
|||
if (req.query.slug) { // validate unique slug
|
||||
User.findOne({slug:slug(req.query.slug)}, function(err, existingUser){
|
||||
if (err) { console.log('/validate error:',err); }
|
||||
if (existingUser && existingUser.id!==req.session.passport.user) { res.sendStatus(400); }
|
||||
if (existingUser && existingUser.id!==req.user) { res.sendStatus(400); }
|
||||
else { res.sendStatus(200); }
|
||||
});
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ router.get('/:slug?', function(req,res,next){
|
|||
|
||||
// Confirm sucessful queries
|
||||
function checkQuery(err,found) {
|
||||
if (err){ mw.throwErr(req,err); }
|
||||
if (err){ mw.throwErr(err,req); }
|
||||
if (found){ return found; }
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ router.get('/:slug?', function(req,res,next){
|
|||
// QUERIES
|
||||
// Get logged in user -> user
|
||||
if (req.isAuthenticated()) {
|
||||
User.findById(req.session.passport.user, function(err, found) {
|
||||
User.findById(req.user, function(err, found) {
|
||||
user = checkQuery(err,found);
|
||||
checkCBC();
|
||||
});
|
||||
|
|
|
@ -17,15 +17,15 @@ router.route('/')
|
|||
|
||||
// Get settings form
|
||||
.get(function(req,res,next){
|
||||
User.findById(req.session.passport.user, function(err,user){
|
||||
if (err){ console.log('Error finding settings for user:',err); mw.throwErr(req,err); }
|
||||
User.findById(req.user, function(err,user){
|
||||
if (err){ mw.throwErr(err,req); }
|
||||
res.render('settings');
|
||||
});
|
||||
})
|
||||
|
||||
// Set new settings
|
||||
.post(function(req,res,next){
|
||||
User.findByIdAndUpdate(req.session.passport.user, {$set:{
|
||||
User.findByIdAndUpdate(req.user, {$set:{
|
||||
name: xss(req.body.name),
|
||||
slug: slug(xss(req.body.slug)),
|
||||
email: req.body.email,
|
||||
|
@ -38,7 +38,7 @@ router.route('/')
|
|||
showStreetview: (req.body.showStreet)?true:false
|
||||
}
|
||||
}}, function(err, user){
|
||||
if (err) { console.log('Error updating user settings:',err); mw.throwErr(req,err); }
|
||||
if (err) { mw.throwErr(err,req); }
|
||||
else { req.flash('success', 'Settings updated. '); }
|
||||
res.redirect('/settings');
|
||||
});
|
||||
|
@ -46,65 +46,131 @@ router.route('/')
|
|||
|
||||
// Delete user account
|
||||
.delete(function(req,res,next){
|
||||
User.findByIdAndRemove( req.session.passport.user,
|
||||
User.findByIdAndRemove( req.user,
|
||||
function(err) {
|
||||
if (err) {
|
||||
console.log('Error deleting user:',err);
|
||||
mw.throwErr(req,err);
|
||||
if (err) {
|
||||
mw.throwErr(err,req);
|
||||
} else {
|
||||
req.flash('success', 'Your account has been deleted. ');
|
||||
res.redirect('/');
|
||||
}
|
||||
res.redirect('/');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
// Set password
|
||||
router.route('/password')
|
||||
router.route('/password/')
|
||||
.all(mw.ensureAuth,function(req,res,next){
|
||||
next();
|
||||
})
|
||||
|
||||
// Email user a token, proceed at /password/:token
|
||||
.get(function(req,res,next){
|
||||
|
||||
// Create token for password change
|
||||
req.user.createToken(function(err,token){
|
||||
if (err){ next(err); }
|
||||
mail.send({
|
||||
to: mail.to(req.user),
|
||||
from: mail.from,
|
||||
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}. `),
|
||||
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>`)
|
||||
}).then(function(){
|
||||
req.flash('success',`An email has been sent to <u>${req.user.email}</u>. Check your inbox to complete your password change. `);
|
||||
res.redirect(req.query.next||'/settings');
|
||||
}).catch(function(err){
|
||||
next(err);
|
||||
});
|
||||
});
|
||||
if (err){ next(err); }
|
||||
|
||||
// Confirm password change request by email.
|
||||
mail.send({
|
||||
to: mail.to(req.user),
|
||||
from: mail.from,
|
||||
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 in 1 hour. `),
|
||||
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 in 1 hour. </p>`)
|
||||
}).catch(function(err){
|
||||
mw.throwErr(err,req);
|
||||
}).then(function(){
|
||||
|
||||
// Alert user to check email.
|
||||
req.flash('success',`An email has been sent to <u>${req.user.email}</u>. Check your inbox to complete your password change. `);
|
||||
res.redirect( (req.isAuthenticated)?'/settings':'/login' );
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
router.route('/password/:token')
|
||||
|
||||
// Check token
|
||||
.all(function(req,res,next){
|
||||
User
|
||||
.findOne({'auth.passToken': req.params.token})
|
||||
.where('auth.tokenExpires').gt(Date.now())
|
||||
//TODO: Add own promise libary
|
||||
.exec((err, user) => {
|
||||
if (err) { mw.throwErr(err,req); }
|
||||
if (!user) {
|
||||
req.flash('danger', 'Password reset token is invalid or has expired. ');
|
||||
res.redirect( (req.isAuthenticated)?'/settings':'/login' );
|
||||
} else {
|
||||
res.locals.passwordUser = user;
|
||||
next();
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
// Show password change form
|
||||
.get(function(req,res){
|
||||
res.render('password');
|
||||
})
|
||||
|
||||
.post(function(req,res,next){
|
||||
|
||||
// Validate matching passwords
|
||||
if (req.body.password!==req.body.repassword) {
|
||||
mw.throwErr( new Error('Passwords do not match. '), req );
|
||||
} else {
|
||||
|
||||
//TODO: Add logic for new users
|
||||
|
||||
// Delete token
|
||||
res.locals.passwordUser.auth.passToken = undefined;
|
||||
res.locals.passwordUser.auth.tokenExpires = undefined;
|
||||
|
||||
// Create hash
|
||||
res.locals.passwordUser.generateHash(req.body.password, function(err,hash){
|
||||
if (err){ mw.throwErr(err); }
|
||||
else {
|
||||
|
||||
// Save new password to db
|
||||
res.locals.passwordUser.auth.password = hash;
|
||||
res.locals.passwordUser.save( function(err){
|
||||
if (err){ mw.throwErr(err,req); }
|
||||
else {
|
||||
req.flash('success', 'Password set. You can use it to log in now. ');
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
res.redirect('/login');
|
||||
|
||||
});
|
||||
|
||||
|
||||
// Tracman pro
|
||||
router.route('/pro').all(mw.ensureAuth, function(req,res,next){
|
||||
router.route('/pro')
|
||||
.all(mw.ensureAuth, function(req,res,next){
|
||||
next();
|
||||
})
|
||||
|
||||
// Get info about pro
|
||||
.get(function(req,res,next){
|
||||
User.findById(req.session.passport.user, function(err, user){
|
||||
if (err){ mw.throwErr(req,err); }
|
||||
if (!user){ next(); }
|
||||
else { res.render('pro'); }
|
||||
});
|
||||
res.render('pro');
|
||||
})
|
||||
|
||||
// Join Tracman pro
|
||||
.post(function(req,res){
|
||||
User.findByIdAndUpdate(req.session.passport.user,
|
||||
User.findByIdAndUpdate(req.user.id,
|
||||
{$set:{ isPro:true }},
|
||||
function(err, user){
|
||||
if (err){ mw.throwErr(req,err); }
|
||||
if (err){ mw.throwErr(err,req); }
|
||||
else { req.flash('success','You have been signed up for pro. '); }
|
||||
res.redirect('/map');
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
'use strict';
|
||||
|
||||
const router = require('express').Router(),
|
||||
mw = require('../middleware.js'),
|
||||
mail = require('../mail.js');
|
||||
|
||||
router.route('/mail').get(function(req,res,next){
|
||||
mail.send({
|
||||
to: `"Keith Irwin" <hypergeek14@gmail.com>`,
|
||||
from: mail.from,
|
||||
subject: 'Test email',
|
||||
text: mail.text("Looks like everything's working! "),
|
||||
html: mail.html("<p>Looks like everything's working! </p>")
|
||||
}).then(function(){
|
||||
console.log("Test email should have sent...");
|
||||
res.sendStatus(200);
|
||||
}).catch(function(err){
|
||||
mw.throwErr(err,req);
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
router.route('/password').get(function(req,res){
|
||||
res.render('password');
|
||||
});
|
||||
|
||||
module.exports = router;
|
|
@ -79,7 +79,7 @@ const
|
|||
// console.log(`Setting local variables for request to ${req.path}.`);
|
||||
|
||||
// User account
|
||||
res.locals.user = req.user;
|
||||
// res.locals.user = req.user;
|
||||
// console.log(`User set as ${res.locals.user}. `);
|
||||
|
||||
// Flash messages
|
||||
|
@ -103,6 +103,11 @@ const
|
|||
// Site administration
|
||||
app.use( '/admin', require('./config/routes/admin.js') );
|
||||
|
||||
// Testing
|
||||
if (env.mode == 'development') {
|
||||
app.use( '/test', require('./config/routes/test.js' ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Errors */ {
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
{% extends 'templates/base.html' %}
|
||||
{% block title %}{{super()}} | Set Password{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
{{super()}}
|
||||
<link rel="stylesheet" type="text/css" href="/static/css/form.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
<section class='container'>
|
||||
|
||||
<h1>Set Password</h1>
|
||||
|
||||
<form id='password-form' role="form" method="post">
|
||||
<style>
|
||||
#password input {
|
||||
min-width: 40%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<p>Your password must be at least 8 characters long. You can use any letter, number, symbol, emoji, or spaces. Your password will be stored as a secure hash on the server. </p>
|
||||
|
||||
<div id='password' class='form-group' title="Type your new password here">
|
||||
<input class='form-control' name="password" type="password" placeholder="enter password" minlength="8" maxlength="160">
|
||||
<input class='form-control' name="repassword" type="password" placeholder="retype password" minlength="8" maxlength="160">
|
||||
</div>
|
||||
|
||||
<div id='submit-group' class='form-group flexbox' style="padding:0 0 60px; justify-content:space-around">
|
||||
<input class='btn yellow' style="width:50%; background:#333" type="submit" value="Save">
|
||||
<a href="#" class='btn'>cancel</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</section>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block javascript %}
|
||||
{{super()}}
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -97,7 +97,7 @@
|
|||
</div>
|
||||
|
||||
<div id='password-delete' class='form-group'>
|
||||
<a class='underline' href="/settings/password?next=/settings" 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 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 class='red underline' style="text-align:right" href="#" onclick="deleteAccount()" title="Permently delete your Tracman account. ">Delete account</a>
|
||||
</div>
|
||||
|
||||
|
|
Loading…
Reference in New Issue