refactor(password-complexity): as component for better form integration
This commit is contained in:
parent
1f930dfe23
commit
4f7839039d
11 changed files with 101 additions and 47 deletions
|
@ -9,12 +9,10 @@ $complexity-color-3: #FFD000;
|
||||||
$complexity-color-4: $green;
|
$complexity-color-4: $green;
|
||||||
|
|
||||||
.password-complexity {
|
.password-complexity {
|
||||||
margin-top: -24px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
background: $complexity-bg;
|
background: $complexity-bg;
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: $default-spacer;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
||||||
|
|
53
app/components/password_complexity_component.rb
Normal file
53
app/components/password_complexity_component.rb
Normal file
|
@ -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
|
|
@ -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.
|
|
@ -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é.
|
|
@ -0,0 +1,6 @@
|
||||||
|
%div{ class: complexity_classes }
|
||||||
|
|
||||||
|
%div{ class: alert_classes }
|
||||||
|
%h3.fr-alert__title= title
|
||||||
|
- if !success?
|
||||||
|
%p= t(".hint")
|
|
@ -3,8 +3,9 @@
|
||||||
- content_for :footer do
|
- content_for :footer do
|
||||||
= render partial: 'root/footer'
|
= render partial: 'root/footer'
|
||||||
|
|
||||||
.container.devise-container
|
.fr-container.fr-my-5w
|
||||||
.one-column-centered
|
.fr-grid-row.fr-grid-row--center
|
||||||
|
.fr-col-lg-6
|
||||||
= devise_error_messages!
|
= devise_error_messages!
|
||||||
|
|
||||||
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :patch, class: '' }) do |f|
|
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :patch, class: '' }) do |f|
|
||||||
|
@ -19,7 +20,9 @@
|
||||||
= render Dsfr::InputComponent.new(form: f, attribute: :password, input_type: :password_field,
|
= 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 }})
|
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
|
.fr-fieldset__element
|
||||||
= render Dsfr::InputComponent.new(form: f, attribute: :password_confirmation, input_type: :password_field, opts: { autocomplete: 'new-password' })
|
= render Dsfr::InputComponent.new(form: f, attribute: :password_confirmation, input_type: :password_field, opts: { autocomplete: 'new-password' })
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
#complexity-bar.password-complexity{ class: "complexity-#{@length < @min_length ? @score/2 : @score}" }
|
|
|
@ -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é.
|
|
|
@ -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...<br> ou améliorer votre mot de passe.
|
|
||||||
- else
|
|
||||||
Félicitations ! Mot de passe suffisamment fort et sécurisé.
|
|
||||||
- else
|
|
||||||
Inscrivez un mot de passe.
|
|
|
@ -1,5 +1,6 @@
|
||||||
= turbo_stream.replace 'complexity-label', partial: 'label'
|
= turbo_stream.update 'password_complexity' do
|
||||||
= turbo_stream.replace 'complexity-bar', partial: 'bar'
|
= render PasswordComplexityComponent.new(length: @length, min_length: @min_length, score: @score, min_complexity: @min_complexity)
|
||||||
|
|
||||||
- if @score < @min_complexity || @length < @min_length
|
- if @score < @min_complexity || @length < @min_length
|
||||||
= turbo_stream.disable 'submit-password'
|
= turbo_stream.disable 'submit-password'
|
||||||
- else
|
- else
|
||||||
|
|
|
@ -27,8 +27,7 @@ describe PasswordComplexityController, type: :controller do
|
||||||
|
|
||||||
it 'renders Javascript that updates the password complexity meter' do
|
it 'renders Javascript that updates the password complexity meter' do
|
||||||
subject
|
subject
|
||||||
expect(response.body).to include('complexity-label')
|
expect(response.body).to include('Mot de passe vulnérable')
|
||||||
expect(response.body).to include('complexity-bar')
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue