From d4d91ab4ec4f646916f8d8ede3ae20130c56e37c Mon Sep 17 00:00:00 2001 From: vgreb Date: Wed, 26 Aug 2026 01:33:59 +0200 Subject: [PATCH 1/2] =?UTF-8?q?Refonte=20page=20mode=20de=20passe=20oubli?= =?UTF-8?q?=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UserMembership/UserService.php | 12 +++- .../Controller/Auth/LostPasswordAction.php | 3 - templates/site/auth/lost_password.html.twig | 65 ++++++++++--------- .../UserMembership/UserServiceTest.php | 45 +++++++++++++ 4 files changed, 88 insertions(+), 37 deletions(-) create mode 100644 tests/unit/AppBundle/Association/UserMembership/UserServiceTest.php diff --git a/sources/AppBundle/Association/UserMembership/UserService.php b/sources/AppBundle/Association/UserMembership/UserService.php index 85c260350..3d3da4a14 100644 --- a/sources/AppBundle/Association/UserMembership/UserService.php +++ b/sources/AppBundle/Association/UserMembership/UserService.php @@ -15,6 +15,7 @@ use AppBundle\MembershipFee\Entity\Cotisation; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; class UserService { @@ -64,10 +65,15 @@ public function resetPassword(User $user): void */ public function resetPasswordForEmail($email): void { - $user = $this->userRepository->loadUserByEmailOrAlternateEmail($email); - if (null !== $user) { - $this->resetPassword($user); + try { + $user = $this->userRepository->loadUserByEmailOrAlternateEmail($email); + } catch (UserNotFoundException) { + // Email inconnu : on s'arrête sans le signaler, pour ne pas révéler quels + // emails correspondent à un compte. + return; } + + $this->resetPassword($user); } public function sendWelcomeEmail(User $user): bool diff --git a/sources/AppBundle/Controller/Auth/LostPasswordAction.php b/sources/AppBundle/Controller/Auth/LostPasswordAction.php index 6be9aa21c..c21db22f3 100644 --- a/sources/AppBundle/Controller/Auth/LostPasswordAction.php +++ b/sources/AppBundle/Controller/Auth/LostPasswordAction.php @@ -34,9 +34,6 @@ public function __invoke(Request $request): Response return $this->view->render('site/auth/lost_password.html.twig', [ 'form' => $form->createView(), - 'title' => 'Mot de passe perdu', - 'page' => 'motdepasse_perdu', - 'class' => 'panel-page', ]); } } diff --git a/templates/site/auth/lost_password.html.twig b/templates/site/auth/lost_password.html.twig index 1a36578ff..0e34c2490 100644 --- a/templates/site/auth/lost_password.html.twig +++ b/templates/site/auth/lost_password.html.twig @@ -1,36 +1,39 @@ -{% extends 'admin/association/membership/_base.html.twig' %} - -{% block page_title %}{% endblock %} - -{% block page_container_extra_classes %}remove-min-height-from-container-id{% endblock %} - -{% block page_content %} - -
- {{ form_start(form) }} -
- Mot de passe perdu -
-
-

- Indiquez votre email ici. Si un compte correspond à cet email, vous recevrez un nouveau mot de passe. -

-
-
- {{ form_label(form.email) }} - {{ form_widget(form.email) }} -
-
- {{ form_widget(form.submit, {attr: {"class": "button button--call-to-action"}}) }} -
-
-

- Retour au formulaire de connexion -

-
+{% extends 'layouts/site.html.twig' %} +{% form_theme form 'form_themes/tailwind.html.twig' %} + +{% block title %}Mot de passe perdu - AFUP{% endblock %} + +{% block content %} + +
+

Mot de passe perdu

+

+ Indiquez votre email ici. Si un compte correspond à cet email, vous recevrez un nouveau mot de passe. +

+ + {{ form_start(form, {attr: {class: 'w-full sm:w-200'}}) }} + + + + {# Le contrôleur pose un flash 'notice' : c'est le seul retour visible de la demande, + et layouts/site.html.twig n'affiche pas les flashs. #} + {% for message in app.flashes('notice') %} + {{ message }} + {% endfor %} + + {{ form_errors(form) }} + {{ form_row(form.email, {label: 'Email', attr: {autofocus: true}}) }} + +
+

+ Retour à la connexion +

+ {{ form_widget(form.submit) }}
-
+ + {{ form_end(form) }}
+ {% endblock %} diff --git a/tests/unit/AppBundle/Association/UserMembership/UserServiceTest.php b/tests/unit/AppBundle/Association/UserMembership/UserServiceTest.php new file mode 100644 index 000000000..068f2ad1c --- /dev/null +++ b/tests/unit/AppBundle/Association/UserMembership/UserServiceTest.php @@ -0,0 +1,45 @@ +createMock(UserRepository::class); + $userRepository + ->method('loadUserByEmailOrAlternateEmail') + ->willThrowException(new UserNotFoundException()); + $userRepository->expects($this->never())->method('save'); + + $mailer = $this->createMock(Mailer::class); + $mailer->expects($this->never())->method('send'); + + $userService = new UserService( + $userRepository, + $mailer, + $this->createStub(UrlGeneratorInterface::class), + $this->createStub(MembershipFeeService::class), + $this->createStub(UserPasswordHasherInterface::class), + ); + + $userService->resetPasswordForEmail('aucun-compte@example.invalid'); + } +} From 15b5a38f7944b71ef80b32e3765f706824a58525 Mon Sep 17 00:00:00 2001 From: vgreb Date: Wed, 26 Aug 2026 19:02:03 +0200 Subject: [PATCH 2/2] Remplacement du test unitaire par un test behat --- .../features/PublicSite/PasswordReset.feature | 5 +++ .../UserMembership/UserServiceTest.php | 45 ------------------- 2 files changed, 5 insertions(+), 45 deletions(-) delete mode 100644 tests/unit/AppBundle/Association/UserMembership/UserServiceTest.php diff --git a/tests/behat/features/PublicSite/PasswordReset.feature b/tests/behat/features/PublicSite/PasswordReset.feature index d278fb3aa..e51178878 100644 --- a/tests/behat/features/PublicSite/PasswordReset.feature +++ b/tests/behat/features/PublicSite/PasswordReset.feature @@ -6,3 +6,8 @@ Scenario: L'utilisateur reçoit une URL de connexion complète dans l'e-mail de When I request a password reset for "edmond.dupont@mycorp.fr" Then I should receive an email And the email should contain a full URL starting with "https://apachephptest:80/login" + +# Prévention contre l'énumération des comptes +Scenario: Un message générique est affiché si on soumet un email inconnu + When I request a password reset for "unkown.email@example.com" + Then I should see "Votre demande a été prise en compte. Si un compte correspond à cet email vous recevez un nouveau mot de passe rapidement." diff --git a/tests/unit/AppBundle/Association/UserMembership/UserServiceTest.php b/tests/unit/AppBundle/Association/UserMembership/UserServiceTest.php deleted file mode 100644 index 068f2ad1c..000000000 --- a/tests/unit/AppBundle/Association/UserMembership/UserServiceTest.php +++ /dev/null @@ -1,45 +0,0 @@ -createMock(UserRepository::class); - $userRepository - ->method('loadUserByEmailOrAlternateEmail') - ->willThrowException(new UserNotFoundException()); - $userRepository->expects($this->never())->method('save'); - - $mailer = $this->createMock(Mailer::class); - $mailer->expects($this->never())->method('send'); - - $userService = new UserService( - $userRepository, - $mailer, - $this->createStub(UrlGeneratorInterface::class), - $this->createStub(MembershipFeeService::class), - $this->createStub(UserPasswordHasherInterface::class), - ); - - $userService->resetPasswordForEmail('aucun-compte@example.invalid'); - } -}