diff --git a/.gitignore b/.gitignore index d6a3c0d8..65b7e5e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ configs/*.json uploads/img/* +gulp/dist/ diff --git a/controllers/forms.js b/controllers/forms.js index 7afcba05..3849fc13 100644 --- a/controllers/forms.js +++ b/controllers/forms.js @@ -18,6 +18,7 @@ const express = require('express') , dismissReports = require(__dirname+'/../models/forms/dismiss-report.js') , dismissGlobalReports = require(__dirname+'/../models/forms/dismissglobalreport.js') , loginAccount = require(__dirname+'/../models/forms/login.js') + , changePassword = require(__dirname+'/../models/forms/changepassword.js') , registerAccount = require(__dirname+'/../models/forms/register.js') , hasPerms = require(__dirname+'/../helpers/haspermsmiddleware.js') , numberConverter = require(__dirname+'/../helpers/number-converter.js') @@ -56,6 +57,58 @@ router.post('/login', (req, res, next) => { }); +//change password +router.post('/changepassword', async (req, res, next) => { + + const errors = []; + + //check exist + if (!req.body.username || req.body.username.length <= 0) { + errors.push('Missing username'); + } + if (!req.body.password || req.body.password.length <= 0) { + errors.push('Missing password'); + } + if (!req.body.newpassword || req.body.newpassword.length <= 0) { + errors.push('Missing new password'); + } + if (!req.body.newpasswordconfirm || req.body.newpasswordconfirm.length <= 0) { + errors.push('Missing new password confirmation'); + } + + //check too long + if (req.body.username && req.body.username.length > 50) { + errors.push('Username must be 50 characters or less'); + } + if (req.body.password && req.body.password.length > 100) { + errors.push('Password must be 100 characters or less'); + } + if (req.body.newpassword && req.body.newpassword.length > 100) { + errors.push('Password must be 100 characters or less'); + } + if (req.body.newpasswordconfirm && req.body.newpasswordconfirm.length > 100) { + errors.push('Password confirmation must be 100 characters or less'); + } + if (req.body.newpassword != req.body.newpasswordconfirm) { + errors.push('New password and password confirmation must match'); + } + + if (errors.length > 0) { + return res.status(400).render('message', { + 'title': 'Bad request', + 'errors': errors, + 'redirect': '/changepassword' + }) + } + + try { + await changePassword(req, res, next); + } catch (err) { + return next(err); + } + +}); + //register account router.post('/register', (req, res, next) => { diff --git a/db/accounts.js b/db/accounts.js index 9138c218..8f3f19a1 100644 --- a/db/accounts.js +++ b/db/accounts.js @@ -31,6 +31,17 @@ module.exports = { }); }, + changePassword: async (username, newPassword) => { + const passwordHash = await bcrypt.hash(newPassword, 12); + return db.updateOne({ + '_id': username + }, { + '$set': { + 'passwordHash': passwordHash + } + }) + }, + promoteUser: (username, newlevel) => { //increase users auth level return db.updateOne({ diff --git a/models/forms/changepassword.js b/models/forms/changepassword.js new file mode 100644 index 00000000..6fb034f0 --- /dev/null +++ b/models/forms/changepassword.js @@ -0,0 +1,40 @@ +'use strict'; + +const bcrypt = require('bcrypt') + , Accounts = require(__dirname+'/../../db/accounts.js'); + +module.exports = async (req, res, next) => { + + const username = req.body.username.toLowerCase(); + const password = req.body.password; + const newPassword = req.body.newpassword; + + //fetch an account + const account = await Accounts.findOne(username); + + //if the account doesnt exist, reject + if (!account) { + return res.status(403).render('message', { + 'title': 'Forbidden', + 'message': 'Incorrect username or password', + 'redirect': redirect ? `/login?redirect=${redirect}` : '/changepassword' + }); + } + + // bcrypt compare input to saved hash + const passwordMatch = await bcrypt.compare(password, account.passwordHash); + + //if hashes matched + if (passwordMatch === true) { + //change the password + await Accounts.changePassword(username, newPassword); + return res.redirect('/login'); + } + + return res.status(403).render('message', { + 'title': 'Forbidden', + 'message': 'Incorrect username or password', + 'redirect': redirect ? `/login?redirect=${redirect}` : '/login' + }); + +} diff --git a/models/pages/changepassword.js b/models/pages/changepassword.js new file mode 100644 index 00000000..bc8cd226 --- /dev/null +++ b/models/pages/changepassword.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = (req, res, next) => { + + //render the page + res.render('changepassword', { + csrf: req.csrfToken() + }); + +} diff --git a/views/pages/changepassword.pug b/views/pages/changepassword.pug new file mode 100644 index 00000000..b0f6d586 --- /dev/null +++ b/views/pages/changepassword.pug @@ -0,0 +1,22 @@ +extends ../layout.pug + +block head + title Login + +block content + section.form-wrapper + form.form-post(action='/forms/changepassword' method='POST') + input(type='hidden' name='_csrf' value=csrf) + section.postform-section + .postform-label Username + input#username(type='text', name='username', placeholder='username' maxlength='50') + section.postform-section + .postform-label Existing Password + input#password(type='password', name='password', maxlength='100') + section.postform-section + .postform-label New Password + input#password(type='password', name='newpassword', maxlength='100') + section.postform-section + .postform-label Confirm New Password + input#password(type='password', name='newpasswordconfirm', maxlength='100') + input(type='submit', value='submit')