0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-11 02:12:21 -05:00

Updated ValidationEngine to support bare native class models (#15567)

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
This commit is contained in:
Kevin Ansfield 2022-10-07 20:13:42 +01:00 committed by GitHub
parent 0a827dfb47
commit 786e0ac9c5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 115 additions and 115 deletions

View file

@ -107,8 +107,8 @@ export default Mixin.create({
model = this.model;
}
type = this.validationType || model.get('validationType');
validator = this.get(`validators.${type}`) || model.get(`validators.${type}`);
type = this.validationType || model.validationType;
validator = this.get(`validators.${type}`) || model.validators[type];
hasValidated = this.hasValidated;
opts.validationType = type;
@ -123,9 +123,9 @@ export default Mixin.create({
if (opts.property) {
// If property isn't in `hasValidated`, add it to mark that this field can show a validation result
hasValidated.addObject(opts.property);
model.get('errors').remove(opts.property);
model.errors.remove(opts.property);
} else {
model.get('errors').clear();
model.errors.clear();
}
passed = validator.check(model, opts.property);

View file

@ -6,23 +6,23 @@ export default BaseValidator.create({
properties: ['email', 'role'],
email(model) {
let email = model.get('email');
let email = model.email;
if (isBlank(email)) {
model.get('errors').add('email', 'Please enter an email.');
model.errors.add('email', 'Please enter an email.');
this.invalidate();
} else if (!validator.isEmail(email)) {
model.get('errors').add('email', 'Invalid Email.');
model.errors.add('email', 'Invalid Email.');
this.invalidate();
}
},
role(model) {
let role = model.get('role');
let role = model.role;
if (isBlank(role)) {
model.get('errors').add('role', 'Please select a role.');
model.get('hasValidated').pushObject('role');
model.errors.add('role', 'Please select a role.');
model.hasValidated.pushObject('role');
this.invalidate();
}
}

View file

@ -13,7 +13,7 @@ export default BaseValidator.create({
},
email(model) {
let email = model.get('email');
let email = model.email;
if (isBlank(email)) {
model.errors.add('email', 'Please enter an email.');
@ -27,17 +27,17 @@ export default BaseValidator.create({
this.invalidate();
}
model.get('hasValidated').addObject('email');
model.hasValidated.addObject('email');
},
note(model) {
let note = model.get('note');
let note = model.note;
if (!validator.isLength(note || '', 0, 500)) {
model.errors.add('note', 'Note is too long.');
this.invalidate();
}
model.get('hasValidated').addObject('note');
model.hasValidated.addObject('note');
}
});

View file

@ -49,13 +49,13 @@ export default Mixin.create({
},
passwordValidation(model, password, errorTarget) {
let blogUrl = model.get('config.blogUrl') || window.location.host;
let blogTitle = model.get('blogTitle') || model.get('config.blogTitle');
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.get('password');
password = password || model.password;
errorTarget = errorTarget || 'password';
blogUrl = blogUrl.replace(/^http(s?):\/\//, '');
@ -65,7 +65,7 @@ export default Mixin.create({
// password must be longer than 10 characters
if (!validator.isLength(password || '', 10)) {
model.get('errors').add(errorTarget, 'Password must be at least 10 characters long.');
model.errors.add(errorTarget, 'Password must be at least 10 characters long.');
return this.invalidate();
}
@ -74,40 +74,40 @@ export default Mixin.create({
// dissallow password from badPasswords list (e. g. '1234567890')
BAD_PASSWORDS.forEach((badPassword) => {
if (badPassword === password) {
model.get('errors').add(errorTarget, 'Sorry, you cannot use an insecure 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.get('email').toLowerCase()) {
model.get('errors').add(errorTarget, 'Sorry, you cannot use an insecure password.');
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.get('errors').add(errorTarget, 'Sorry, you cannot use an insecure password.');
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.get('errors').add(errorTarget, 'Sorry, you cannot use an insecure password.');
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.get('errors').add(errorTarget, 'Sorry, you cannot use an insecure password.');
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.get('errors').add(errorTarget, 'Sorry, you cannot use an insecure password.');
model.errors.add(errorTarget, 'Sorry, you cannot use an insecure password.');
this.invalidate();
}
}

View file

@ -6,11 +6,11 @@ export default BaseValidator.create({
properties: ['label', 'url'],
label(model) {
let label = model.get('label');
let hasValidated = model.get('hasValidated');
let label = model.label;
let hasValidated = model.hasValidated;
if (isBlank(label)) {
model.get('errors').add('label', 'You must specify a label');
model.errors.add('label', 'You must specify a label');
this.invalidate();
}
@ -18,18 +18,18 @@ export default BaseValidator.create({
},
url(model) {
let url = model.get('url');
let hasValidated = model.get('hasValidated');
let url = model.url;
let hasValidated = model.hasValidated;
/* eslint-disable camelcase */
let validatorOptions = {require_protocol: true};
/* eslint-enable camelcase */
let urlRegex = new RegExp(/^(\/|#|[a-zA-Z0-9-]+:)/);
if (isBlank(url)) {
model.get('errors').add('url', 'You must specify a URL or relative path');
model.errors.add('url', 'You must specify a URL or relative path');
this.invalidate();
} else if (url.match(/\s/) || (!validator.isURL(url, validatorOptions) && !url.match(urlRegex))) {
model.get('errors').add('url', 'You must specify a valid URL or relative path');
model.errors.add('url', 'You must specify a valid URL or relative path');
this.invalidate();
}

View file

@ -10,27 +10,27 @@ export default BaseValidator.extend(PasswordValidatorMixin, {
},
name(model) {
let name = model.get('name');
let name = model.name;
if (!validator.isLength(name || '', 1)) {
model.get('errors').add('name', 'Please enter a name.');
model.get('hasValidated').addObject('email');
model.errors.add('name', 'Please enter a name.');
model.hasValidated.addObject('email');
this.invalidate();
}
},
email(model) {
let email = model.get('email');
let email = model.email;
if (isBlank(email)) {
model.get('errors').add('email', 'Please enter an email.');
model.errors.add('email', 'Please enter an email.');
this.invalidate();
} else if (!validator.isEmail(email)) {
model.get('errors').add('email', 'Invalid Email.');
model.errors.add('email', 'Invalid Email.');
this.invalidate();
}
model.get('hasValidated').addObject('email');
model.hasValidated.addObject('email');
},
password(model) {

View file

@ -10,14 +10,14 @@ const resetValidator = BaseValidator.extend(PasswordValidatorMixin, {
},
newPassword(model) {
let p1 = model.get('newPassword');
let p2 = model.get('ne2Password');
let p1 = model.newPassword;
let p2 = model.ne2Password;
if (isBlank(p1)) {
model.get('errors').add('newPassword', 'Please enter a password.');
model.errors.add('newPassword', 'Please enter a password.');
this.invalidate();
} else if (!validator.equals(p1, p2 || '')) {
model.get('errors').add('ne2Password', 'The two new passwords don\'t match.');
model.errors.add('ne2Password', 'The two new passwords don\'t match.');
this.invalidate();
}

View file

@ -5,38 +5,38 @@ import {isBlank} from '@ember/utils';
export default BaseValidator.create({
properties: ['title', 'description', 'password', 'slackUrl'],
title(model) {
let title = model.get('title');
let title = model.title;
if (!validator.isLength(title || '', 0, 150)) {
model.get('errors').add('title', 'Title is too long');
model.errors.add('title', 'Title is too long');
this.invalidate();
}
},
description(model) {
let desc = model.get('description');
let desc = model.description;
if (!validator.isLength(desc || '', 0, 200)) {
model.get('errors').add('description', 'Description is too long');
model.errors.add('description', 'Description is too long');
this.invalidate();
}
},
password(model) {
let isPrivate = model.get('isPrivate');
let password = model.get('password');
let isPrivate = model.isPrivate;
let password = model.password;
if (isPrivate && password === '') {
model.get('errors').add('password', 'Password must be supplied');
model.errors.add('password', 'Password must be supplied');
this.invalidate();
}
},
slackUrl(model) {
let slackUrl = model.get('slackUrl');
let slackUrl = model.slackUrl;
if (!isBlank(slackUrl) && !validator.isURL(slackUrl, {require_protocol: true})) {
model.get('errors').add(
model.errors.add(
'slackUrl',
'The URL must be in a format like https://hooks.slack.com/services/<your personal key>'
);

View file

@ -5,15 +5,15 @@ export default NewUserValidator.create({
properties: ['name', 'email', 'password', 'blogTitle'],
blogTitle(model) {
let blogTitle = model.get('blogTitle');
let blogTitle = model.blogTitle;
if (!validator.isLength(blogTitle || '', 1)) {
model.get('errors').add('blogTitle', 'Please enter a site title.');
model.errors.add('blogTitle', 'Please enter a site title.');
this.invalidate();
}
if (!validator.isLength(blogTitle || '', 0, 150)) {
model.get('errors').add('blogTitle', 'Title is too long');
model.errors.add('blogTitle', 'Title is too long');
this.invalidate();
}
}

View file

@ -7,43 +7,43 @@ export default BaseValidator.create({
invalidMessage: 'Email address is not valid',
identification(model) {
let id = model.get('identification');
let id = model.identification;
if (!isBlank(id) && !validator.isEmail(id)) {
model.get('errors').add('identification', this.invalidMessage);
model.errors.add('identification', this.invalidMessage);
this.invalidate();
}
},
signin(model) {
let id = model.get('identification');
let password = model.get('password');
let id = model.identification;
let password = model.password;
model.get('errors').clear();
model.errors.clear();
if (isBlank(id)) {
model.get('errors').add('identification', 'Please enter an email');
model.errors.add('identification', 'Please enter an email');
this.invalidate();
}
if (!isBlank(id) && !validator.isEmail(id)) {
model.get('errors').add('identification', this.invalidMessage);
model.errors.add('identification', this.invalidMessage);
this.invalidate();
}
if (isBlank(password)) {
model.get('errors').add('password', 'Please enter a password');
model.errors.add('password', 'Please enter a password');
this.invalidate();
}
},
forgotPassword(model) {
let id = model.get('identification');
let id = model.identification;
model.get('errors').clear();
model.errors.clear();
if (isBlank(id) || !validator.isEmail(id)) {
model.get('errors').add('identification', this.invalidMessage);
model.errors.add('identification', this.invalidMessage);
this.invalidate();
}
}

View file

@ -18,14 +18,14 @@ export default BaseValidator.create({
this.invalidate();
}
model.get('hasValidated').addObject('name');
model.hasValidated.addObject('name');
},
mobiledoc(model) {
if (isBlank(model.get('mobiledoc'))) {
if (isBlank(model.mobiledoc)) {
model.errors.add('mobiledoc', 'Content cannot be blank.');
this.invalidate();
}
model.get('hasValidated').addObject('mobiledoc');
model.hasValidated.addObject('mobiledoc');
}
});

View file

@ -6,19 +6,19 @@ export default BaseValidator.create({
properties: ['email'],
email(model) {
let email = model.get('email');
let email = model.email;
if (isBlank(email)) {
model.get('errors').add('email', 'Please enter an email.');
model.get('hasValidated').pushObject('email');
model.errors.add('email', 'Please enter an email.');
model.hasValidated.pushObject('email');
this.invalidate();
} else if (!validator.isEmail(email)) {
model.get('errors').add('email', 'Invalid email.');
model.get('hasValidated').pushObject('email');
model.errors.add('email', 'Invalid email.');
model.hasValidated.pushObject('email');
this.invalidate();
} else if (!validator.isLength(email, 0, 191)) {
model.get('errors').add('email', 'Email is too long');
model.get('hasValidated').pushObject('email');
model.errors.add('email', 'Email is too long');
model.hasValidated.pushObject('email');
this.invalidate();
}
}

View file

@ -6,52 +6,52 @@ export default BaseValidator.create({
properties: ['name', 'slug', 'description', 'metaTitle', 'metaDescription'],
name(model) {
let name = model.get('name');
let name = model.name;
if (isBlank(name)) {
model.get('errors').add('name', 'You must specify a name for the tag.');
model.errors.add('name', 'You must specify a name for the tag.');
this.invalidate();
} else if (name.match(/^,/)) {
model.get('errors').add('name', 'Tag names can\'t start with commas.');
model.errors.add('name', 'Tag names can\'t start with commas.');
this.invalidate();
} else if (!validator.isLength(name, 0, 191)) {
model.get('errors').add('name', 'Tag names cannot be longer than 191 characters.');
model.errors.add('name', 'Tag names cannot be longer than 191 characters.');
this.invalidate();
}
},
slug(model) {
let slug = model.get('slug');
let slug = model.slug;
if (!validator.isLength(slug || '', 0, 191)) {
model.get('errors').add('slug', 'URL cannot be longer than 191 characters.');
model.errors.add('slug', 'URL cannot be longer than 191 characters.');
this.invalidate();
}
},
description(model) {
let description = model.get('description');
let description = model.description;
if (!validator.isLength(description || '', 0, 500)) {
model.get('errors').add('description', 'Description cannot be longer than 500 characters.');
model.errors.add('description', 'Description cannot be longer than 500 characters.');
this.invalidate();
}
},
metaTitle(model) {
let metaTitle = model.get('metaTitle');
let metaTitle = model.metaTitle;
if (!validator.isLength(metaTitle || '', 0, 300)) {
model.get('errors').add('metaTitle', 'Meta Title cannot be longer than 300 characters.');
model.errors.add('metaTitle', 'Meta Title cannot be longer than 300 characters.');
this.invalidate();
}
},
metaDescription(model) {
let metaDescription = model.get('metaDescription');
let metaDescription = model.metaDescription;
if (!validator.isLength(metaDescription || '', 0, 500)) {
model.get('errors').add('metaDescription', 'Meta Description cannot be longer than 500 characters.');
model.errors.add('metaDescription', 'Meta Description cannot be longer than 500 characters.');
this.invalidate();
}
}

View file

@ -5,11 +5,11 @@ export default BaseValidator.create({
properties: ['name'],
name(model) {
let name = model.get('name');
let hasValidated = model.get('hasValidated');
let name = model.name;
let hasValidated = model.hasValidated;
if (isBlank(name)) {
model.get('errors').add('name', 'Please enter a benefit');
model.errors.add('name', 'Please enter a benefit');
this.invalidate();
}

View file

@ -10,68 +10,68 @@ const userValidator = BaseValidator.extend(PasswordValidatorMixin, {
},
isActive(model) {
return (model.get('status') === 'active');
return (model.status === 'active');
},
name(model) {
let name = model.get('name');
let name = model.name;
if (this.isActive(model)) {
if (isBlank(name)) {
model.get('errors').add('name', 'Please enter a name.');
model.errors.add('name', 'Please enter a name.');
this.invalidate();
} else if (!validator.isLength(name, 0, 191)) {
model.get('errors').add('name', 'Name is too long');
model.errors.add('name', 'Name is too long');
this.invalidate();
}
}
},
bio(model) {
let bio = model.get('bio');
let bio = model.bio;
if (this.isActive(model)) {
if (!validator.isLength(bio || '', 0, 200)) {
model.get('errors').add('bio', 'Bio is too long');
model.errors.add('bio', 'Bio is too long');
this.invalidate();
}
}
},
email(model) {
let email = model.get('email');
let email = model.email;
if (!validator.isEmail(email || '')) {
model.get('errors').add('email', 'Please supply a valid email address');
model.errors.add('email', 'Please supply a valid email address');
this.invalidate();
}
if (!validator.isLength(email || '', 0, 191)) {
model.get('errors').add('email', 'Email is too long');
model.errors.add('email', 'Email is too long');
this.invalidate();
}
},
location(model) {
let location = model.get('location');
let location = model.location;
if (this.isActive(model)) {
if (!validator.isLength(location || '', 0, 150)) {
model.get('errors').add('location', 'Location is too long');
model.errors.add('location', 'Location is too long');
this.invalidate();
}
}
},
website(model) {
let website = model.get('website');
let website = model.website;
// eslint-disable-next-line camelcase
let isInvalidWebsite = !validator.isURL(website || '', {require_protocol: false})
|| !validator.isLength(website || '', 0, 2000);
if (this.isActive(model)) {
if (!isBlank(website) && isInvalidWebsite) {
model.get('errors').add('website', 'Website is not a valid url');
model.errors.add('website', 'Website is not a valid url');
this.invalidate();
}
}
@ -79,30 +79,30 @@ const userValidator = BaseValidator.extend(PasswordValidatorMixin, {
roles(model) {
if (!this.isActive(model)) {
let roles = model.get('roles');
let roles = model.roles;
if (roles.length < 1) {
model.get('errors').add('role', 'Please select a role');
model.errors.add('role', 'Please select a role');
this.invalidate();
}
}
},
passwordChange(model) {
let newPassword = model.get('newPassword');
let ne2Password = model.get('ne2Password');
let newPassword = model.newPassword;
let ne2Password = model.ne2Password;
// validation only marks the requested property as validated so we
// have to add properties manually
model.get('hasValidated').addObject('newPassword');
model.get('hasValidated').addObject('ne2Password');
model.hasValidated.addObject('newPassword');
model.hasValidated.addObject('ne2Password');
if (isBlank(newPassword) && isBlank(ne2Password)) {
model.get('errors').add('newPassword', 'Sorry, passwords can\'t be blank');
model.errors.add('newPassword', 'Sorry, passwords can\'t be blank');
this.invalidate();
} else {
if (!validator.equals(newPassword, ne2Password || '')) {
model.get('errors').add('ne2Password', 'Your new passwords do not match');
model.errors.add('ne2Password', 'Your new passwords do not match');
this.invalidate();
}
@ -111,16 +111,16 @@ const userValidator = BaseValidator.extend(PasswordValidatorMixin, {
},
ownPasswordChange(model) {
let oldPassword = model.get('password');
let oldPassword = model.password;
this.passwordChange(model);
// validation only marks the requested property as validated so we
// have to add properties manually
model.get('hasValidated').addObject('password');
model.hasValidated.addObject('password');
if (isBlank(oldPassword)) {
model.get('errors').add('password', 'Your current password is required to set a new one');
model.errors.add('password', 'Your current password is required to set a new one');
this.invalidate();
}
}