First pass formatting with Prettier
* Added .prettierrc.json * Added .prettierignore * Formatted
This commit is contained in:
@@ -2,26 +2,25 @@
|
||||
'use strict';
|
||||
|
||||
// ENiGMA½
|
||||
const Config = require('./config.js').get;
|
||||
const Errors = require('./enig_error.js').Errors;
|
||||
const getServer = require('./listening_server.js').getServer;
|
||||
const webServerPackageName = require('./servers/content/web.js').moduleInfo.packageName;
|
||||
const User = require('./user.js');
|
||||
const userDb = require('./database.js').dbs.user;
|
||||
const Config = require('./config.js').get;
|
||||
const Errors = require('./enig_error.js').Errors;
|
||||
const getServer = require('./listening_server.js').getServer;
|
||||
const webServerPackageName = require('./servers/content/web.js').moduleInfo.packageName;
|
||||
const User = require('./user.js');
|
||||
const userDb = require('./database.js').dbs.user;
|
||||
const getISOTimestampString = require('./database.js').getISOTimestampString;
|
||||
const Log = require('./logger.js').log;
|
||||
const UserProps = require('./user_property.js');
|
||||
const Log = require('./logger.js').log;
|
||||
const UserProps = require('./user_property.js');
|
||||
|
||||
// deps
|
||||
const async = require('async');
|
||||
const crypto = require('crypto');
|
||||
const fs = require('graceful-fs');
|
||||
const url = require('url');
|
||||
const querystring = require('querystring');
|
||||
const _ = require('lodash');
|
||||
const async = require('async');
|
||||
const crypto = require('crypto');
|
||||
const fs = require('graceful-fs');
|
||||
const url = require('url');
|
||||
const querystring = require('querystring');
|
||||
const _ = require('lodash');
|
||||
|
||||
const PW_RESET_EMAIL_TEXT_TEMPLATE_DEFAULT =
|
||||
`%USERNAME%:
|
||||
const PW_RESET_EMAIL_TEXT_TEMPLATE_DEFAULT = `%USERNAME%:
|
||||
A password reset has been requested for your account on %BOARDNAME%.
|
||||
|
||||
* If this was not you, please ignore this email.
|
||||
@@ -33,34 +32,37 @@ function getWebServer() {
|
||||
}
|
||||
|
||||
class WebPasswordReset {
|
||||
|
||||
static startup(cb) {
|
||||
WebPasswordReset.registerRoutes( err => {
|
||||
WebPasswordReset.registerRoutes(err => {
|
||||
return cb(err);
|
||||
});
|
||||
}
|
||||
|
||||
static sendForgotPasswordEmail(username, cb) {
|
||||
const webServer = getServer(webServerPackageName);
|
||||
if(!webServer || !webServer.instance.isEnabled()) {
|
||||
if (!webServer || !webServer.instance.isEnabled()) {
|
||||
return cb(Errors.General('Web server is not enabled'));
|
||||
}
|
||||
|
||||
async.waterfall(
|
||||
[
|
||||
function getEmailAddress(callback) {
|
||||
if(!username) {
|
||||
if (!username) {
|
||||
return callback(Errors.MissingParam('Missing "username"'));
|
||||
}
|
||||
|
||||
User.getUserIdAndName(username, (err, userId) => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
User.getUser(userId, (err, user) => {
|
||||
if(err || !user.properties[UserProps.EmailAddress]) {
|
||||
return callback(Errors.DoesNotExist('No email address associated with this user'));
|
||||
if (err || !user.properties[UserProps.EmailAddress]) {
|
||||
return callback(
|
||||
Errors.DoesNotExist(
|
||||
'No email address associated with this user'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return callback(null, user);
|
||||
@@ -72,15 +74,15 @@ class WebPasswordReset {
|
||||
// Reset "token" is simply HEX encoded cryptographically generated bytes
|
||||
//
|
||||
crypto.randomBytes(256, (err, token) => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
token = token.toString('hex');
|
||||
|
||||
const newProperties = {
|
||||
[ UserProps.EmailPwResetToken ] : token,
|
||||
[ UserProps.EmailPwResetTokenTs ] : getISOTimestampString(),
|
||||
[UserProps.EmailPwResetToken]: token,
|
||||
[UserProps.EmailPwResetTokenTs]: getISOTimestampString(),
|
||||
};
|
||||
|
||||
// we simply place the reset token in the user's properties
|
||||
@@ -88,57 +90,84 @@ class WebPasswordReset {
|
||||
return callback(err, user);
|
||||
});
|
||||
});
|
||||
|
||||
},
|
||||
function getEmailTemplates(user, callback) {
|
||||
const config = Config();
|
||||
fs.readFile(config.contentServers.web.resetPassword.resetPassEmailText, 'utf8', (err, textTemplate) => {
|
||||
if(err) {
|
||||
textTemplate = PW_RESET_EMAIL_TEXT_TEMPLATE_DEFAULT;
|
||||
}
|
||||
fs.readFile(
|
||||
config.contentServers.web.resetPassword.resetPassEmailText,
|
||||
'utf8',
|
||||
(err, textTemplate) => {
|
||||
if (err) {
|
||||
textTemplate = PW_RESET_EMAIL_TEXT_TEMPLATE_DEFAULT;
|
||||
}
|
||||
|
||||
fs.readFile(config.contentServers.web.resetPassword.resetPassEmailHtml, 'utf8', (err, htmlTemplate) => {
|
||||
return callback(null, user, textTemplate, htmlTemplate);
|
||||
});
|
||||
});
|
||||
fs.readFile(
|
||||
config.contentServers.web.resetPassword
|
||||
.resetPassEmailHtml,
|
||||
'utf8',
|
||||
(err, htmlTemplate) => {
|
||||
return callback(
|
||||
null,
|
||||
user,
|
||||
textTemplate,
|
||||
htmlTemplate
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
function buildAndSendEmail(user, textTemplate, htmlTemplate, callback) {
|
||||
const sendMail = require('./email.js').sendMail;
|
||||
|
||||
const resetUrl = webServer.instance.buildUrl(`/reset_password?token=${user.properties[UserProps.EmailPwResetToken]}`);
|
||||
const resetUrl = webServer.instance.buildUrl(
|
||||
`/reset_password?token=${
|
||||
user.properties[UserProps.EmailPwResetToken]
|
||||
}`
|
||||
);
|
||||
|
||||
function replaceTokens(s) {
|
||||
return s
|
||||
.replace(/%BOARDNAME%/g, Config().general.boardName)
|
||||
.replace(/%USERNAME%/g, user.username)
|
||||
.replace(/%TOKEN%/g, user.properties[UserProps.EmailPwResetToken])
|
||||
.replace(/%RESET_URL%/g, resetUrl)
|
||||
;
|
||||
.replace(/%BOARDNAME%/g, Config().general.boardName)
|
||||
.replace(/%USERNAME%/g, user.username)
|
||||
.replace(
|
||||
/%TOKEN%/g,
|
||||
user.properties[UserProps.EmailPwResetToken]
|
||||
)
|
||||
.replace(/%RESET_URL%/g, resetUrl);
|
||||
}
|
||||
|
||||
textTemplate = replaceTokens(textTemplate);
|
||||
if(htmlTemplate) {
|
||||
if (htmlTemplate) {
|
||||
htmlTemplate = replaceTokens(htmlTemplate);
|
||||
}
|
||||
|
||||
const message = {
|
||||
to : `${user.properties[UserProps.RealName]||user.username} <${user.properties[UserProps.EmailAddress]}>`,
|
||||
to: `${user.properties[UserProps.RealName] || user.username} <${
|
||||
user.properties[UserProps.EmailAddress]
|
||||
}>`,
|
||||
// from will be filled in
|
||||
subject : 'Forgot Password',
|
||||
text : textTemplate,
|
||||
html : htmlTemplate,
|
||||
subject: 'Forgot Password',
|
||||
text: textTemplate,
|
||||
html: htmlTemplate,
|
||||
};
|
||||
|
||||
sendMail(message, (err, info) => {
|
||||
if(err) {
|
||||
Log.warn( { error : err.message }, 'Failed sending password reset email' );
|
||||
if (err) {
|
||||
Log.warn(
|
||||
{ error: err.message },
|
||||
'Failed sending password reset email'
|
||||
);
|
||||
} else {
|
||||
Log.info( { info : info }, 'Successfully sent password reset email');
|
||||
Log.info(
|
||||
{ info: info },
|
||||
'Successfully sent password reset email'
|
||||
);
|
||||
}
|
||||
|
||||
return callback(err);
|
||||
});
|
||||
}
|
||||
},
|
||||
],
|
||||
err => {
|
||||
return cb(err);
|
||||
@@ -153,27 +182,27 @@ class WebPasswordReset {
|
||||
|
||||
static registerRoutes(cb) {
|
||||
const webServer = getWebServer();
|
||||
if(!webServer) {
|
||||
return cb(null); // no webserver enabled
|
||||
if (!webServer) {
|
||||
return cb(null); // no webserver enabled
|
||||
}
|
||||
|
||||
if(!webServer.instance.isEnabled()) {
|
||||
return cb(null); // no error, but we're not serving web stuff
|
||||
if (!webServer.instance.isEnabled()) {
|
||||
return cb(null); // no error, but we're not serving web stuff
|
||||
}
|
||||
|
||||
[
|
||||
{
|
||||
// this is the page displayed to user when they GET it
|
||||
method : 'GET',
|
||||
path : '^\\/reset_password\\?token\\=[a-f0-9]+$', // Config.contentServers.web.forgotPasswordPageTemplate
|
||||
handler : WebPasswordReset.routeResetPasswordGet,
|
||||
method: 'GET',
|
||||
path: '^\\/reset_password\\?token\\=[a-f0-9]+$', // Config.contentServers.web.forgotPasswordPageTemplate
|
||||
handler: WebPasswordReset.routeResetPasswordGet,
|
||||
},
|
||||
// POST handler for performing the actual reset
|
||||
{
|
||||
method : 'POST',
|
||||
path : '^\\/reset_password$',
|
||||
handler : WebPasswordReset.routeResetPasswordPost,
|
||||
}
|
||||
method: 'POST',
|
||||
path: '^\\/reset_password$',
|
||||
handler: WebPasswordReset.routeResetPasswordPost,
|
||||
},
|
||||
].forEach(r => {
|
||||
webServer.instance.addRoute(r);
|
||||
});
|
||||
@@ -181,7 +210,6 @@ class WebPasswordReset {
|
||||
return cb(null);
|
||||
}
|
||||
|
||||
|
||||
static fileNotFound(webServer, resp) {
|
||||
return webServer.instance.fileNotFound(resp);
|
||||
}
|
||||
@@ -194,13 +222,19 @@ class WebPasswordReset {
|
||||
async.waterfall(
|
||||
[
|
||||
function validateToken(callback) {
|
||||
User.getUserIdsWithProperty('email_password_reset_token', token, (err, userIds) => {
|
||||
if(userIds && userIds.length === 1) {
|
||||
return callback(null, userIds[0]);
|
||||
}
|
||||
User.getUserIdsWithProperty(
|
||||
'email_password_reset_token',
|
||||
token,
|
||||
(err, userIds) => {
|
||||
if (userIds && userIds.length === 1) {
|
||||
return callback(null, userIds[0]);
|
||||
}
|
||||
|
||||
return callback(Errors.Invalid('Invalid password reset token'));
|
||||
});
|
||||
return callback(
|
||||
Errors.Invalid('Invalid password reset token')
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
function getUser(userId, callback) {
|
||||
User.getUser(userId, (err, user) => {
|
||||
@@ -215,19 +249,24 @@ class WebPasswordReset {
|
||||
}
|
||||
|
||||
static routeResetPasswordGet(req, resp) {
|
||||
const webServer = getWebServer(); // must be valid, we just got a req!
|
||||
const webServer = getWebServer(); // must be valid, we just got a req!
|
||||
|
||||
const urlParts = url.parse(req.url, true);
|
||||
const token = urlParts.query && urlParts.query.token;
|
||||
const urlParts = url.parse(req.url, true);
|
||||
const token = urlParts.query && urlParts.query.token;
|
||||
|
||||
if(!token) {
|
||||
if (!token) {
|
||||
return WebPasswordReset.accessDenied(webServer, resp);
|
||||
}
|
||||
|
||||
WebPasswordReset.getUserByToken(token, (err, user) => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
// assume it's expired
|
||||
return webServer.instance.respondWithError(resp, 410, 'Invalid or expired reset link.', 'Expired Link');
|
||||
return webServer.instance.respondWithError(
|
||||
resp,
|
||||
410,
|
||||
'Invalid or expired reset link.',
|
||||
'Expired Link'
|
||||
);
|
||||
}
|
||||
|
||||
const postResetUrl = webServer.instance.buildUrl('/reset_password');
|
||||
@@ -236,14 +275,11 @@ class WebPasswordReset {
|
||||
return webServer.instance.routeTemplateFilePage(
|
||||
config.contentServers.web.resetPassword.resetPageTemplate,
|
||||
(templateData, preprocessFinished) => {
|
||||
|
||||
const finalPage = templateData
|
||||
.replace(/%BOARDNAME%/g, config.general.boardName)
|
||||
.replace(/%USERNAME%/g, user.username)
|
||||
.replace(/%TOKEN%/g, token)
|
||||
.replace(/%RESET_URL%/g, postResetUrl)
|
||||
;
|
||||
|
||||
.replace(/%BOARDNAME%/g, config.general.boardName)
|
||||
.replace(/%USERNAME%/g, user.username)
|
||||
.replace(/%TOKEN%/g, token)
|
||||
.replace(/%RESET_URL%/g, postResetUrl);
|
||||
return preprocessFinished(null, finalPage);
|
||||
},
|
||||
resp
|
||||
@@ -252,7 +288,7 @@ class WebPasswordReset {
|
||||
}
|
||||
|
||||
static routeResetPasswordPost(req, resp) {
|
||||
const webServer = getWebServer(); // must be valid, we just got a req!
|
||||
const webServer = getWebServer(); // must be valid, we just got a req!
|
||||
|
||||
let bodyData = '';
|
||||
req.on('data', data => {
|
||||
@@ -260,39 +296,53 @@ class WebPasswordReset {
|
||||
});
|
||||
|
||||
function badRequest() {
|
||||
return webServer.instance.respondWithError(resp, 400, 'Bad Request.', 'Bad Request');
|
||||
return webServer.instance.respondWithError(
|
||||
resp,
|
||||
400,
|
||||
'Bad Request.',
|
||||
'Bad Request'
|
||||
);
|
||||
}
|
||||
|
||||
req.on('end', () => {
|
||||
const formData = querystring.parse(bodyData);
|
||||
|
||||
const config = Config();
|
||||
if(!formData.token || !formData.password || !formData.confirm_password ||
|
||||
if (
|
||||
!formData.token ||
|
||||
!formData.password ||
|
||||
!formData.confirm_password ||
|
||||
formData.password !== formData.confirm_password ||
|
||||
formData.password.length < config.users.passwordMin || formData.password.length > config.users.passwordMax)
|
||||
{
|
||||
formData.password.length < config.users.passwordMin ||
|
||||
formData.password.length > config.users.passwordMax
|
||||
) {
|
||||
return badRequest();
|
||||
}
|
||||
|
||||
WebPasswordReset.getUserByToken(formData.token, (err, user) => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
return badRequest();
|
||||
}
|
||||
|
||||
user.setNewAuthCredentials(formData.password, err => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
return badRequest();
|
||||
}
|
||||
|
||||
// delete assoc properties - no need to wait for completion
|
||||
user.removeProperties([ UserProps.EmailPwResetToken, UserProps.EmailPwResetTokenTs ]);
|
||||
user.removeProperties([
|
||||
UserProps.EmailPwResetToken,
|
||||
UserProps.EmailPwResetTokenTs,
|
||||
]);
|
||||
|
||||
if(true === _.get(config, 'users.unlockAtEmailPwReset')) {
|
||||
if (true === _.get(config, 'users.unlockAtEmailPwReset')) {
|
||||
Log.info(
|
||||
{ username : user.username, userId : user.userId },
|
||||
{ username: user.username, userId: user.userId },
|
||||
'Remove any lock on account due to password reset policy'
|
||||
);
|
||||
user.unlockAccount( () => { /* dummy */ } );
|
||||
user.unlockAccount(() => {
|
||||
/* dummy */
|
||||
});
|
||||
}
|
||||
|
||||
resp.writeHead(200);
|
||||
@@ -304,7 +354,6 @@ class WebPasswordReset {
|
||||
}
|
||||
|
||||
function performMaintenanceTask(args, cb) {
|
||||
|
||||
const forgotPassExpireTime = args[0] || '24 hours';
|
||||
|
||||
// remove all reset token associated properties older than |forgotPassExpireTime|
|
||||
@@ -317,13 +366,16 @@ function performMaintenanceTask(args, cb) {
|
||||
AND DATETIME("now") >= DATETIME(prop_value, "+${forgotPassExpireTime}")
|
||||
) AND prop_name IN ("email_password_reset_token_ts", "email_password_reset_token");`,
|
||||
err => {
|
||||
if(err) {
|
||||
Log.warn( { error : err.message }, 'Failed deleting old email reset tokens');
|
||||
if (err) {
|
||||
Log.warn(
|
||||
{ error: err.message },
|
||||
'Failed deleting old email reset tokens'
|
||||
);
|
||||
}
|
||||
return cb(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
exports.WebPasswordReset = WebPasswordReset;
|
||||
exports.performMaintenanceTask = performMaintenanceTask;
|
||||
exports.WebPasswordReset = WebPasswordReset;
|
||||
exports.performMaintenanceTask = performMaintenanceTask;
|
||||
|
||||
Reference in New Issue
Block a user