Skip to content
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
55 changes: 22 additions & 33 deletions app/javascript/packs/pw-strength.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,10 @@ const snakeCase = (string) => string.replace(/[ -]/g, '_').replace(/\W/g, '').to
// fallback if zxcvbn lookup fails / field is empty
const fallback = ['pw-na', '...'];

function clearErrors() {
const x = document.getElementsByClassName('error-message');
if (x.length > 0) {
x[0].innerHTML = '';
}
}

function getStrength(z) {
// override the strength value to 2 if the password is < 12
if (!(z && z.password.length && z.password.length >= 12)) {
if (z.score >= 3) {
z.score = 2;
}
if (z.password.length < 12 && z.score >= 3) {
z.score = 2;
}
return z && z.password.length ? scale[z.score] : fallback;
}
Expand Down Expand Up @@ -85,18 +76,6 @@ export function getFeedback(z) {
return `${suggestions.map((s) => lookup(s)).join('. ')}`;
}

function disableSubmit(submitEl, length = 0, score = 0) {
if (!submitEl) {
return;
}

if (score < 3 || length < 12) {
submitEl.setAttribute('disabled', true);
} else {
submitEl.removeAttribute('disabled');
}
}

/**
* @param {HTMLElement?} element
*
Expand All @@ -115,30 +94,40 @@ function analyzePw() {
const pwCntnr = document.getElementById('pw-strength-cntnr');
const pwStrength = document.getElementById('pw-strength-txt');
const pwFeedback = document.getElementById('pw-strength-feedback');
const submit = document.querySelector('[type="submit"]');
const forbiddenPasswordsElement = document.querySelector('[data-forbidden]');
const forbiddenPasswords = getForbiddenPasswords(forbiddenPasswordsElement);

disableSubmit(submit);

// the pw strength module is hidden by default ("display-none" CSS class)
// (so that javascript disabled browsers won't see it)
// thus, first step is unhiding it
pwCntnr.className = '';

function checkPasswordStrength(e) {
const z = zxcvbn(e.target.value, forbiddenPasswords);
const [cls, strength] = getStrength(z);
const feedback = getFeedback(z);
function updatePasswordFeedback(cls, strength, feedback) {
pwCntnr.className = cls;
pwStrength.innerHTML = strength;
pwFeedback.innerHTML = feedback;
}

function validatePasswordField(score) {
if (score < 3) {
input.setCustomValidity(t('errors.messages.stronger_password'));
} else {
input.setCustomValidity('');
}
}

function checkPasswordStrength(password) {
const z = zxcvbn(password, forbiddenPasswords);
const [cls, strength] = getStrength(z);
const feedback = getFeedback(z);

clearErrors();
disableSubmit(submit, z.password.length, z.score);
validatePasswordField(z.score);
updatePasswordFeedback(cls, strength, feedback);
}

input.addEventListener('input', checkPasswordStrength);
input.addEventListener('input', (e) => {
checkPasswordStrength(e.target.value);
});
}

document.addEventListener('DOMContentLoaded', analyzePw);
1 change: 1 addition & 0 deletions config/locales/errors/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ en:
select a different number and try again.
pwned_password: The password you entered is not safe. It’s in a list of known
passwords exposed in data breaches.
stronger_password: Enter a stronger password
too_long:
one: is too long (maximum is 1 character)
other: is too long (maximum is %{count} characters)
Expand Down
1 change: 1 addition & 0 deletions config/locales/errors/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ es:
premium. Por favor, seleccione otro número e inténtelo de nuevo.
pwned_password: La contraseña que ingresaste no es segura. Está en una lista de
contraseñas conocidas expuestas en violaciones de datos.
stronger_password: Introduzca una contraseña más segura
too_long:
one: es demasiado largo (el máximo es 1 carácter)
other: es demasiado largo (el máximo es %{count} caracteres)
Expand Down
1 change: 1 addition & 0 deletions config/locales/errors/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ fr:
pwned_password: Le mot de passe que vous avez entré n’est pas sécurisé. C’est
dans une liste de mots de passe connus exposés dans les violations de
données.
stronger_password: Entrez un mot de passe plus fort
too_long:
one: Est trop long (maximum de 1 caractère)
other: est trop long (le maximum est de %{count} caractères)
Expand Down
8 changes: 2 additions & 6 deletions spec/features/visitors/password_recovery_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,9 @@
context 'when password form values are invalid' do
it 'does not allow the user to submit until password score is good', js: true do
fill_in t('forms.passwords.edit.labels.password'), with: 'invalid'
expect(page).not_to have_button(t('forms.passwords.edit.buttons.submit'))

fill_in t('forms.passwords.edit.labels.password'), with: 'password@132!'
expect(page).not_to have_button(t('forms.passwords.edit.buttons.submit'))
click_button t('forms.passwords.edit.buttons.submit')

fill_in t('forms.passwords.edit.labels.password'), with: 'a unique and exciting zxjsahfas'
expect(page).to have_button(t('forms.passwords.edit.buttons.submit'))
expect(page).to have_css('.usa-error-message', text: t('errors.messages.stronger_password'))
end

it 'displays field validation error when password fields are empty' do
Expand Down
22 changes: 14 additions & 8 deletions spec/features/visitors/set_password_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
confirm_last_user
end

it 'does not allow the user to submit the form' do
it 'visitor gets field is required message' do
fill_in t('forms.password'), with: ''

expect(page).to_not have_button(t('forms.buttons.continue'))
click_button t('forms.buttons.continue')
expect(page).to have_content t('simple_form.required.text')
end
end

Expand Down Expand Up @@ -64,9 +64,12 @@
end

context 'password is invalid' do
scenario 'visitor is redirected back to password form' do
before do
create(:user, :unconfirmed)
confirm_last_user
end

scenario 'visitor is redirected back to password form' do
fill_in t('forms.password'), with: 'Q!2e'

click_button t('forms.buttons.continue')
Expand All @@ -76,8 +79,6 @@
end

scenario 'visitor gets password help message' do
create(:user, :unconfirmed)
confirm_last_user
fill_in t('forms.password'), with: '1234567891011'

click_button t('forms.buttons.continue')
Expand All @@ -86,13 +87,18 @@
end

scenario 'visitor gets password pwned message' do
create(:user, :unconfirmed)
confirm_last_user
fill_in t('forms.password'), with: '3.1415926535'

click_button t('forms.buttons.continue')

expect(page).to have_content t('errors.messages.pwned_password')
end

scenario 'visitor gets enter a stronger password message', js: true do
fill_in t('forms.password'), with: 'badpwd'
click_button t('forms.buttons.continue')

expect(page).to have_css('.usa-error-message', text: t('errors.messages.stronger_password'))
end
end
end