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

Fixer des contraintes de longueur et de complexité des mots de passe #2224

Merged
merged 8 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
103 changes: 78 additions & 25 deletions public/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -963,32 +963,18 @@ document?.querySelectorAll('.fr-password-toggle')?.forEach(pwdToggle => {
})
})
document?.querySelector('form[name="login-creation-mdp-form"]')?.querySelectorAll('[name^="password"]').forEach(pwd => {
Copy link
Collaborator

@sfinx13 sfinx13 Feb 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Déporter toute cette logique dans un nouveau fichier js dans assets/controllers. ll est prévu de supprimer app.js

pwd.addEventListener('input', () => {
let pass = document?.querySelector('form[name="login-creation-mdp-form"] #login-password').value;
let repeat = document?.querySelector('form[name="login-creation-mdp-form"] #login-password-repeat').value;
let pwdMatchError = document?.querySelector('form[name="login-creation-mdp-form"] #password-match-error');
let submitBtn = document?.querySelector('form[name="login-creation-mdp-form"] #submitter');
submitBtn.addEventListener('click', (e) => {
e.preventDefault()
})
if (pass !== repeat) {
document?.querySelector('form[name="login-creation-mdp-form"]').querySelectorAll('.fr-input-group').forEach(iptGroup => {
iptGroup.classList.add('fr-input-group--error')
iptGroup.querySelector('.fr-input').classList.add('fr-input--error')
})
submitBtn.disabled = true;
pwdMatchError.classList.remove('fr-hidden')
} else {
document?.querySelector('form[name="login-creation-mdp-form"]').querySelectorAll('.fr-input-group--error,.fr-input--error').forEach(iptError => {
['fr-input-group--error', 'fr-input--error'].map(c => {
iptError.classList.remove(c)
});
})
pwdMatchError.classList.add('fr-hidden');
submitBtn.disabled = false;
}
})
pwd.addEventListener('input', canSubmitFormReinitPassword)
})
document?.querySelector('form[name="login-creation-mdp-form"]')?.addEventListener('submit', (event) => {
event.preventDefault();
let modalCgu = document.getElementById("fr-modal-cgu-bo");
numew marked this conversation as resolved.
Show resolved Hide resolved
dsfr(modalCgu).modal.conceal();
if(canSubmitFormReinitPassword()){
event.target.submit();
}
})


document.querySelector('#modal-dpe-opener')?.addEventListener('click', (event) => {
let urlDpe = event.target.getAttribute('data-dpe-url');
fetch(urlDpe).then(r => {
Expand Down Expand Up @@ -1019,6 +1005,73 @@ document.querySelector('#modal-dpe-opener')?.addEventListener('click', (event) =
})
})

function canSubmitFormReinitPassword() {
let pass = document?.querySelector('form[name="login-creation-mdp-form"] #login-password').value;
let repeat = document?.querySelector('form[name="login-creation-mdp-form"] #login-password-repeat').value;
let pwdMatchError = document?.querySelector('form[name="login-creation-mdp-form"] #password-match-error');
let submitBtn = document?.querySelector('form[name="login-creation-mdp-form"] #submitter');
numew marked this conversation as resolved.
Show resolved Hide resolved
let canSubmit = true;
document?.querySelector('form[name="login-creation-mdp-form"] .fr-input-group-password').classList.remove('fr-input-group--error')
document?.querySelector('form[name="login-creation-mdp-form"] .fr-input-group-password-repeat').classList.remove('fr-input-group--error')
document?.querySelectorAll('form[name="login-creation-mdp-form"] #password-input-messages .message-password').forEach((el) => {
el.classList.remove('fr-message--error', 'fr-message--valid');
el.classList.add('fr-message--info');
});
pwdMatchError.classList.add('fr-hidden')
submitBtn.disabled = false;
if (pass !== repeat) {
canSubmit = false;
pwdMatchError.classList.remove('fr-hidden')
}
console.log(pass);
numew marked this conversation as resolved.
Show resolved Hide resolved
if (pass.length < 8) {
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-length').classList.remove('fr-message--info')
numew marked this conversation as resolved.
Show resolved Hide resolved
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-length').classList.add('fr-message--error')
canSubmit = false;
}else{
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-length').classList.remove('fr-message--info')
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-length').classList.add('fr-message--valid')
}
if (!/[A-Z]/.test(pass)) {
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-maj').classList.remove('fr-message--info')
numew marked this conversation as resolved.
Show resolved Hide resolved
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-maj').classList.add('fr-message--error')
canSubmit = false;
}else{
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-maj').classList.remove('fr-message--info')
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-maj').classList.add('fr-message--valid')
}
if(!/[a-z]/.test(pass)){
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-min').classList.remove('fr-message--info')
numew marked this conversation as resolved.
Show resolved Hide resolved
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-min').classList.add('fr-message--error')
canSubmit = false;
}else{
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-min').classList.remove('fr-message--info')
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-min').classList.add('fr-message--valid')
}
if(!/[0-9]/.test(pass)){
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-nb').classList.remove('fr-message--info')
numew marked this conversation as resolved.
Show resolved Hide resolved
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-nb').classList.add('fr-message--error')
canSubmit = false;
}else{
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-nb').classList.remove('fr-message--info')
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-nb').classList.add('fr-message--valid')
}
if(!/[^a-zA-Z0-9]/.test(pass)){
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-special').classList.remove('fr-message--info')
numew marked this conversation as resolved.
Show resolved Hide resolved
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-special').classList.add('fr-message--error')
canSubmit = false;
}else{
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-special').classList.remove('fr-message--info')
document?.querySelector('form[name="login-creation-mdp-form"] #password-input-message-info-special').classList.add('fr-message--valid')
}
if(!canSubmit){
document?.querySelector('form[name="login-creation-mdp-form"] .fr-input-group-password').classList.add('fr-input-group--error')
document?.querySelector('form[name="login-creation-mdp-form"] .fr-input-group-password-repeat').classList.add('fr-input-group--error')
submitBtn.disabled = true;
}
return canSubmit;
}

const refetchAddress = (form) => {
// If the code postal is manually edited, we reinit the insee/geoloc and fetch the first result
form.querySelector('#signalement-insee-occupant').value = '';
Expand Down
7 changes: 4 additions & 3 deletions src/Command/AddUserCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
);
}

$password = $this->hasher->hashPassword($user, 'histologe');

$user->setPassword($password)->setStatut(User::STATUS_ACTIVE);
$user->setPassword('histologe-HI1')->setStatut(User::STATUS_ACTIVE);

/** @var ConstraintViolationList $errors */
$errors = $this->validator->validate($user, null, ['Default', 'password']);
Expand All @@ -205,6 +203,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return Command::FAILURE;
}

$password = $this->hasher->hashPassword($user, $user->getPassword());
$user->setPassword($password);

$this->entityManager->persist($user);
$this->entityManager->flush();

Expand Down
8 changes: 6 additions & 2 deletions src/Entity/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,13 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface

#[ORM\Column(type: 'string', nullable: true)]
#[Assert\NotBlank(groups: ['password'])]
#[Assert\Length(min: 8, max: 200, minMessage: 'Votre mot de passe doit contenir au moins {{ limit }} caratères', groups: ['password'])]
#[Assert\Length(min: 8, max: 200, minMessage: 'Le mot de passe doit contenir au moins {{ limit }} caratères', groups: ['password'])]
#[Assert\Regex(pattern: '/[A-Z]/', message: 'Le mot de passe doit contenir au moins une lettre majuscule.', groups: ['password'])]
#[Assert\Regex(pattern: '/[a-z]/', message: 'Le mot de passe doit contenir au moins une lettre minuscule.', groups: ['password'])]
#[Assert\Regex(pattern: '/[0-9]/', message: 'Le mot de passe doit contenir au moins un chiffre.', groups: ['password'])]
#[Assert\Regex(pattern: '/[^a-zA-Z0-9]/', message: 'Le mot de passe doit contenir au moins un caractère spécial.', groups: ['password'])]
#[Assert\NotCompromisedPassword(message: 'Ce mot de passe est compromis, veuillez en choisir un autre.', groups: ['password'])]
#[Assert\NotEqualTo(propertyPath: 'email', message: 'Votre mot de passe ne doit pas contenir votre email.', groups: ['password'])]
#[Assert\NotEqualTo(propertyPath: 'email', message: 'Le mot de passe ne doit pas être votre email.', groups: ['password'])]
private $password;
numew marked this conversation as resolved.
Show resolved Hide resolved

