mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-30 02:25:55 +01:00
Closes #5521
This commit is contained in:
@@ -95,7 +95,8 @@
|
|||||||
"validator": "^6.1.0",
|
"validator": "^6.1.0",
|
||||||
"winston": "^2.1.0",
|
"winston": "^2.1.0",
|
||||||
"xml": "^1.0.1",
|
"xml": "^1.0.1",
|
||||||
"xregexp": "~3.1.0"
|
"xregexp": "~3.1.0",
|
||||||
|
"zxcvbn": "^4.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"coveralls": "^2.11.14",
|
"coveralls": "^2.11.14",
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
"min-username-length": "Minimum Username Length",
|
"min-username-length": "Minimum Username Length",
|
||||||
"max-username-length": "Maximum Username Length",
|
"max-username-length": "Maximum Username Length",
|
||||||
"min-password-length": "Minimum Password Length",
|
"min-password-length": "Minimum Password Length",
|
||||||
|
"min-password-strength": "Minimum Password Strength",
|
||||||
"max-about-me-length": "Maximum About Me Length",
|
"max-about-me-length": "Maximum About Me Length",
|
||||||
"terms-of-use": "Forum Terms of Use <small>(Leave blank to disable)</small>",
|
"terms-of-use": "Forum Terms of Use <small>(Leave blank to disable)</small>",
|
||||||
"user-search": "User Search",
|
"user-search": "User Search",
|
||||||
|
|||||||
@@ -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 <strong>%1</strong>",
|
"username_taken_workaround": "The username you requested was already taken, so we have altered it slightly. You are now known as <strong>%1</strong>",
|
||||||
"password_same_as_username": "Your password is the same as your username, please select another password.",
|
"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.",
|
"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_picture": "Upload picture",
|
||||||
"upload_a_picture": "Upload a picture",
|
"upload_a_picture": "Upload a picture",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'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 = {};
|
var AccountEditPassword = {};
|
||||||
|
|
||||||
AccountEditPassword.init = function () {
|
AccountEditPassword.init = function () {
|
||||||
@@ -20,6 +20,7 @@ define('forum/account/edit/password', ['forum/account/header', 'translator'], fu
|
|||||||
var passwordsmatch = false;
|
var passwordsmatch = false;
|
||||||
|
|
||||||
function onPasswordChanged() {
|
function onPasswordChanged() {
|
||||||
|
var passwordStrength = zxcvbn(password.val());
|
||||||
passwordvalid = false;
|
passwordvalid = false;
|
||||||
if (password.val().length < ajaxify.data.minimumPasswordLength) {
|
if (password.val().length < ajaxify.data.minimumPasswordLength) {
|
||||||
showError(password_notify, '[[user:change_password_error_length]]');
|
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]]');
|
showError(password_notify, '[[user:password_same_as_username]]');
|
||||||
} else if (password.val() === ajaxify.data.email) {
|
} else if (password.val() === ajaxify.data.email) {
|
||||||
showError(password_notify, '[[user:password_same_as_email]]');
|
showError(password_notify, '[[user:password_same_as_email]]');
|
||||||
|
} else if (passwordStrength.score < ajaxify.data.minimumPasswordStrength) {
|
||||||
|
showError(password_notify, '[[user:weak_password]]');
|
||||||
} else {
|
} else {
|
||||||
showSuccess(password_notify);
|
showSuccess(password_notify);
|
||||||
passwordvalid = true;
|
passwordvalid = true;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
define('forum/register', ['translator'], function (translator) {
|
define('forum/register', ['translator', 'zxcvbn'], function (translator, zxcvbn) {
|
||||||
var Register = {};
|
var Register = {};
|
||||||
var validationError = false;
|
var validationError = false;
|
||||||
var successIcon = '';
|
var successIcon = '';
|
||||||
@@ -170,6 +170,7 @@ define('forum/register', ['translator'], function (translator) {
|
|||||||
function validatePassword(password, password_confirm) {
|
function validatePassword(password, password_confirm) {
|
||||||
var password_notify = $('#password-notify');
|
var password_notify = $('#password-notify');
|
||||||
var password_confirm_notify = $('#password-confirm-notify');
|
var password_confirm_notify = $('#password-confirm-notify');
|
||||||
|
var passwordStrength = zxcvbn(password);
|
||||||
|
|
||||||
if (password.length < ajaxify.data.minimumPasswordLength) {
|
if (password.length < ajaxify.data.minimumPasswordLength) {
|
||||||
showError(password_notify, '[[user:change_password_error_length]]');
|
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]]');
|
showError(password_notify, '[[user:password_same_as_username]]');
|
||||||
} else if (password === $('#email').val()) {
|
} else if (password === $('#email').val()) {
|
||||||
showError(password_notify, '[[user:password_same_as_email]]');
|
showError(password_notify, '[[user:password_same_as_email]]');
|
||||||
|
} else if (passwordStrength.score < ajaxify.data.minimumPasswordStrength) {
|
||||||
|
showError(password_notify, '[[user:weak_password]]');
|
||||||
} else {
|
} else {
|
||||||
showSuccess(password_notify, successIcon);
|
showSuccess(password_notify, successIcon);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,15 +36,12 @@ editController.get = function (req, res, callback) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
userData.title = '[[pages:account/edit, ' + userData.username + ']]';
|
userData.title = '[[pages:account/edit, ' + userData.username + ']]';
|
||||||
userData.breadcrumbs = helpers.buildBreadcrumbs([
|
userData.breadcrumbs = helpers.buildBreadcrumbs([{
|
||||||
{
|
text: userData.username,
|
||||||
text: userData.username,
|
url: '/user/' + userData.userslug,
|
||||||
url: '/user/' + userData.userslug,
|
}, {
|
||||||
},
|
text: '[[user:edit]]',
|
||||||
{
|
}]);
|
||||||
text: '[[user:edit]]',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
userData.editButtons = [];
|
userData.editButtons = [];
|
||||||
|
|
||||||
plugins.fireHook('filter:user.account.edit', userData, function (err, userData) {
|
plugins.fireHook('filter:user.account.edit', userData, function (err, userData) {
|
||||||
@@ -80,22 +77,19 @@ function renderRoute(name, req, res, next) {
|
|||||||
|
|
||||||
if (name === 'password') {
|
if (name === 'password') {
|
||||||
userData.minimumPasswordLength = parseInt(meta.config.minimumPasswordLength, 10);
|
userData.minimumPasswordLength = parseInt(meta.config.minimumPasswordLength, 10);
|
||||||
|
userData.minimumPasswordStrength = parseInt(meta.config.minimumPasswordStrength || 0, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
userData.title = '[[pages:account/edit/' + name + ', ' + userData.username + ']]';
|
userData.title = '[[pages:account/edit/' + name + ', ' + userData.username + ']]';
|
||||||
userData.breadcrumbs = helpers.buildBreadcrumbs([
|
userData.breadcrumbs = helpers.buildBreadcrumbs([{
|
||||||
{
|
text: userData.username,
|
||||||
text: userData.username,
|
url: '/user/' + userData.userslug,
|
||||||
url: '/user/' + userData.userslug,
|
}, {
|
||||||
},
|
text: '[[user:edit]]',
|
||||||
{
|
url: '/user/' + userData.userslug + '/edit',
|
||||||
text: '[[user:edit]]',
|
}, {
|
||||||
url: '/user/' + userData.userslug + '/edit',
|
text: '[[user:' + name + ']]',
|
||||||
},
|
}]);
|
||||||
{
|
|
||||||
text: '[[user:' + name + ']]',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
res.render('account/edit/' + name, userData);
|
res.render('account/edit/' + name, userData);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -48,7 +48,11 @@ Controllers.home = function (req, res, next) {
|
|||||||
var hook = 'action:homepage.get:' + route;
|
var hook = 'action:homepage.get:' + route;
|
||||||
|
|
||||||
if (plugins.hasListeners(hook)) {
|
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 === '/') {
|
if (route === 'categories' || route === '/') {
|
||||||
@@ -85,7 +89,12 @@ Controllers.reset = function (req, res, next) {
|
|||||||
displayExpiryNotice: req.session.passwordExpired,
|
displayExpiryNotice: req.session.passwordExpired,
|
||||||
code: req.params.code,
|
code: req.params.code,
|
||||||
minimumPasswordLength: parseInt(meta.config.minimumPasswordLength, 10),
|
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]]',
|
title: '[[pages:reset]]',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -94,7 +103,9 @@ Controllers.reset = function (req, res, next) {
|
|||||||
} else {
|
} else {
|
||||||
res.render('reset', {
|
res.render('reset', {
|
||||||
code: null,
|
code: null,
|
||||||
breadcrumbs: helpers.buildBreadcrumbs([{ text: '[[reset_password:reset_password]]' }]),
|
breadcrumbs: helpers.buildBreadcrumbs([{
|
||||||
|
text: '[[reset_password:reset_password]]',
|
||||||
|
}]),
|
||||||
title: '[[pages:reset]]',
|
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.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.allowRegistration = registrationType === 'normal' || registrationType === 'admin-approval' || registrationType === 'admin-approval-ip';
|
||||||
data.allowLoginWith = '[[login:' + allowLoginWith + ']]';
|
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.error = req.flash('error')[0] || errorText;
|
||||||
data.title = '[[pages:login]]';
|
data.title = '[[pages:login]]';
|
||||||
|
|
||||||
@@ -171,7 +184,11 @@ Controllers.register = function (req, res, next) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
function (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) {
|
], function (err, termsOfUse) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -188,8 +205,11 @@ Controllers.register = function (req, res, next) {
|
|||||||
data.minimumUsernameLength = parseInt(meta.config.minimumUsernameLength, 10);
|
data.minimumUsernameLength = parseInt(meta.config.minimumUsernameLength, 10);
|
||||||
data.maximumUsernameLength = parseInt(meta.config.maximumUsernameLength, 10);
|
data.maximumUsernameLength = parseInt(meta.config.maximumUsernameLength, 10);
|
||||||
data.minimumPasswordLength = parseInt(meta.config.minimumPasswordLength, 10);
|
data.minimumPasswordLength = parseInt(meta.config.minimumPasswordLength, 10);
|
||||||
|
data.minimumPasswordStrength = parseInt(meta.config.minimumPasswordStrength || 0, 10);
|
||||||
data.termsOfUse = termsOfUse.postData.content;
|
data.termsOfUse = termsOfUse.postData.content;
|
||||||
data.breadcrumbs = helpers.buildBreadcrumbs([{ text: '[[register:register]]' }]);
|
data.breadcrumbs = helpers.buildBreadcrumbs([{
|
||||||
|
text: '[[register:register]]',
|
||||||
|
}]);
|
||||||
data.regFormEntry = [];
|
data.regFormEntry = [];
|
||||||
data.error = req.flash('error')[0] || errorText;
|
data.error = req.flash('error')[0] || errorText;
|
||||||
data.title = '[[pages:register]]';
|
data.title = '[[pages:register]]';
|
||||||
@@ -333,7 +353,9 @@ Controllers.outgoing = function (req, res, next) {
|
|||||||
res.render('outgoing', {
|
res.render('outgoing', {
|
||||||
outgoing: validator.escape(String(url)),
|
outgoing: validator.escape(String(url)),
|
||||||
title: meta.config.title,
|
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) {
|
if (!meta.config.termsOfUse) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
res.render('tos', { termsOfUse: meta.config.termsOfUse });
|
res.render('tos', {
|
||||||
|
termsOfUse: meta.config.termsOfUse,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Controllers.ping = function (req, res) {
|
Controllers.ping = function (req, res) {
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ module.exports = function (Meta) {
|
|||||||
'jqueryui.js': 'public/vendor/jquery/js/jquery-ui.js',
|
'jqueryui.js': 'public/vendor/jquery/js/jquery-ui.js',
|
||||||
'buzz.js': 'public/vendor/buzz/buzz.js',
|
'buzz.js': 'public/vendor/buzz/buzz.js',
|
||||||
'cropper.js': './node_modules/cropperjs/dist/cropper.min.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')) {
|
if (filePath.endsWith('.min.js')) {
|
||||||
minified = { code: buffer.toString() };
|
minified = {
|
||||||
|
code: buffer.toString(),
|
||||||
|
};
|
||||||
return cb();
|
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
|
* otherwise, just clean up --debug/--debug-brk options which are set up by default from the parent one
|
||||||
*/
|
*/
|
||||||
forkProcessParams = { execArgv: [] };
|
forkProcessParams = {
|
||||||
|
execArgv: [],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return forkProcessParams;
|
return forkProcessParams;
|
||||||
|
|||||||
@@ -167,6 +167,16 @@
|
|||||||
<label>[[admin/settings/user:min-password-length]]</label>
|
<label>[[admin/settings/user:min-password-length]]</label>
|
||||||
<input type="text" class="form-control" value="6" data-field="minimumPasswordLength">
|
<input type="text" class="form-control" value="6" data-field="minimumPasswordLength">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>[[admin/settings/user:min-password-strength]]</label>
|
||||||
|
<select class="form-control" data-field="minimumPasswordStrength">
|
||||||
|
<option value="0">0 - too guessable: risky password</option>
|
||||||
|
<option value="1">1 - very guessable</option>
|
||||||
|
<option value="2">2 - somewhat guessable</option>
|
||||||
|
<option value="3">3 - safely unguessable</option>
|
||||||
|
<option value="4">4 - very unguessable</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>[[admin/settings/user:max-about-me-length]]</label>
|
<label>[[admin/settings/user:max-about-me-length]]</label>
|
||||||
<input type="text" class="form-control" value="500" data-field="maximumAboutMeLength">
|
<input type="text" class="form-control" value="500" data-field="maximumAboutMeLength">
|
||||||
|
|||||||
Reference in New Issue
Block a user