From 25058f087d7060215de620876f7b0cbeda554d66 Mon Sep 17 00:00:00 2001 From: Ben Hardill Date: Thu, 23 Mar 2017 14:07:42 +0000 Subject: [PATCH] Added password reset --- index.js | 80 +++++++++++++++++++++++++++++++ models/lostPassword.js | 15 ++++++ package.json | 3 +- sendemail.js | 59 +++++++++++++++++++++++ views/email/resetPasswordHTML.ejs | 16 +++++++ views/email/resetPasswordText.ejs | 11 +++++ views/pages/changePassword.ejs | 49 +++++++++++++++++++ views/pages/login.ejs | 1 + views/pages/lostPassword.ejs | 39 +++++++++++++++ views/pages/register.ejs | 6 +++ 10 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 models/lostPassword.js create mode 100644 sendemail.js create mode 100644 views/email/resetPasswordHTML.ejs create mode 100644 views/email/resetPasswordText.ejs create mode 100644 views/pages/changePassword.ejs create mode 100644 views/pages/lostPassword.ejs diff --git a/index.js b/index.js index f6ec0bb..0149440 100644 --- a/index.js +++ b/index.js @@ -117,6 +117,8 @@ var Account = require('./models/account'); var oauthModels = require('./models/oauth'); var Devices = require('./models/devices'); var Topics = require('./models/topics'); +var LostPassword = require('./models/lostPassword'); + Account.findOne({username: mqtt_user}, function(error, account){ if (!error && !account) { @@ -331,6 +333,84 @@ app.post('/newuser', function(req,res){ }); +app.get('/changePassword/:key',function(req, res, next){ + var uuid = req.params.key; + LostPassword.findOne({uuid: uuid}).populate('user').exec(function(error, lostPassword){ + if (!error && lostPassword) { + req.login(lostPassword.user, function(err){ + if (!err){ + lostPassword.remove(); + res.redirect('/changePassword'); + } else { + console.log(err); + res.redirect('/'); + } + }) + } else { + res.redirect('/'); + } + }); +}); + +app.get('/changePassword', ensureAuthenticated, function(req, res, next){ + res.render('pages/changePassword', {user: req.user}); +}); + +app.post('/changePassword', ensureAuthenticated, function(req, res, next){ + Account.findOne({username: req.user.username}, function (err, user){ + if (!err && user) { + user.setPassword(req.body.password, function(e,u){ + // var s = Buffer.from(account.salt, 'hex').toString('base64'); + // var h = Buffer.from(account.hash, 'hex').toString(('base64')); + var mqttPass = "PBKDF2$sha256$901$" + user.salt + "$" + user.hash; + u.mqttPass = mqttPass; + u.save(function(error){ + if (!error) { + console.log("Chagned %s's password", u.username); + res.status(200).send(); + } else { + console.log(error); + res.status(400).send("Problem setting new password"); + } + }); + }); + } else { + console.log(err); + res.status(400).send("Problem setting new password"); + } + }); +}); + +app.get('/lostPassword', function(req, res, next){ + res.render('pages/lostPassword', { user: req.user}); +}); + +var sendemail = require('./sendemail'); +var mailer = new sendemail(); + +app.post('/lostPassword', function(req, res, next){ + var email = req.body.email; + Account.findOne({email: email}, function(error, user){ + if (!error){ + if (user){ + var lostPassword = new LostPassword({user: user}); + console.log(lostPassword); + lostPassword.save(function(err){ + if (!err) { + res.status(200).send(); + } + console.log(lostPassword.uuid); + console.log(lostPassword.user.username); + var body = mailer.buildLostPasswordBody(lostPassword.uuid, lostPassword.user.username); + mailer.send(email, 'alexa-node-red@hardill.me.uk', 'Password Reset for Alexa Node-RED', body.text, body.html); + }); + } else { + res.status(404).send("No user found with that email address"); + } + } + }); +}); + app.get('/auth/start',oauthServer.authorize(function(applicationID, redirectURI,done){ oauthModels.Application.findOne({ oauth_id: applicationID }, function(error, application) { if (application) { diff --git a/models/lostPassword.js b/models/lostPassword.js new file mode 100644 index 0000000..878c0e3 --- /dev/null +++ b/models/lostPassword.js @@ -0,0 +1,15 @@ +var uid = require('uid2'); +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +var LostPassword = new Schema({ + uuid: { type: String, unique: true, required: true, default: function() { + return uid(42); + }}, + user: { type: Schema.Types.ObjectId, ref: 'Account' }, + createdDate: { type: Date, expires: 86400 , default: function(){ + return new Date(); + }} +}); + +module.exports = mongoose.model('LostPassword', LostPassword); \ No newline at end of file diff --git a/package.json b/package.json index c595890..b0349b3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "home-skill-web", - "version": "0.0.1", + "version": "0.0.2", "description": "", "main": "index.js", "scripts": { @@ -25,6 +25,7 @@ "mongoose-sequence": "^3.1.0", "morgan": "^1.7.0", "mqtt": "^2.0.1", + "nodemailer": "^1.11.0", "oauth2orize": "^1.5.1", "passport": "^0.3.2", "passport-http": "^0.3.0", diff --git a/sendemail.js b/sendemail.js new file mode 100644 index 0000000..fd9d8f3 --- /dev/null +++ b/sendemail.js @@ -0,0 +1,59 @@ +var fs = require("fs"); +var ejs = require('ejs'); +var path = require('path'); +var nodemailer = require('nodemailer'); + +var smtpOptions = { + host: process.env.MAIL_SERVER, + port: 587, + secure: false, + auth: { + user: process.env.MAIL_USER, + pass: process.env.MAIL_PASSWORD + } +} + +var lostPasswordTxtTemplate; +var lostPasswordHTMLTemplate; + +fs.readFile( + path.join(__dirname, 'views', 'email', 'resetPasswordText.ejs'), + "utf-8", + function(err, file){ + lostPasswordTxtTemplate = file; + }); + + +fs.readFile( + path.join(__dirname, 'views', 'email', 'resetPasswordHTML.ejs'), + "utf-8", + function(err, file){ + lostPasswordHTMLTemplate = file; + }); + +var transporter = nodemailer.createTransport(smtpOptions); + +var Mailer = function() { + +}; + +Mailer.prototype.send = function send(to, from, subject, text, html){ + var message = { + to: to, + from: from, + subject: subject, + text: text, + html: html + }; + + transporter.sendMail(message); +} + +Mailer.prototype.buildLostPasswordBody = function buildLostBody(uuid, userid){ + var body = ejs.render(lostPasswordTxtTemplate, {uuid: uuid, username: userid}); + var htmlBody = ejs.render(lostPasswordHTMLTemplate, {uuid: uuid, username: userid}); + + return {text: body, html: htmlBody }; +} + +module.exports = Mailer; \ No newline at end of file diff --git a/views/email/resetPasswordHTML.ejs b/views/email/resetPasswordHTML.ejs new file mode 100644 index 0000000..dc62533 --- /dev/null +++ b/views/email/resetPasswordHTML.ejs @@ -0,0 +1,16 @@ + + + +

Hello

+ +

A request was recently made to reset the password for your Node-RED Alexa Home Skill account.

+ +

Your username is <%= username %>.

+ +

Follow this link to reset your password

+ +https://alexa-node-red.bm.hardill.me.uk/changePassword/<%= uuid %> + +

This link will only be valid for the next 24hrs and will only work once.

+ + \ No newline at end of file diff --git a/views/email/resetPasswordText.ejs b/views/email/resetPasswordText.ejs new file mode 100644 index 0000000..270ce0a --- /dev/null +++ b/views/email/resetPasswordText.ejs @@ -0,0 +1,11 @@ +Hello + +A request was recently made to reset the password for your Node-RED Alexa Home Skill account. + +Your username is <%= username %>. + +Follow this link to reset your password + +https://alexa-node-red.bm.hardill.me.uk/changePassword/<%= uuid %> + +This link will only be valid for the next 24hrs and will only work once. \ No newline at end of file diff --git a/views/pages/changePassword.ejs b/views/pages/changePassword.ejs new file mode 100644 index 0000000..3baacb9 --- /dev/null +++ b/views/pages/changePassword.ejs @@ -0,0 +1,49 @@ +<% include ../fragments/header.ejs %> +
+
+

Enter New Password

+ + +
+ + +
+ + +
+
+<% include ../fragments/footer.ejs %> \ No newline at end of file diff --git a/views/pages/login.ejs b/views/pages/login.ejs index 655f1be..ab536c5 100644 --- a/views/pages/login.ejs +++ b/views/pages/login.ejs @@ -14,5 +14,6 @@ +

If you have forgetten your password click here

<% include ../fragments/footer.ejs %> \ No newline at end of file diff --git a/views/pages/lostPassword.ejs b/views/pages/lostPassword.ejs new file mode 100644 index 0000000..28cda85 --- /dev/null +++ b/views/pages/lostPassword.ejs @@ -0,0 +1,39 @@ +<% include ../fragments/header.ejs %> +
+
+

Enter email address

+ + +
+ + +
+
+<% include ../fragments/footer.ejs %> \ No newline at end of file diff --git a/views/pages/register.ejs b/views/pages/register.ejs index f46137d..7b02553 100644 --- a/views/pages/register.ejs +++ b/views/pages/register.ejs @@ -33,6 +33,12 @@ alert("Passwords don't match"); return; } + + if (password.length < 1) { + alert("Zero length passwords are note really a good idea"); + return; + } + var email = document.getElementById('email').value; //need to try validate the email address here: if (email.indexOf('@') == -1) {