diff --git a/app/assets/stylesheets/password_complexity.scss b/app/assets/stylesheets/password_complexity.scss index 24fc2ef77..ee521b0b3 100644 --- a/app/assets/stylesheets/password_complexity.scss +++ b/app/assets/stylesheets/password_complexity.scss @@ -9,12 +9,10 @@ $complexity-color-3: #FFD000; $complexity-color-4: $green; .password-complexity { - margin-top: -24px; width: 100%; height: 12px; background: $complexity-bg; display: block; - margin-bottom: $default-spacer; text-align: center; border-radius: 8px; diff --git a/app/components/password_complexity_component.rb b/app/components/password_complexity_component.rb new file mode 100644 index 000000000..32efd3908 --- /dev/null +++ b/app/components/password_complexity_component.rb @@ -0,0 +1,53 @@ +class PasswordComplexityComponent < ApplicationComponent + def initialize(length: nil, min_length: nil, score: nil, min_complexity: nil) + @length = length + @min_length = min_length + @score = score + @min_complexity = min_complexity + end + + private + + def filled? + !@length.nil? || !@score.nil? + end + + def alert_classes + class_names( + "fr-alert": true, + "fr-alert--sm": true, + "fr-alert--info": !success?, + "fr-alert--success": success? + ) + end + + def success? + return false if !filled? + + @length >= @min_length && @score >= @min_complexity + end + + def complexity_classes + [ + "password-complexity fr-mt-2w fr-mb-1w", + filled? ? "complexity-#{@length < @min_length ? @score / 2 : @score}" : nil + ] + end + + def title + return t(".title.empty") if !filled? + + return t(".title.too_short", min_length: @min_length) if @length < @min_length + + case @score + when 0..1 + return t(".title.weakest") + when 2...@min_complexity + return t(".title.weak") + when @min_complexity...4 + return t(".title.passable") + else + return t(".title.strong") + end + end +end diff --git a/app/components/password_complexity_component/password_complexity_component.en.yml b/app/components/password_complexity_component/password_complexity_component.en.yml new file mode 100644 index 000000000..d5930cd58 --- /dev/null +++ b/app/components/password_complexity_component/password_complexity_component.en.yml @@ -0,0 +1,10 @@ +--- +en: + title: + empty: Enter a password. + too_short: Password must be at least %{min_length} characters long. + passable: Password is acceptable. You can validate… or improve your password. + strong: Congratulations! Password is strong and secure enough. + weak: Vulnerable password. + weakest: Very vulnerable password. + hint: A short sentence with punctuation can be a very secure password. diff --git a/app/components/password_complexity_component/password_complexity_component.fr.yml b/app/components/password_complexity_component/password_complexity_component.fr.yml new file mode 100644 index 000000000..225a5775c --- /dev/null +++ b/app/components/password_complexity_component/password_complexity_component.fr.yml @@ -0,0 +1,10 @@ +--- +fr: + title: + empty: Inscrivez un mot de passe. + too_short: Le mot de passe doit faire au moins %{min_length} caractères. + passable: Mot de passe acceptable. Vous pouvez valider… ou améliorer votre mot de passe. + strong: Félicitations ! Mot de passe suffisamment fort et sécurisé. + weak: Mot de passe vulnérable. + weakest: Mot de passe très vulnérable. + hint: Une courte phrase avec ponctuation peut être un mot de passe très sécurisé. diff --git a/app/components/password_complexity_component/password_complexity_component.html.haml b/app/components/password_complexity_component/password_complexity_component.html.haml new file mode 100644 index 000000000..bfa7d5620 --- /dev/null +++ b/app/components/password_complexity_component/password_complexity_component.html.haml @@ -0,0 +1,6 @@ +%div{ class: complexity_classes } + +%div{ class: alert_classes } + %h3.fr-alert__title= title + - if !success? + %p= t(".hint") diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml index e30dbf1d9..2ebfb3ee8 100644 --- a/app/views/devise/passwords/edit.html.haml +++ b/app/views/devise/passwords/edit.html.haml @@ -3,25 +3,28 @@ - content_for :footer do = render partial: 'root/footer' -.container.devise-container - .one-column-centered - = devise_error_messages! +.fr-container.fr-my-5w + .fr-grid-row.fr-grid-row--center + .fr-col-lg-6 + = devise_error_messages! - = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :patch, class: '' }) do |f| - = f.hidden_field :reset_password_token + = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :patch, class: '' }) do |f| + = f.hidden_field :reset_password_token - %fieldset.fr-mb-0.fr-fieldset{ aria: { labelledby: 'edit-password-legend' } } - %legend.fr-fieldset__legend#edit-password-legend - %h1.fr-h2= I18n.t('views.users.passwords.edit.subtitle') + %fieldset.fr-mb-0.fr-fieldset{ aria: { labelledby: 'edit-password-legend' } } + %legend.fr-fieldset__legend#edit-password-legend + %h1.fr-h2= I18n.t('views.users.passwords.edit.subtitle') - .fr-fieldset__element - = render Dsfr::InputComponent.new(form: f, attribute: :password, input_type: :password_field, - opts: { autofocus: 'true', autocomplete: 'new-password', data: { controller: populated_resource.validate_password_complexity? ? 'turbo-input' : false, turbo_input_url_value: show_password_complexity_path }}) + .fr-fieldset__element + = render Dsfr::InputComponent.new(form: f, attribute: :password, input_type: :password_field, + opts: { autofocus: 'true', autocomplete: 'new-password', data: { controller: populated_resource.validate_password_complexity? ? 'turbo-input' : false, turbo_input_url_value: show_password_complexity_path }}) - = render 'password_complexity/field', { form: f, test_complexity: populated_resource.validate_password_complexity? } + - if populated_resource.validate_password_complexity? + #password_complexity + = render PasswordComplexityComponent.new - .fr-fieldset__element - = render Dsfr::InputComponent.new(form: f, attribute: :password_confirmation, input_type: :password_field, opts: { autocomplete: 'new-password' }) + .fr-fieldset__element + = render Dsfr::InputComponent.new(form: f, attribute: :password_confirmation, input_type: :password_field, opts: { autocomplete: 'new-password' }) - = f.submit t('views.users.passwords.edit.submit'), id: 'submit-password', class: "fr-btn fr-btn--lg fr-mt-2w", data: { disable_with: t('views.users.passwords.edit.submit_loading') } + = f.submit t('views.users.passwords.edit.submit'), id: 'submit-password', class: "fr-btn fr-btn--lg fr-mt-2w", data: { disable_with: t('views.users.passwords.edit.submit_loading') } diff --git a/app/views/password_complexity/_bar.html.haml b/app/views/password_complexity/_bar.html.haml deleted file mode 100644 index a9b8c8262..000000000 --- a/app/views/password_complexity/_bar.html.haml +++ /dev/null @@ -1 +0,0 @@ -#complexity-bar.password-complexity{ class: "complexity-#{@length < @min_length ? @score/2 : @score}" } diff --git a/app/views/password_complexity/_field.html.haml b/app/views/password_complexity/_field.html.haml deleted file mode 100644 index 2e031f574..000000000 --- a/app/views/password_complexity/_field.html.haml +++ /dev/null @@ -1,9 +0,0 @@ -= form.password_field :password, autofocus: true, autocomplete: 'off', placeholder: 'Mot de passe', data: { controller: test_complexity ? 'turbo-input' : false, turbo_input_url_value: show_password_complexity_path } - -- if test_complexity - #complexity-bar.password-complexity - - .explication - #complexity-label{ style: 'font-weight: bold' } - Inscrivez un mot de passe. - Une courte phrase avec ponctuation peut être un mot de passe très sécurisé. diff --git a/app/views/password_complexity/_label.html.haml b/app/views/password_complexity/_label.html.haml deleted file mode 100644 index 2e9bda1d0..000000000 --- a/app/views/password_complexity/_label.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -#complexity-label{ style: 'font-weight: bold' } - - if @length > 0 - - if @length < @min_length - Le mot de passe doit faire au moins #{@min_length} caractères. - - else - - case @score - - when 0..1 - Mot de passe très vulnérable. - - when 2...@min_complexity - Mot de passe vulnérable. - - when @min_complexity...4 - Mot de passe acceptable. Vous pouvez valider...
ou améliorer votre mot de passe. - - else - Félicitations ! Mot de passe suffisamment fort et sécurisé. - - else - Inscrivez un mot de passe. diff --git a/app/views/password_complexity/show.turbo_stream.haml b/app/views/password_complexity/show.turbo_stream.haml index 3fd3648f6..461ad5b12 100644 --- a/app/views/password_complexity/show.turbo_stream.haml +++ b/app/views/password_complexity/show.turbo_stream.haml @@ -1,5 +1,6 @@ -= turbo_stream.replace 'complexity-label', partial: 'label' -= turbo_stream.replace 'complexity-bar', partial: 'bar' += turbo_stream.update 'password_complexity' do + = render PasswordComplexityComponent.new(length: @length, min_length: @min_length, score: @score, min_complexity: @min_complexity) + - if @score < @min_complexity || @length < @min_length = turbo_stream.disable 'submit-password' - else diff --git a/spec/controllers/password_complexity_controller_spec.rb b/spec/controllers/password_complexity_controller_spec.rb index b04985806..6d03cab40 100644 --- a/spec/controllers/password_complexity_controller_spec.rb +++ b/spec/controllers/password_complexity_controller_spec.rb @@ -27,8 +27,7 @@ describe PasswordComplexityController, type: :controller do it 'renders Javascript that updates the password complexity meter' do subject - expect(response.body).to include('complexity-label') - expect(response.body).to include('complexity-bar') + expect(response.body).to include('Mot de passe vulnérable') end end end