786e0ac9c5
no issue - if a plain native class instance with tracked properties is validated against the `ValidationEngine` and it's associated validators would cause errors by assuming that the instance has a `.get()` method - updated all model access in `ValidationEngine` and the validators to use direct property access which works for both native class and `EmberObject` instances
115 lines
4.1 KiB
JavaScript
115 lines
4.1 KiB
JavaScript
import Mixin from '@ember/object/mixin';
|
|
import validator from 'validator';
|
|
|
|
const BAD_PASSWORDS = [
|
|
'1234567890',
|
|
'qwertyuiop',
|
|
'qwertzuiop',
|
|
'asdfghjkl;',
|
|
'abcdefghij',
|
|
'0987654321',
|
|
'1q2w3e4r5t',
|
|
'12345asdfg'
|
|
];
|
|
const DISALLOWED_PASSWORDS = ['ghost', 'password', 'passw0rd'];
|
|
|
|
export default Mixin.create({
|
|
/**
|
|
* Counts repeated characters if 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}
|
|
*/
|
|
_characterOccurance(stringToTest) {
|
|
let chars = {};
|
|
let allowedOccurancy;
|
|
let valid = true;
|
|
|
|
allowedOccurancy = stringToTest.length / 2;
|
|
|
|
// Loop through string and accumulate character counts
|
|
for (let i = 0; i < stringToTest.length; i += 1) {
|
|
if (!chars[stringToTest[i]]) {
|
|
chars[stringToTest[i]] = 1;
|
|
} else {
|
|
chars[stringToTest[i]] += 1;
|
|
}
|
|
}
|
|
|
|
// check if any of the accumulated chars exceed the allowed occurancy
|
|
// of 50% of the words' length.
|
|
for (let charCount in chars) {
|
|
if (chars[charCount] >= allowedOccurancy) {
|
|
valid = false;
|
|
return valid;
|
|
}
|
|
}
|
|
|
|
return valid;
|
|
},
|
|
|
|
passwordValidation(model, password, errorTarget) {
|
|
let blogUrl = model.config?.blogUrl || window.location.host;
|
|
let blogTitle = model.blogTitle || model.config?.blogTitle;
|
|
let blogUrlWithSlash;
|
|
|
|
// the password that needs to be validated can differ from the password in the
|
|
// passed model, e. g. for password changes or reset.
|
|
password = password || model.password;
|
|
errorTarget = errorTarget || 'password';
|
|
|
|
blogUrl = blogUrl.replace(/^http(s?):\/\//, '');
|
|
blogUrlWithSlash = blogUrl.match(/\/$/) ? blogUrl : `${blogUrl}/`;
|
|
|
|
blogTitle = blogTitle ? blogTitle.trim().toLowerCase() : blogTitle;
|
|
|
|
// password must be longer than 10 characters
|
|
if (!validator.isLength(password || '', 10)) {
|
|
model.errors.add(errorTarget, 'Password must be at least 10 characters long.');
|
|
return this.invalidate();
|
|
}
|
|
|
|
password = password.toString();
|
|
|
|
// dissallow password from badPasswords list (e. g. '1234567890')
|
|
BAD_PASSWORDS.forEach((badPassword) => {
|
|
if (badPassword === password) {
|
|
model.errors.add(errorTarget, 'Sorry, you cannot use an insecure password.');
|
|
this.invalidate();
|
|
}
|
|
});
|
|
|
|
// password must not match with users' email
|
|
if (password.toLowerCase() === model.email.toLowerCase()) {
|
|
model.errors.add(errorTarget, 'Sorry, you cannot use an insecure password.');
|
|
this.invalidate();
|
|
}
|
|
|
|
// password must not contain the words 'ghost', 'password', or 'passw0rd'
|
|
DISALLOWED_PASSWORDS.forEach((disallowedPassword) => {
|
|
if (password.toLowerCase().indexOf(disallowedPassword) >= 0) {
|
|
model.errors.add(errorTarget, 'Sorry, you cannot use an insecure password.');
|
|
this.invalidate();
|
|
}
|
|
});
|
|
|
|
// password must not match with blog title
|
|
if (password.toLowerCase() === blogTitle) {
|
|
model.errors.add(errorTarget, 'Sorry, you cannot use an insecure password.');
|
|
this.invalidate();
|
|
}
|
|
|
|
// password must not match with blog URL (without protocol, with or without trailing slash)
|
|
if (password.toLowerCase() === blogUrl || password.toLowerCase() === blogUrlWithSlash) {
|
|
model.errors.add(errorTarget, 'Sorry, you cannot use an insecure password.');
|
|
this.invalidate();
|
|
}
|
|
|
|
// dissallow passwords where 50% or more of characters are the same
|
|
if (!this._characterOccurance(password)) {
|
|
model.errors.add(errorTarget, 'Sorry, you cannot use an insecure password.');
|
|
this.invalidate();
|
|
}
|
|
}
|
|
});
|