From 5b6e8f606f69910cf670ee1aabfd93cbf9521910 Mon Sep 17 00:00:00 2001 From: Tomasz Subik Date: Mon, 30 Sep 2024 18:02:55 +0200 Subject: [PATCH] add new password complexity validations --- components/form/Validator.js | 40 +++++++++++++++++++++++++++- components/users/edit.js | 13 +++++++++ components/users/new.js | 15 ++++++++++- components/users/reset-password.js | 19 +++++++++++-- e2e/cypress/e2e/operator.cy.js | 2 +- e2e/cypress/e2e/password-reset.cy.js | 4 +-- e2e/cypress/e2e/user.cy.js | 24 ++++++++++------- lang/zu.json | 7 ++++- 8 files changed, 107 insertions(+), 17 deletions(-) diff --git a/components/form/Validator.js b/components/form/Validator.js index 5bc4f424..b468e853 100644 --- a/components/form/Validator.js +++ b/components/form/Validator.js @@ -20,6 +20,44 @@ class Validator { message: intl.formatMessage({ id: 'The field is required' }) }, + minLength: { + validate(value, condition) { + return value.length >= condition; + }, + message: (condition) => intl.formatMessage({ id: 'validation.minLength', defaultMessage: 'The field should have at least {min} characters' }, { min: condition }) + }, + + maxLength: { + validate(value, condition) { + return value.length <= condition; + }, + message: (condition) => intl.formatMessage({ id: 'validation.maxLength', defaultMessage: 'The field should have at most {max} characters' }, { max: condition }) + }, + + haveLowercaseLetter: { + validate(value) { + const regex = /(?=.*[a-z]).+/; + return regex.test(value || ''); + }, + message: intl.formatMessage({ id: 'validation.haveLowercaseLetter', defaultMessage: 'The field should have at least one lowercase letter' }) + }, + + haveUppercaseLetter: { + validate(value) { + const regex = /(?=.*[A-Z]).+/; + return regex.test(value || ''); + }, + message: intl.formatMessage({ id: 'validation.haveUppercaseLetter', defaultMessage: 'The field should have at least one capital (uppercase) letter' }) + }, + + haveDigit: { + validate(value) { + const regex = /(?=.*\d).+/; + return regex.test(value || ''); + }, + message: intl.formatMessage({ id: 'validation.haveDigit', defaultMessage: 'The field should have at least one digit' }) + }, + email: { validate(value) { const regex = /\S+@\S+\.\S+/; @@ -59,7 +97,7 @@ class Validator { if (typeof validation === 'object') { const validObj = this.validations[validation.type]; valid = validObj.validate(value, validation.condition); - message = validation.message || validObj.message || ''; + message = validation.message || ((validObj.message && typeof validObj.message === 'function') ? validObj.message(validation.condition) : validObj.message) || ''; } return { diff --git a/components/users/edit.js b/components/users/edit.js index cd6850af..b4fad559 100644 --- a/components/users/edit.js +++ b/components/users/edit.js @@ -126,6 +126,19 @@ const UserEditForm = (props) => { { {
{Input} @@ -57,6 +71,7 @@ const ResetPasswordForm = ({ token }) => { { autoComplete: 'new-password', label: intl.formatMessage({ id: 'Confirm New Password' }), type: 'password', - required: false + required: true }} > {Input} diff --git a/e2e/cypress/e2e/operator.cy.js b/e2e/cypress/e2e/operator.cy.js index dc4a897d..000f77dc 100644 --- a/e2e/cypress/e2e/operator.cy.js +++ b/e2e/cypress/e2e/operator.cy.js @@ -9,7 +9,7 @@ describe('Operator', function () { context('when logged in as Operator', function () { beforeEach(function () { - cy.login('operator@example.com', 'password'); + cy.login('operator@example.com', 'Supersecret1'); }); describe('updating operator profile', function () { diff --git a/e2e/cypress/e2e/password-reset.cy.js b/e2e/cypress/e2e/password-reset.cy.js index abce6119..168e9265 100644 --- a/e2e/cypress/e2e/password-reset.cy.js +++ b/e2e/cypress/e2e/password-reset.cy.js @@ -17,8 +17,8 @@ describe('Password Reset', () => { describe('errors', () => { it('shows error with invalid token', function () { cy.visit('/reset-password?reset_password_token=invalid'); - cy.get('#input-password').type('newpassword'); - cy.get('#input-passwordConfirmation').type('newpassword'); + cy.get('#input-password').type('NewPassword1'); + cy.get('#input-passwordConfirmation').type('NewPassword1'); cy.get('button').contains('Change Password').click(); cy.get('.rrt-text').should('have.text', 'reset_password_token is invalid'); }); diff --git a/e2e/cypress/e2e/user.cy.js b/e2e/cypress/e2e/user.cy.js index 51c7642b..b4d64a90 100644 --- a/e2e/cypress/e2e/user.cy.js +++ b/e2e/cypress/e2e/user.cy.js @@ -15,7 +15,7 @@ describe('User', () => { cy.get('#input-password').type('wrongpassword'); cy.get('button').contains('Log in').click(); cy.get('.rrt-text').should('have.text', 'Wrong email or password'); - cy.get('#input-password').clear().type('password'); + cy.get('#input-password').clear().type('Supersecret1'); cy.get('button').contains('Log in').click(); cy.contains('a', 'My account'); }); @@ -23,7 +23,7 @@ describe('User', () => { context('Public user', () => { it('can log in and out', function () { - cy.login('operator@example.com', 'password'); + cy.login('operator@example.com', 'Supersecret1'); cy.get('a').contains('My account').click(); cy.get('a').contains('My profile').click(); cy.get('#input-firstName').should('have.value', 'Operator'); @@ -44,11 +44,17 @@ describe('User', () => { cy.get('#input-first_name').type('Test'); cy.get('#input-last_name').type('Operator'); cy.get('#input-email').type('testoperator@example.com'); - cy.get('#input-password').type('supersecret'); - cy.get('#input-password_confirmation').type('supersecret'); + cy.get('#input-password').type('password'); + cy.get('#input-password_confirmation').type('password2'); cy.get('button').contains('Sign up').click(); - cy.get('.error').should('have.text', 'The field is required'); + cy.contains('.error', 'The field is required'); + cy.contains('.error', 'The field should have at least one capital (uppercase) letter'); + cy.contains('.error', 'The field should have at least one digit'); + cy.contains('.error', 'The field should have at least 10 characters'); + cy.contains('.error', 'The field should be equal to password'); cy.get('.rrt-text').should('have.text', 'Fill all the required fields'); + cy.get('#input-password').clear().type('Superpassword1'); + cy.get('#input-password_confirmation').clear().type('Superpassword1'); cy.get('label[for=checkbox-agree-undefined]').click(); cy.get('button').contains('Sign up').click(); cy.get('.c-info-box').contains('you will receive an email to testoperator@example.com once your account is approved'); @@ -78,7 +84,7 @@ describe('User', () => { context('Logged in User', () => { beforeEach(() => { - cy.login('operator@example.com', 'password'); + cy.login('operator@example.com', 'Supersecret1'); }); it('can update user profile', function () { @@ -90,9 +96,9 @@ describe('User', () => { cy.get('#input-lastName').clear(); cy.get('#input-lastName').type('Operator 2'); cy.selectOption('[name=locale]', 'Po', 'Português'); - cy.get('#input-password').type('supersecret'); - cy.get('#input-passwordConfirmation').type('supersecret'); - cy.get('#input-currentPassword').type('password'); + cy.get('#input-password').type('GreatPassword1'); + cy.get('#input-passwordConfirmation').type('GreatPassword1'); + cy.get('#input-currentPassword').type('Supersecret1'); cy.get('button').contains('Update').click(); cy.get('.rrt-text').should('have.text', 'Profile saved correctly'); diff --git a/lang/zu.json b/lang/zu.json index cd6dabfe..1dba1f31 100644 --- a/lang/zu.json +++ b/lang/zu.json @@ -422,6 +422,11 @@ "The field should be a valid url: http://example.com": "The field should be a valid url: http://example.com", "The field should be a valid email address": "The field should be a valid email address", "The field should be equal to password": "The field should be equal to password", + "validation.minLength": "The field should have at least {min} characters", + "validation.maxLength": "The field should have at most {max} characters", + "validation.haveLowercaseLetter": "The field should have at least one lowercase letter", + "validation.haveUppercaseLetter": "The field should have at least one capital (uppercase) letter", + "validation.haveDigit": "The field should have at least one digit", "First Name": "First Name", "Last Name": "Last Name", "Organization": "Organization", @@ -471,4 +476,4 @@ "newsletter.want_to_receive": "Want to receive the latest updates from the Open Timber Portal?", "newsletter.subscribe_to": "Subscribe to our newsletter", "newsletter.showing_results": "Showing {count} previous newsletters" -} \ No newline at end of file +}