Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new password complexity validations #224

Merged
merged 1 commit into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion components/form/Validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -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+/;
Expand Down Expand Up @@ -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 {
Expand Down
13 changes: 13 additions & 0 deletions components/users/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,19 @@ const UserEditForm = (props) => {
</h2>

<Field
validations={[
'haveLowercaseLetter',
'haveUppercaseLetter',
'haveDigit',
{
type: 'minLength',
condition: 10
},
{
type: 'maxLength',
condition: 128
}
]}
className="-fluid"
properties={{
name: 'password',
Expand Down
15 changes: 14 additions & 1 deletion components/users/new.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,20 @@ const UserNewForm = (props) => {
</Field>

<Field
validations={['required']}
validations={[
'required',
'haveLowercaseLetter',
'haveUppercaseLetter',
'haveDigit',
{
type: 'minLength',
condition: 10
},
{
type: 'maxLength',
condition: 128
}
]}
className="-fluid"
properties={{
name: 'password',
Expand Down
19 changes: 17 additions & 2 deletions components/users/reset-password.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,35 @@ const ResetPasswordForm = ({ token }) => {
<Form>
<fieldset className="c-field-container">
<Field
validations={[
'required',
'haveLowercaseLetter',
'haveUppercaseLetter',
'haveDigit',
{
type: 'minLength',
condition: 10
},
{
type: 'maxLength',
condition: 128
}
]}
className="-fluid"
properties={{
name: 'password',
autoComplete: 'new-password',
label: intl.formatMessage({ id: 'New Password' }),
type: 'password',
required: false
required: true
}}
>
{Input}
</Field>

<Field
validations={[
'required',
{
type: 'isEqual',
condition: form.password,
Expand All @@ -69,7 +84,7 @@ const ResetPasswordForm = ({ token }) => {
autoComplete: 'new-password',
label: intl.formatMessage({ id: 'Confirm New Password' }),
type: 'password',
required: false
required: true
}}
>
{Input}
Expand Down
2 changes: 1 addition & 1 deletion e2e/cypress/e2e/operator.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ describe('Operator', function () {

context('when logged in as Operator', function () {
beforeEach(function () {
cy.login('[email protected]', 'password');
cy.login('[email protected]', 'Supersecret1');
});

describe('updating operator profile', function () {
Expand Down
4 changes: 2 additions & 2 deletions e2e/cypress/e2e/password-reset.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
Expand Down
24 changes: 15 additions & 9 deletions e2e/cypress/e2e/user.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ 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');
});
})

context('Public user', () => {
it('can log in and out', function () {
cy.login('[email protected]', 'password');
cy.login('[email protected]', 'Supersecret1');
cy.get('a').contains('My account').click();
cy.get('a').contains('My profile').click();
cy.get('#input-firstName').should('have.value', 'Operator');
Expand All @@ -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('[email protected]');
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 [email protected] once your account is approved');
Expand Down Expand Up @@ -78,7 +84,7 @@ describe('User', () => {

context('Logged in User', () => {
beforeEach(() => {
cy.login('[email protected]', 'password');
cy.login('[email protected]', 'Supersecret1');
});

it('can update user profile', function () {
Expand All @@ -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');
Expand Down
7 changes: 6 additions & 1 deletion lang/zu.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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"
}
}
Loading