From fb28c5b8b929c9693653593a5faebf0fa717b1bc Mon Sep 17 00:00:00 2001 From: Mathieu Date: Mon, 6 Feb 2023 17:09:41 +0100 Subject: [PATCH] [Form] Check for `RepeatedType` child in `PasswordHasherListener` --- .../EventListener/PasswordHasherListener.php | 14 ++++-- ...asswordTypePasswordHasherExtensionTest.php | 50 +++++++++++++++++++ .../Tests/Fixtures/RepeatedPasswordField.php | 36 +++++++++++++ 3 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 src/Symfony/Component/Form/Tests/Fixtures/RepeatedPasswordField.php diff --git a/src/Symfony/Component/Form/Extension/PasswordHasher/EventListener/PasswordHasherListener.php b/src/Symfony/Component/Form/Extension/PasswordHasher/EventListener/PasswordHasherListener.php index e83a117c921c4..3fccfd8965810 100644 --- a/src/Symfony/Component/Form/Extension/PasswordHasher/EventListener/PasswordHasherListener.php +++ b/src/Symfony/Component/Form/Extension/PasswordHasher/EventListener/PasswordHasherListener.php @@ -71,12 +71,18 @@ public function hashPasswords(FormEvent $event) private function getTargetForm(FormInterface $form): FormInterface { - $parent = $form->getParent(); - - if ($parent && $parent->getConfig()->getType()->getInnerType() instanceof RepeatedType) { - return $parent; + if (!$parentForm = $form->getParent()) { + return $form; } + $parentType = $parentForm->getConfig()->getType(); + + do { + if ($parentType->getInnerType() instanceof RepeatedType) { + return $parentForm; + } + } while ($parentType = $parentType->getParent()); + return $form; } diff --git a/src/Symfony/Component/Form/Tests/Extension/PasswordHasher/Type/PasswordTypePasswordHasherExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/PasswordHasher/Type/PasswordTypePasswordHasherExtensionTest.php index a55843861c150..895a39c41b45d 100644 --- a/src/Symfony/Component/Form/Tests/Extension/PasswordHasher/Type/PasswordTypePasswordHasherExtensionTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/PasswordHasher/Type/PasswordTypePasswordHasherExtensionTest.php @@ -13,9 +13,12 @@ use PHPUnit\Framework\MockObject\MockObject; use Symfony\Component\Form\Exception\InvalidConfigurationException; +use Symfony\Component\Form\Extension\Core\Type\PasswordType; +use Symfony\Component\Form\Extension\Core\Type\RepeatedType; use Symfony\Component\Form\Extension\PasswordHasher\EventListener\PasswordHasherListener; use Symfony\Component\Form\Extension\PasswordHasher\PasswordHasherExtension; use Symfony\Component\Form\Test\TypeTestCase; +use Symfony\Component\Form\Tests\Fixtures\RepeatedPasswordField; use Symfony\Component\Form\Tests\Fixtures\User; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasher; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; @@ -113,6 +116,53 @@ public function testPasswordHashSuccessWithEmptyData() $this->assertSame($user->getPassword(), $hashedPassword); } + /** + * @dataProvider provideRepeatedPasswordField + */ + public function testRepeatedPasswordField(string $type, array $options = []) + { + $user = new User(); + + $plainPassword = 'PlainPassword'; + $hashedPassword = 'HashedPassword'; + + $this->passwordHasher + ->expects($this->once()) + ->method('hashPassword') + ->with($user, $plainPassword) + ->willReturn($hashedPassword) + ; + + $this->assertNull($user->getPassword()); + + $form = $this->factory + ->createBuilder(data: $user) + ->add('plainPassword', $type, $options) + ->getForm() + ; + + $form->submit(['plainPassword' => ['first' => $plainPassword, 'second' => $plainPassword]]); + + $this->assertTrue($form->isValid()); + $this->assertSame($user->getPassword(), $hashedPassword); + } + + public static function provideRepeatedPasswordField(): iterable + { + yield 'RepeatedType' => [ + RepeatedType::class, + [ + 'type' => PasswordType::class, + 'first_options' => [ + 'hash_property_path' => 'password', + ], + 'mapped' => false, + ], + ]; + + yield 'RepeatedType child' => [RepeatedPasswordField::class]; + } + public function testPasswordHashOnInvalidForm() { $user = new User(); diff --git a/src/Symfony/Component/Form/Tests/Fixtures/RepeatedPasswordField.php b/src/Symfony/Component/Form/Tests/Fixtures/RepeatedPasswordField.php new file mode 100644 index 0000000000000..605c81c4c78e3 --- /dev/null +++ b/src/Symfony/Component/Form/Tests/Fixtures/RepeatedPasswordField.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Fixtures; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\PasswordType; +use Symfony\Component\Form\Extension\Core\Type\RepeatedType; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class RepeatedPasswordField extends AbstractType +{ + public function getParent(): ?string + { + return RepeatedType::class; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'mapped' => false, + 'type' => PasswordType::class, + 'first_options' => [ + 'hash_property_path' => 'password', + ], + ]); + } +}