From 4c89497cc42d980d35f9e209bfc30e1740ef7be5 Mon Sep 17 00:00:00 2001 From: pichalite Date: Fri, 7 Apr 2017 20:57:00 +0000 Subject: [PATCH 1/2] Closes #5521 --- package.json | 3 +- .../language/en-GB/admin/settings/user.json | 1 + public/language/en-GB/user.json | 1 + public/src/client/account/edit/password.js | 5 ++- public/src/client/register.js | 5 ++- src/controllers/accounts/edit.js | 38 ++++++++---------- src/controllers/index.js | 40 +++++++++++++++---- src/meta/js.js | 9 ++++- src/views/admin/settings/user.tpl | 10 +++++ 9 files changed, 77 insertions(+), 35 deletions(-) diff --git a/package.json b/package.json index e7a21bc5c0..95ac88b2ed 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,8 @@ "validator": "^6.1.0", "winston": "^2.1.0", "xml": "^1.0.1", - "xregexp": "~3.1.0" + "xregexp": "~3.1.0", + "zxcvbn": "^4.4.2" }, "devDependencies": { "coveralls": "^2.11.14", diff --git a/public/language/en-GB/admin/settings/user.json b/public/language/en-GB/admin/settings/user.json index bdabb075e9..b8f51c9288 100644 --- a/public/language/en-GB/admin/settings/user.json +++ b/public/language/en-GB/admin/settings/user.json @@ -37,6 +37,7 @@ "min-username-length": "Minimum Username Length", "max-username-length": "Maximum Username Length", "min-password-length": "Minimum Password Length", + "min-password-strength": "Minimum Password Strength", "max-about-me-length": "Maximum About Me Length", "terms-of-use": "Forum Terms of Use (Leave blank to disable)", "user-search": "User Search", diff --git a/public/language/en-GB/user.json b/public/language/en-GB/user.json index 4013d5898c..8a5206d272 100644 --- a/public/language/en-GB/user.json +++ b/public/language/en-GB/user.json @@ -62,6 +62,7 @@ "username_taken_workaround": "The username you requested was already taken, so we have altered it slightly. You are now known as %1", "password_same_as_username": "Your password is the same as your username, please select another password.", "password_same_as_email": "Your password is the same as your email, please select another password.", + "weak_password": "Weak password.", "upload_picture": "Upload picture", "upload_a_picture": "Upload a picture", diff --git a/public/src/client/account/edit/password.js b/public/src/client/account/edit/password.js index d2239b36a0..44723014ce 100644 --- a/public/src/client/account/edit/password.js +++ b/public/src/client/account/edit/password.js @@ -1,7 +1,7 @@ 'use strict'; -define('forum/account/edit/password', ['forum/account/header', 'translator'], function (header, translator) { +define('forum/account/edit/password', ['forum/account/header', 'translator', 'zxcvbn'], function (header, translator, zxcvbn) { var AccountEditPassword = {}; AccountEditPassword.init = function () { @@ -20,6 +20,7 @@ define('forum/account/edit/password', ['forum/account/header', 'translator'], fu var passwordsmatch = false; function onPasswordChanged() { + var passwordStrength = zxcvbn(password.val()); passwordvalid = false; if (password.val().length < ajaxify.data.minimumPasswordLength) { showError(password_notify, '[[user:change_password_error_length]]'); @@ -29,6 +30,8 @@ define('forum/account/edit/password', ['forum/account/header', 'translator'], fu showError(password_notify, '[[user:password_same_as_username]]'); } else if (password.val() === ajaxify.data.email) { showError(password_notify, '[[user:password_same_as_email]]'); + } else if (passwordStrength.score < ajaxify.data.minimumPasswordStrength) { + showError(password_notify, '[[user:weak_password]]'); } else { showSuccess(password_notify); passwordvalid = true; diff --git a/public/src/client/register.js b/public/src/client/register.js index 329762271e..8070263906 100644 --- a/public/src/client/register.js +++ b/public/src/client/register.js @@ -1,7 +1,7 @@ 'use strict'; -define('forum/register', ['translator'], function (translator) { +define('forum/register', ['translator', 'zxcvbn'], function (translator, zxcvbn) { var Register = {}; var validationError = false; var successIcon = ''; @@ -170,6 +170,7 @@ define('forum/register', ['translator'], function (translator) { function validatePassword(password, password_confirm) { var password_notify = $('#password-notify'); var password_confirm_notify = $('#password-confirm-notify'); + var passwordStrength = zxcvbn(password); if (password.length < ajaxify.data.minimumPasswordLength) { showError(password_notify, '[[user:change_password_error_length]]'); @@ -181,6 +182,8 @@ define('forum/register', ['translator'], function (translator) { showError(password_notify, '[[user:password_same_as_username]]'); } else if (password === $('#email').val()) { showError(password_notify, '[[user:password_same_as_email]]'); + } else if (passwordStrength.score < ajaxify.data.minimumPasswordStrength) { + showError(password_notify, '[[user:weak_password]]'); } else { showSuccess(password_notify, successIcon); } diff --git a/src/controllers/accounts/edit.js b/src/controllers/accounts/edit.js index 3c2e57bec4..71cf7717c8 100644 --- a/src/controllers/accounts/edit.js +++ b/src/controllers/accounts/edit.js @@ -36,15 +36,12 @@ editController.get = function (req, res, callback) { }); userData.title = '[[pages:account/edit, ' + userData.username + ']]'; - userData.breadcrumbs = helpers.buildBreadcrumbs([ - { - text: userData.username, - url: '/user/' + userData.userslug, - }, - { - text: '[[user:edit]]', - }, - ]); + userData.breadcrumbs = helpers.buildBreadcrumbs([{ + text: userData.username, + url: '/user/' + userData.userslug, + }, { + text: '[[user:edit]]', + }]); userData.editButtons = []; plugins.fireHook('filter:user.account.edit', userData, function (err, userData) { @@ -80,22 +77,19 @@ function renderRoute(name, req, res, next) { if (name === 'password') { userData.minimumPasswordLength = parseInt(meta.config.minimumPasswordLength, 10); + userData.minimumPasswordStrength = parseInt(meta.config.minimumPasswordStrength || 0, 10); } userData.title = '[[pages:account/edit/' + name + ', ' + userData.username + ']]'; - userData.breadcrumbs = helpers.buildBreadcrumbs([ - { - text: userData.username, - url: '/user/' + userData.userslug, - }, - { - text: '[[user:edit]]', - url: '/user/' + userData.userslug + '/edit', - }, - { - text: '[[user:' + name + ']]', - }, - ]); + userData.breadcrumbs = helpers.buildBreadcrumbs([{ + text: userData.username, + url: '/user/' + userData.userslug, + }, { + text: '[[user:edit]]', + url: '/user/' + userData.userslug + '/edit', + }, { + text: '[[user:' + name + ']]', + }]); res.render('account/edit/' + name, userData); }); diff --git a/src/controllers/index.js b/src/controllers/index.js index 93b8e9a383..ac7c514d83 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -48,7 +48,11 @@ Controllers.home = function (req, res, next) { var hook = 'action:homepage.get:' + route; if (plugins.hasListeners(hook)) { - return plugins.fireHook(hook, { req: req, res: res, next: next }); + return plugins.fireHook(hook, { + req: req, + res: res, + next: next, + }); } if (route === 'categories' || route === '/') { @@ -85,7 +89,12 @@ Controllers.reset = function (req, res, next) { displayExpiryNotice: req.session.passwordExpired, code: req.params.code, minimumPasswordLength: parseInt(meta.config.minimumPasswordLength, 10), - breadcrumbs: helpers.buildBreadcrumbs([{ text: '[[reset_password:reset_password]]', url: '/reset' }, { text: '[[reset_password:update_password]]' }]), + breadcrumbs: helpers.buildBreadcrumbs([{ + text: '[[reset_password:reset_password]]', + url: '/reset', + }, { + text: '[[reset_password:update_password]]', + }]), title: '[[pages:reset]]', }); @@ -94,7 +103,9 @@ Controllers.reset = function (req, res, next) { } else { res.render('reset', { code: null, - breadcrumbs: helpers.buildBreadcrumbs([{ text: '[[reset_password:reset_password]]' }]), + breadcrumbs: helpers.buildBreadcrumbs([{ + text: '[[reset_password:reset_password]]', + }]), title: '[[pages:reset]]', }); } @@ -124,7 +135,9 @@ Controllers.login = function (req, res, next) { data.allowLocalLogin = parseInt(meta.config.allowLocalLogin, 10) === 1 || parseInt(req.query.local, 10) === 1; data.allowRegistration = registrationType === 'normal' || registrationType === 'admin-approval' || registrationType === 'admin-approval-ip'; data.allowLoginWith = '[[login:' + allowLoginWith + ']]'; - data.breadcrumbs = helpers.buildBreadcrumbs([{ text: '[[global:login]]' }]); + data.breadcrumbs = helpers.buildBreadcrumbs([{ + text: '[[global:login]]', + }]); data.error = req.flash('error')[0] || errorText; data.title = '[[pages:login]]'; @@ -171,7 +184,11 @@ Controllers.register = function (req, res, next) { } }, function (next) { - plugins.fireHook('filter:parse.post', { postData: { content: meta.config.termsOfUse || '' } }, next); + plugins.fireHook('filter:parse.post', { + postData: { + content: meta.config.termsOfUse || '', + }, + }, next); }, ], function (err, termsOfUse) { if (err) { @@ -188,8 +205,11 @@ Controllers.register = function (req, res, next) { data.minimumUsernameLength = parseInt(meta.config.minimumUsernameLength, 10); data.maximumUsernameLength = parseInt(meta.config.maximumUsernameLength, 10); data.minimumPasswordLength = parseInt(meta.config.minimumPasswordLength, 10); + data.minimumPasswordStrength = parseInt(meta.config.minimumPasswordStrength || 0, 10); data.termsOfUse = termsOfUse.postData.content; - data.breadcrumbs = helpers.buildBreadcrumbs([{ text: '[[register:register]]' }]); + data.breadcrumbs = helpers.buildBreadcrumbs([{ + text: '[[register:register]]', + }]); data.regFormEntry = []; data.error = req.flash('error')[0] || errorText; data.title = '[[pages:register]]'; @@ -333,7 +353,9 @@ Controllers.outgoing = function (req, res, next) { res.render('outgoing', { outgoing: validator.escape(String(url)), title: meta.config.title, - breadcrumbs: helpers.buildBreadcrumbs([{ text: '[[notifications:outgoing_link]]' }]), + breadcrumbs: helpers.buildBreadcrumbs([{ + text: '[[notifications:outgoing_link]]', + }]), }); }; @@ -341,7 +363,9 @@ Controllers.termsOfUse = function (req, res, next) { if (!meta.config.termsOfUse) { return next(); } - res.render('tos', { termsOfUse: meta.config.termsOfUse }); + res.render('tos', { + termsOfUse: meta.config.termsOfUse, + }); }; Controllers.ping = function (req, res) { diff --git a/src/meta/js.js b/src/meta/js.js index 72e41c578f..95534f5d47 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -89,6 +89,7 @@ module.exports = function (Meta) { 'jqueryui.js': 'public/vendor/jquery/js/jquery-ui.js', 'buzz.js': 'public/vendor/buzz/buzz.js', 'cropper.js': './node_modules/cropperjs/dist/cropper.min.js', + 'zxcvbn.js': './node_modules/zxcvbn/dist/zxcvbn.js', }, }, }; @@ -110,7 +111,9 @@ module.exports = function (Meta) { } if (filePath.endsWith('.min.js')) { - minified = { code: buffer.toString() }; + minified = { + code: buffer.toString(), + }; return cb(); } @@ -345,7 +348,9 @@ module.exports = function (Meta) { /** * otherwise, just clean up --debug/--debug-brk options which are set up by default from the parent one */ - forkProcessParams = { execArgv: [] }; + forkProcessParams = { + execArgv: [], + }; } return forkProcessParams; diff --git a/src/views/admin/settings/user.tpl b/src/views/admin/settings/user.tpl index c7373fbeb7..adaa4f8010 100644 --- a/src/views/admin/settings/user.tpl +++ b/src/views/admin/settings/user.tpl @@ -167,6 +167,16 @@ +
+ + +
From d4de345e87db1c03da404a5d1ab3fced81e8edf6 Mon Sep 17 00:00:00 2001 From: pichalite Date: Fri, 7 Apr 2017 21:39:22 +0000 Subject: [PATCH 2/2] syntax --- src/controllers/accounts/edit.js | 37 +++++++++++++++++++------------- src/controllers/index.js | 15 +++++++------ 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/controllers/accounts/edit.js b/src/controllers/accounts/edit.js index 71cf7717c8..d659b0153f 100644 --- a/src/controllers/accounts/edit.js +++ b/src/controllers/accounts/edit.js @@ -36,12 +36,15 @@ editController.get = function (req, res, callback) { }); userData.title = '[[pages:account/edit, ' + userData.username + ']]'; - userData.breadcrumbs = helpers.buildBreadcrumbs([{ - text: userData.username, - url: '/user/' + userData.userslug, - }, { - text: '[[user:edit]]', - }]); + userData.breadcrumbs = helpers.buildBreadcrumbs([ + { + text: userData.username, + url: '/user/' + userData.userslug, + }, + { + text: '[[user:edit]]', + }, + ]); userData.editButtons = []; plugins.fireHook('filter:user.account.edit', userData, function (err, userData) { @@ -81,15 +84,19 @@ function renderRoute(name, req, res, next) { } userData.title = '[[pages:account/edit/' + name + ', ' + userData.username + ']]'; - userData.breadcrumbs = helpers.buildBreadcrumbs([{ - text: userData.username, - url: '/user/' + userData.userslug, - }, { - text: '[[user:edit]]', - url: '/user/' + userData.userslug + '/edit', - }, { - text: '[[user:' + name + ']]', - }]); + userData.breadcrumbs = helpers.buildBreadcrumbs([ + { + text: userData.username, + url: '/user/' + userData.userslug, + }, + { + text: '[[user:edit]]', + url: '/user/' + userData.userslug + '/edit', + }, + { + text: '[[user:' + name + ']]', + }, + ]); res.render('account/edit/' + name, userData); }); diff --git a/src/controllers/index.js b/src/controllers/index.js index ac7c514d83..6cebf29d8f 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -89,12 +89,15 @@ Controllers.reset = function (req, res, next) { displayExpiryNotice: req.session.passwordExpired, code: req.params.code, minimumPasswordLength: parseInt(meta.config.minimumPasswordLength, 10), - breadcrumbs: helpers.buildBreadcrumbs([{ - text: '[[reset_password:reset_password]]', - url: '/reset', - }, { - text: '[[reset_password:update_password]]', - }]), + breadcrumbs: helpers.buildBreadcrumbs([ + { + text: '[[reset_password:reset_password]]', + url: '/reset', + }, + { + text: '[[reset_password:update_password]]', + }, + ]), title: '[[pages:reset]]', });