123 lines
4.1 KiB
JavaScript
123 lines
4.1 KiB
JavaScript
|
const _ = require('lodash');
|
||
|
|
||
|
const validator = require('./validator');
|
||
|
|
||
|
const i18n = require('../../../shared/i18n');
|
||
|
const settingsCache = require('../../services/settings/cache');
|
||
|
const urlUtils = require('../../../shared/url-utils');
|
||
|
|
||
|
/**
|
||
|
* Counts repeated characters in a string. When 50% or more characters are the same,
|
||
|
* we return false and therefore invalidate the string.
|
||
|
* @param {String} stringToTest The password string to check.
|
||
|
* @return {Boolean}
|
||
|
*/
|
||
|
function characterOccurance(stringToTest) {
|
||
|
const chars = {};
|
||
|
let allowedOccurancy;
|
||
|
let valid = true;
|
||
|
|
||
|
stringToTest = _.toString(stringToTest);
|
||
|
allowedOccurancy = stringToTest.length / 2;
|
||
|
|
||
|
// Loop through string and accumulate character counts
|
||
|
_.each(stringToTest, function (char) {
|
||
|
if (!chars[char]) {
|
||
|
chars[char] = 1;
|
||
|
} else {
|
||
|
chars[char] += 1;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// check if any of the accumulated chars exceed the allowed occurancy
|
||
|
// of 50% of the words' length.
|
||
|
_.forIn(chars, function (charCount) {
|
||
|
if (charCount >= allowedOccurancy) {
|
||
|
valid = false;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
return valid;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Validation against simple password rules
|
||
|
* Returns false when validation fails and true for a valid password
|
||
|
* @param {String} password The password string to check.
|
||
|
* @param {String} email The users email address to validate agains password.
|
||
|
* @param {String} blogTitle Optional blogTitle value, when blog title is not set yet, e. g. in setup process.
|
||
|
* @return {Object} example for returned validation Object:
|
||
|
* invalid password: `validationResult: {isValid: false, message: 'Sorry, you cannot use an insecure password.'}`
|
||
|
* valid password: `validationResult: {isValid: true}`
|
||
|
*/
|
||
|
function validatePassword(password, email, blogTitle) {
|
||
|
const validationResult = {isValid: true};
|
||
|
const disallowedPasswords = ['password', 'ghost', 'passw0rd'];
|
||
|
let blogUrl = urlUtils.urlFor('home', true);
|
||
|
|
||
|
const badPasswords = [
|
||
|
'1234567890',
|
||
|
'qwertyuiop',
|
||
|
'qwertzuiop',
|
||
|
'asdfghjkl;',
|
||
|
'abcdefghij',
|
||
|
'0987654321',
|
||
|
'1q2w3e4r5t',
|
||
|
'12345asdfg'
|
||
|
];
|
||
|
|
||
|
blogTitle = blogTitle ? blogTitle : settingsCache.get('title');
|
||
|
blogUrl = blogUrl.replace(/^http(s?):\/\//, '');
|
||
|
|
||
|
// password must be longer than 10 characters
|
||
|
if (!validator.isLength(password, 10)) {
|
||
|
validationResult.isValid = false;
|
||
|
validationResult.message = i18n.t('errors.models.user.passwordDoesNotComplyLength', {minLength: 10});
|
||
|
|
||
|
return validationResult;
|
||
|
}
|
||
|
|
||
|
// dissallow password from badPasswords list (e. g. '1234567890')
|
||
|
_.each(badPasswords, function (badPassword) {
|
||
|
if (badPassword === password) {
|
||
|
validationResult.isValid = false;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// password must not match with users' email
|
||
|
if (email && email.toLowerCase() === password.toLowerCase()) {
|
||
|
validationResult.isValid = false;
|
||
|
}
|
||
|
|
||
|
// password must not contain the words 'ghost', 'password', or 'passw0rd'
|
||
|
_.each(disallowedPasswords, function (disallowedPassword) {
|
||
|
if (password.toLowerCase().indexOf(disallowedPassword) >= 0) {
|
||
|
validationResult.isValid = false;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// password must not match with blog title
|
||
|
if (blogTitle && blogTitle.toLowerCase() === password.toLowerCase()) {
|
||
|
validationResult.isValid = false;
|
||
|
}
|
||
|
|
||
|
// password must not match with blog URL (without protocol, with or without trailing slash)
|
||
|
if (blogUrl && (blogUrl.toLowerCase() === password.toLowerCase() || blogUrl.toLowerCase().replace(/\/$/, '') === password.toLowerCase())) {
|
||
|
validationResult.isValid = false;
|
||
|
}
|
||
|
|
||
|
// dissallow passwords where 50% or more of characters are the same
|
||
|
if (!characterOccurance(password)) {
|
||
|
validationResult.isValid = false;
|
||
|
}
|
||
|
|
||
|
// Generic error message for the rules where no dedicated error massage is set
|
||
|
if (!validationResult.isValid && !validationResult.message) {
|
||
|
validationResult.message = i18n.t('errors.models.user.passwordDoesNotComplySecurity');
|
||
|
}
|
||
|
|
||
|
return validationResult;
|
||
|
}
|
||
|
|
||
|
module.exports = validatePassword;
|