#[ORM\Column(length: 255, nullable: true)]
Expand Down
48 changes: 25 additions & 23 deletions templates/security/reset_password_new.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
</em>
{% endif %}
</header>
<form class="needs-validation fr-mt-5v fr-col-md-6" name="login-creation-mdp-form" method="POST"
<form class="fr-mt-5v fr-col-md-6" name="login-creation-mdp-form" method="POST"
novalidate="">
<dialog aria-labelledby="fr-modal-cgu-bo-title" id="fr-modal-cgu-bo" class="fr-modal" role="dialog">
<div class="fr-container fr-container--fluid fr-container-md">
Expand Down Expand Up @@ -63,41 +63,43 @@
</label>
<div class="fr-input">{{ user.email }}</div>
</div>
<div class="fr-input-group fr-grid-row fr-grid-row--middle">
<div class="fr-input-group fr-input-group-password fr-grid-row fr-grid-row--middle">
<label class="fr-label fr-col-12" for="login-password">
Mot de passe
<span class="fr-hint-text">Choisissez un mot de passe <u>fort et unique</u> <em>(minimum 8 caratères)</em></span>
<span class="fr-hint-text">Choisissez un mot de passe <u>fort et unique</u></span>
</label>
<input class="fr-input fr-col-11" aria-describedby="login-password-error-desc-error" type="password"
id="login-password" name="password" required minlength="8">
<span class="fr-btn fr-fi-eye-off-fill fr-col-1 fr-mt-2v fr-password-toggle"
title="Afficher/Cacher le mot de passe"></span>
<p id="login-password-error-desc-error" class="fr-error-text fr-hidden fr-col-12">
Veuillez saisir votre mot de passe.
</p>
<input class="fr-input fr-col-11" type="password" id="login-password" name="password" required minlength="8">
<span class="fr-btn fr-fi-eye-off-fill fr-col-1 fr-mt-2v fr-password-toggle" title="Afficher/Cacher le mot de passe"></span>
<div class="fr-messages-group" id="password-input-messages" aria-live="assertive">
<p class="fr-message">Votre mot de passe doit contenir :</p>
<p class="message-password fr-message fr-message--info" id="password-input-message-info-length">8 caractères minimum</p>
<p class="message-password fr-message fr-message--info" id="password-input-message-info-maj">1 caractère majuscule minimum</p>
<p class="message-password fr-message fr-message--info" id="password-input-message-info-min">1 caractère minuscule minimum</p>
<p class="message-password fr-message fr-message--info" id="password-input-message-info-nb">1 chiffre minimum</p>
<p class="message-password fr-message fr-message--info" id="password-input-message-info-special">1 caractère spécial minimum</p>
</div>
</div>
<div class="fr-input-group fr-grid-row fr-grid-row--middle">
<div class="fr-input-group fr-input-group-password-repeat fr-grid-row fr-grid-row--middle">
<label class="fr-label fr-col-12" for="login-password-repeat">
Confirmation du mot de passe
<span class="fr-hint-text">Saisissez à nouveau votre mot de passe</span>
</label>
<input class="fr-input fr-col-11" aria-describedby="login-password-error-desc-error" type="password"
id="login-password-repeat" name="password-repeat" required minlength="8">
<span class="fr-btn fr-fi-eye-off-fill fr-col-1 fr-mt-2v fr-password-toggle"
title="Afficher/Cacher le mot de passe"></span>
<p id="login-password-error-desc-error" class="fr-error-text fr-hidden fr-col-12">
Veuillez confirmer votre mot de passe.
</p>
</div>
<input type="hidden" name="_csrf_token" value="{{ csrf_token('create_password_'~ user.uuid) }}">
<div class="fr-form-group fr-hidden" id="password-match-error">
<p class="fr-error-text fr-col-12">
<input class="fr-input fr-col-11" type="password" id="login-password-repeat" name="password-repeat" required minlength="8">
<span class="fr-btn fr-fi-eye-off-fill fr-col-1 fr-mt-2v fr-password-toggle" title="Afficher/Cacher le mot de passe"></span>
<p id="password-match-error" class="fr-error-text fr-hidden fr-col-12">
Les mots de passes ne correspondent pas.
</p>
</div>
<input type="hidden" name="_csrf_token" value="{{ csrf_token('create_password_'~ user.uuid) }}">
<div class="fr-form-group">
<button class="fr-btn fr-icon-checkbox-circle-fill fr-btn--icon-right" disabled
aria-controls="fr-modal-cgu-bo" data-fr-opened="false" id="submitter">
{% if user.statut is same as constant('App\\Entity\\User::STATUS_INACTIVE') %}
aria-controls="fr-modal-cgu-bo" data-fr-opened="false" type="button"
{% else %}
type="submit"
{% endif %}
id="submitter"
>
Confirmer
</button>
</div>
Expand Down
Loading