From b8f300d34b273a08de1c587785be8621c54f323b Mon Sep 17 00:00:00 2001 From: Amr Ezzat <3mr3zzat@gmail.com> Date: Fri, 10 Jan 2020 14:19:32 +0200 Subject: [PATCH 01/27] Add Validation on RequestResetPassword --- .../Customer/RequestPasswordResettingAction.php | 16 ++++++++++++++-- .../config/services/actions/customer.xml | 1 + .../GenerateResetPasswordTokenRequest.xml | 12 ++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 src/Resources/config/validation/customer/GenerateResetPasswordTokenRequest.xml diff --git a/src/Controller/Customer/RequestPasswordResettingAction.php b/src/Controller/Customer/RequestPasswordResettingAction.php index 7a6a12910..0a52d1491 100644 --- a/src/Controller/Customer/RequestPasswordResettingAction.php +++ b/src/Controller/Customer/RequestPasswordResettingAction.php @@ -10,6 +10,7 @@ use Sylius\Component\Core\Model\ChannelInterface; use Sylius\ShopApiPlugin\CommandProvider\ChannelBasedCommandProviderInterface; use Sylius\ShopApiPlugin\CommandProvider\CommandProviderInterface; +use Sylius\ShopApiPlugin\Factory\ValidationErrorViewFactoryInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Messenger\MessageBusInterface; @@ -22,6 +23,9 @@ final class RequestPasswordResettingAction /** @var MessageBusInterface */ private $bus; + /** @var ValidationErrorViewFactoryInterface */ + private $validationErrorViewFactory; + /** @var ChannelContextInterface */ private $channelContext; @@ -34,10 +38,12 @@ final class RequestPasswordResettingAction public function __construct( ViewHandlerInterface $viewHandler, MessageBusInterface $bus, + ValidationErrorViewFactoryInterface $validationErrorViewFactory, ChannelContextInterface $channelContext, CommandProviderInterface $generateResetPasswordTokenCommandProvider, ChannelBasedCommandProviderInterface $sendResetPasswordTokenCommandProvider - ) { + ) + { $this->viewHandler = $viewHandler; $this->bus = $bus; $this->channelContext = $channelContext; @@ -49,7 +55,13 @@ public function __invoke(Request $request): Response { /** @var ChannelInterface $channel */ $channel = $this->channelContext->getChannel(); - + $validationResults = $this->generateResetPasswordTokenCommandProvider->validate($request); + if (0 !== count($validationResults)) { + return $this->viewHandler->handle(View::create( + $this->validationErrorViewFactory->create($validationResults), + Response::HTTP_BAD_REQUEST + )); + } $this->bus->dispatch($this->generateResetPasswordTokenCommandProvider->getCommand($request)); $this->bus->dispatch($this->sendResetPasswordTokenCommandProvider->getCommand($request, $channel)); diff --git a/src/Resources/config/services/actions/customer.xml b/src/Resources/config/services/actions/customer.xml index 4a241f8d9..261c40ba6 100644 --- a/src/Resources/config/services/actions/customer.xml +++ b/src/Resources/config/services/actions/customer.xml @@ -42,6 +42,7 @@ > + diff --git a/src/Resources/config/validation/customer/GenerateResetPasswordTokenRequest.xml b/src/Resources/config/validation/customer/GenerateResetPasswordTokenRequest.xml new file mode 100644 index 000000000..7e965b564 --- /dev/null +++ b/src/Resources/config/validation/customer/GenerateResetPasswordTokenRequest.xml @@ -0,0 +1,12 @@ + + + + + + + + + + From 58fb7748547333a6542c57eafa3442e25f26330c Mon Sep 17 00:00:00 2001 From: Amr Ezzat <3mr3zzat@gmail.com> Date: Fri, 10 Jan 2020 14:20:07 +0200 Subject: [PATCH 02/27] Add Tests --- .../Customer/RequestPasswordResettingApiTest.php | 13 +++++++++++++ .../request_reset_password_failed_email.json | 9 +++++++++ 2 files changed, 22 insertions(+) create mode 100644 tests/Responses/Expected/customer/request_reset_password_failed_email.json diff --git a/tests/Controller/Customer/RequestPasswordResettingApiTest.php b/tests/Controller/Customer/RequestPasswordResettingApiTest.php index 3c496f2f3..9627d7060 100644 --- a/tests/Controller/Customer/RequestPasswordResettingApiTest.php +++ b/tests/Controller/Customer/RequestPasswordResettingApiTest.php @@ -34,6 +34,19 @@ public function it_allows_to_reset_user_password(): void $this->assertTrue($emailChecker->hasRecipient('oliver@queen.com')); } + public function it_does_not_allow_to_reset_user_password_without_entering_valid_email(): void + { + $this->loadFixturesFromFiles(['channel.yml', 'customer.yml']); + + $data = '{"email": "oliver"}'; + + $this->client->request('PUT', '/shop-api/request-password-reset', [], [], self::CONTENT_TYPE_HEADER, $data); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'customer/request_reset_password_failed_email', Response::HTTP_BAD_REQUEST); + } + + protected function getContainer(): ContainerInterface { return static::$sharedKernel->getContainer(); diff --git a/tests/Responses/Expected/customer/request_reset_password_failed_email.json b/tests/Responses/Expected/customer/request_reset_password_failed_email.json new file mode 100644 index 000000000..f33026a6a --- /dev/null +++ b/tests/Responses/Expected/customer/request_reset_password_failed_email.json @@ -0,0 +1,9 @@ +{ + "code": 400, + "message": "Validation failed", + "errors": { + "email": [ + "This value is not a valid email address." + ] + } +} From 6bcc97e51b20e5fab8b23e7fbc6d82e0a8f7d9d4 Mon Sep 17 00:00:00 2001 From: Amr Ezzat <3mr3zzat@gmail.com> Date: Fri, 10 Jan 2020 18:42:12 +0200 Subject: [PATCH 03/27] Fix Email Validation --- .../RequestPasswordResettingAction.php | 1 + .../GenerateResetPasswordTokenRequest.xml | 5 ++++- .../RequestPasswordResettingApiTest.php | 20 +++++++++++++++++++ .../request_reset_password_empty_email.json | 9 +++++++++ .../request_reset_password_failed_email.json | 14 ++++++------- 5 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 tests/Responses/Expected/customer/request_reset_password_empty_email.json diff --git a/src/Controller/Customer/RequestPasswordResettingAction.php b/src/Controller/Customer/RequestPasswordResettingAction.php index 0a52d1491..21a51c861 100644 --- a/src/Controller/Customer/RequestPasswordResettingAction.php +++ b/src/Controller/Customer/RequestPasswordResettingAction.php @@ -46,6 +46,7 @@ public function __construct( { $this->viewHandler = $viewHandler; $this->bus = $bus; + $this->validationErrorViewFactory = $validationErrorViewFactory; $this->channelContext = $channelContext; $this->generateResetPasswordTokenCommandProvider = $generateResetPasswordTokenCommandProvider; $this->sendResetPasswordTokenCommandProvider = $sendResetPasswordTokenCommandProvider; diff --git a/src/Resources/config/validation/customer/GenerateResetPasswordTokenRequest.xml b/src/Resources/config/validation/customer/GenerateResetPasswordTokenRequest.xml index 7e965b564..64b0d1339 100644 --- a/src/Resources/config/validation/customer/GenerateResetPasswordTokenRequest.xml +++ b/src/Resources/config/validation/customer/GenerateResetPasswordTokenRequest.xml @@ -2,8 +2,11 @@ - + + + + diff --git a/tests/Controller/Customer/RequestPasswordResettingApiTest.php b/tests/Controller/Customer/RequestPasswordResettingApiTest.php index 9627d7060..16c56154e 100644 --- a/tests/Controller/Customer/RequestPasswordResettingApiTest.php +++ b/tests/Controller/Customer/RequestPasswordResettingApiTest.php @@ -17,6 +17,8 @@ final class RequestPasswordResettingApiTest extends JsonApiTestCase /** * @test */ + + public function it_allows_to_reset_user_password(): void { $this->loadFixturesFromFiles(['channel.yml', 'customer.yml']); @@ -34,6 +36,10 @@ public function it_allows_to_reset_user_password(): void $this->assertTrue($emailChecker->hasRecipient('oliver@queen.com')); } + + /** + * @test + */ public function it_does_not_allow_to_reset_user_password_without_entering_valid_email(): void { $this->loadFixturesFromFiles(['channel.yml', 'customer.yml']); @@ -46,6 +52,20 @@ public function it_does_not_allow_to_reset_user_password_without_entering_valid_ $this->assertResponse($response, 'customer/request_reset_password_failed_email', Response::HTTP_BAD_REQUEST); } + /** + * @test + */ + public function it_does_not_allow_to_reset_user_password_without_entering_email(): void + { + $this->loadFixturesFromFiles(['channel.yml', 'customer.yml']); + + $data = '{}'; + + $this->client->request('PUT', '/shop-api/request-password-reset', [], [], self::CONTENT_TYPE_HEADER, $data); + + $response = $this->client->getResponse(); + $this->assertResponse($response, 'customer/request_reset_password_empty_email', Response::HTTP_BAD_REQUEST); + } protected function getContainer(): ContainerInterface { diff --git a/tests/Responses/Expected/customer/request_reset_password_empty_email.json b/tests/Responses/Expected/customer/request_reset_password_empty_email.json new file mode 100644 index 000000000..1a910c0fa --- /dev/null +++ b/tests/Responses/Expected/customer/request_reset_password_empty_email.json @@ -0,0 +1,9 @@ +{ + "code": 400, + "message": "Validation failed", + "errors": { + "email": [ + "Please enter your email." + ] + } +} diff --git a/tests/Responses/Expected/customer/request_reset_password_failed_email.json b/tests/Responses/Expected/customer/request_reset_password_failed_email.json index f33026a6a..168f79134 100644 --- a/tests/Responses/Expected/customer/request_reset_password_failed_email.json +++ b/tests/Responses/Expected/customer/request_reset_password_failed_email.json @@ -1,9 +1,9 @@ { - "code": 400, - "message": "Validation failed", - "errors": { - "email": [ - "This value is not a valid email address." - ] - } + "code": 400, + "message": "Validation failed", + "errors": { + "email": [ + "This email is invalid." + ] + } } From b761ad9b1b79e97377b78de0b33fe665f0b63ce4 Mon Sep 17 00:00:00 2001 From: Amr Ezzat <3mr3zzat@gmail.com> Date: Fri, 10 Jan 2020 19:13:55 +0200 Subject: [PATCH 04/27] Handle email not exists RequestResetPassword --- .../GenerateResetPasswordTokenHandler.php | 9 ++++----- .../Customer/SendResetPasswordTokenHandler.php | 16 ++++++++-------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/Handler/Customer/GenerateResetPasswordTokenHandler.php b/src/Handler/Customer/GenerateResetPasswordTokenHandler.php index 18f1b6c4f..97718ef6a 100644 --- a/src/Handler/Customer/GenerateResetPasswordTokenHandler.php +++ b/src/Handler/Customer/GenerateResetPasswordTokenHandler.php @@ -8,7 +8,6 @@ use Sylius\Component\User\Repository\UserRepositoryInterface; use Sylius\Component\User\Security\Generator\GeneratorInterface; use Sylius\ShopApiPlugin\Command\Customer\GenerateResetPasswordToken; -use Webmozart\Assert\Assert; final class GenerateResetPasswordTokenHandler { @@ -30,10 +29,10 @@ public function __invoke(GenerateResetPasswordToken $generateResetPasswordToken) /** @var ShopUserInterface $user */ $user = $this->userRepository->findOneByEmail($email); + if (null !== $user) { + $user->setPasswordResetToken($this->tokenGenerator->generate()); + $user->setPasswordRequestedAt(new \DateTime()); + } - Assert::notNull($user, sprintf('User with %s email has not been found.', $email)); - - $user->setPasswordResetToken($this->tokenGenerator->generate()); - $user->setPasswordRequestedAt(new \DateTime()); } } diff --git a/src/Handler/Customer/SendResetPasswordTokenHandler.php b/src/Handler/Customer/SendResetPasswordTokenHandler.php index 2054a54b2..411a357c6 100644 --- a/src/Handler/Customer/SendResetPasswordTokenHandler.php +++ b/src/Handler/Customer/SendResetPasswordTokenHandler.php @@ -31,14 +31,14 @@ public function __invoke(SendResetPasswordToken $resendResetPasswordToken): void /** @var ShopUserInterface $user */ $user = $this->userRepository->findOneByEmail($email); + if (null !== $user) { + Assert::notNull($user->getPasswordResetToken(), sprintf('User with %s email has not verification token defined.', $email)); + $this->sender->send( + Emails::EMAIL_RESET_PASSWORD_TOKEN, + [$email], + ['user' => $user, 'channelCode' => $resendResetPasswordToken->channelCode()] + ); + } - Assert::notNull($user, sprintf('User with %s email has not been found.', $email)); - Assert::notNull($user->getPasswordResetToken(), sprintf('User with %s email has not verification token defined.', $email)); - - $this->sender->send( - Emails::EMAIL_RESET_PASSWORD_TOKEN, - [$email], - ['user' => $user, 'channelCode' => $resendResetPasswordToken->channelCode()] - ); } } From 019cb00c383e4cade24c8035e0e0d14c1b642f97 Mon Sep 17 00:00:00 2001 From: Amr Ezzat <3mr3zzat@gmail.com> Date: Fri, 10 Jan 2020 19:16:01 +0200 Subject: [PATCH 05/27] Add Test email doesn't exist RequestResetPassword --- .../Customer/RequestPasswordResettingApiTest.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/Controller/Customer/RequestPasswordResettingApiTest.php b/tests/Controller/Customer/RequestPasswordResettingApiTest.php index 16c56154e..76df861c8 100644 --- a/tests/Controller/Customer/RequestPasswordResettingApiTest.php +++ b/tests/Controller/Customer/RequestPasswordResettingApiTest.php @@ -67,6 +67,21 @@ public function it_does_not_allow_to_reset_user_password_without_entering_email( $this->assertResponse($response, 'customer/request_reset_password_empty_email', Response::HTTP_BAD_REQUEST); } + /** + * @test + */ + public function it_allow_to_reset_user_password_without_sending_mail_user_not_exist(): void + { + $this->loadFixturesFromFiles(['channel.yml', 'customer.yml']); + + $data = '{"email": "Amr@amr.com"}'; + + $this->client->request('PUT', '/shop-api/request-password-reset', [], [], self::CONTENT_TYPE_HEADER, $data); + + $response = $this->client->getResponse(); + $this->assertResponseCode($response, Response::HTTP_NO_CONTENT); + } + protected function getContainer(): ContainerInterface { return static::$sharedKernel->getContainer(); From a5711ddebc8439c495ecc4a2f1d8f9952e580974 Mon Sep 17 00:00:00 2001 From: Amr Ezzat <3mr3zzat@gmail.com> Date: Fri, 10 Jan 2020 19:16:28 +0200 Subject: [PATCH 06/27] Add docs email doesn't exist RequestResetPassword --- doc/swagger.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/swagger.yml b/doc/swagger.yml index eda95c905..4a6587954 100644 --- a/doc/swagger.yml +++ b/doc/swagger.yml @@ -709,9 +709,9 @@ paths: $ref: "#/definitions/RequestPasswordResetting" responses: 204: - description: "Reset password request has been sent." - 500: - description: "User with provided email has not been found." + description: "Reset password request has been sent If the email exists in our system,." + 400: + description: "Email not valid" /password-reset/{token}: parameters: From 3d320f82bf3b031dde287b67531041bfb34116e4 Mon Sep 17 00:00:00 2001 From: Amr Ezzat <3mr3zzat@gmail.com> Date: Fri, 10 Jan 2020 21:49:34 +0200 Subject: [PATCH 07/27] Remove user not found from specs --- .../Customer/GenerateResetPasswordTokenHandlerSpec.php | 7 ------- .../Customer/SendResetPasswordTokenHandlerSpec.php | 9 +-------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php b/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php index e2a950c60..e7b8fb2d4 100644 --- a/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php +++ b/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php @@ -39,11 +39,4 @@ function it_handles_generating_user_verification_token( $this(new GenerateResetPasswordToken('example@customer.com')); } - function it_throws_an_exception_if_user_has_not_been_found( - UserRepositoryInterface $userRepository - ): void { - $userRepository->findOneByEmail('example@customer.com')->willReturn(null); - - $this->shouldThrow(\InvalidArgumentException::class)->during('__invoke', [new GenerateResetPasswordToken('example@customer.com')]); - } } diff --git a/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php b/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php index ab62d3b6e..18aff4fe7 100644 --- a/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php +++ b/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php @@ -36,14 +36,7 @@ function it_handles_emailing_user_with_verification_email( $this(new SendResetPasswordToken('example@customer.com', 'WEB_GB')); } - - function it_throws_an_exception_if_user_has_not_been_found( - UserRepositoryInterface $userRepository - ): void { - $userRepository->findOneByEmail('example@customer.com')->willReturn(null); - - $this->shouldThrow(\InvalidArgumentException::class)->during('__invoke', [new SendResetPasswordToken('example@customer.com', 'WEB_GB')]); - } + function it_throws_an_exception_if_user_has_not_verification_token( UserRepositoryInterface $userRepository, From caaeeca5a7b7080ece258fac8a5e8d2ee9fbab6d Mon Sep 17 00:00:00 2001 From: mamazu <14860264+mamazu@users.noreply.github.com> Date: Sat, 11 Jan 2020 13:07:07 +0100 Subject: [PATCH 08/27] Codestyle --- .../Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php | 1 - spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php | 1 - src/Controller/Customer/RequestPasswordResettingAction.php | 3 +-- src/Handler/Customer/GenerateResetPasswordTokenHandler.php | 1 - src/Handler/Customer/SendResetPasswordTokenHandler.php | 1 - tests/Controller/Customer/RequestPasswordResettingApiTest.php | 3 --- 6 files changed, 1 insertion(+), 9 deletions(-) diff --git a/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php b/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php index e7b8fb2d4..edd664d9e 100644 --- a/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php +++ b/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php @@ -38,5 +38,4 @@ function it_handles_generating_user_verification_token( $this(new GenerateResetPasswordToken('example@customer.com')); } - } diff --git a/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php b/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php index 18aff4fe7..1f3d01379 100644 --- a/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php +++ b/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php @@ -36,7 +36,6 @@ function it_handles_emailing_user_with_verification_email( $this(new SendResetPasswordToken('example@customer.com', 'WEB_GB')); } - function it_throws_an_exception_if_user_has_not_verification_token( UserRepositoryInterface $userRepository, diff --git a/src/Controller/Customer/RequestPasswordResettingAction.php b/src/Controller/Customer/RequestPasswordResettingAction.php index 21a51c861..e294820d5 100644 --- a/src/Controller/Customer/RequestPasswordResettingAction.php +++ b/src/Controller/Customer/RequestPasswordResettingAction.php @@ -42,8 +42,7 @@ public function __construct( ChannelContextInterface $channelContext, CommandProviderInterface $generateResetPasswordTokenCommandProvider, ChannelBasedCommandProviderInterface $sendResetPasswordTokenCommandProvider - ) - { + ) { $this->viewHandler = $viewHandler; $this->bus = $bus; $this->validationErrorViewFactory = $validationErrorViewFactory; diff --git a/src/Handler/Customer/GenerateResetPasswordTokenHandler.php b/src/Handler/Customer/GenerateResetPasswordTokenHandler.php index 97718ef6a..be21de7cd 100644 --- a/src/Handler/Customer/GenerateResetPasswordTokenHandler.php +++ b/src/Handler/Customer/GenerateResetPasswordTokenHandler.php @@ -33,6 +33,5 @@ public function __invoke(GenerateResetPasswordToken $generateResetPasswordToken) $user->setPasswordResetToken($this->tokenGenerator->generate()); $user->setPasswordRequestedAt(new \DateTime()); } - } } diff --git a/src/Handler/Customer/SendResetPasswordTokenHandler.php b/src/Handler/Customer/SendResetPasswordTokenHandler.php index 411a357c6..7169b97d4 100644 --- a/src/Handler/Customer/SendResetPasswordTokenHandler.php +++ b/src/Handler/Customer/SendResetPasswordTokenHandler.php @@ -39,6 +39,5 @@ public function __invoke(SendResetPasswordToken $resendResetPasswordToken): void ['user' => $user, 'channelCode' => $resendResetPasswordToken->channelCode()] ); } - } } diff --git a/tests/Controller/Customer/RequestPasswordResettingApiTest.php b/tests/Controller/Customer/RequestPasswordResettingApiTest.php index 76df861c8..1302bcdcb 100644 --- a/tests/Controller/Customer/RequestPasswordResettingApiTest.php +++ b/tests/Controller/Customer/RequestPasswordResettingApiTest.php @@ -17,8 +17,6 @@ final class RequestPasswordResettingApiTest extends JsonApiTestCase /** * @test */ - - public function it_allows_to_reset_user_password(): void { $this->loadFixturesFromFiles(['channel.yml', 'customer.yml']); @@ -36,7 +34,6 @@ public function it_allows_to_reset_user_password(): void $this->assertTrue($emailChecker->hasRecipient('oliver@queen.com')); } - /** * @test */ From 133f48cb1ffc02ad0d3e027b5961ea1ea67fa2e2 Mon Sep 17 00:00:00 2001 From: mamazu <14860264+mamazu@users.noreply.github.com> Date: Sat, 11 Jan 2020 13:10:43 +0100 Subject: [PATCH 09/27] Documenting changes in passwort reset endpoint --- UPGRADE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/UPGRADE.md b/UPGRADE.md index 21fc074db..6b53e3f2c 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -7,6 +7,7 @@ access_control: - { path: "%sylius_shop_api.security.regex%/me", role: ROLE_USER} ``` +* The forgot passwort function now doesn't return an error if the user with this email is not found. It might give a potential attacker information about which users are registered in the shop. # UPGRADE FROM 1.0.0-rc.2 or 1.0.0-rc.3 to 1.0.0 From 113a9805fd692f0de30141a041557b0e79d8182f Mon Sep 17 00:00:00 2001 From: Amr Ezzat <3mr3zzat@gmail.com> Date: Sat, 11 Jan 2020 15:40:00 +0200 Subject: [PATCH 10/27] Fix Typo --- UPGRADE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPGRADE.md b/UPGRADE.md index 6b53e3f2c..a9792b126 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -7,7 +7,7 @@ access_control: - { path: "%sylius_shop_api.security.regex%/me", role: ROLE_USER} ``` -* The forgot passwort function now doesn't return an error if the user with this email is not found. It might give a potential attacker information about which users are registered in the shop. +* The forgot password function now doesn't return an error if the user with this email is not found. It might give a potential attacker information about which users are registered in the shop. # UPGRADE FROM 1.0.0-rc.2 or 1.0.0-rc.3 to 1.0.0 From fefbc304278fba45656b57d2dca842118cc7e1a5 Mon Sep 17 00:00:00 2001 From: Amr Ezzat <3mr3zzat@gmail.com> Date: Sat, 11 Jan 2020 16:14:29 +0200 Subject: [PATCH 11/27] Make Logic clearer ResetPassword --- .../GenerateResetPasswordTokenHandler.php | 8 +++++--- .../Customer/SendResetPasswordTokenHandler.php | 16 +++++++++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Handler/Customer/GenerateResetPasswordTokenHandler.php b/src/Handler/Customer/GenerateResetPasswordTokenHandler.php index be21de7cd..2c16b0565 100644 --- a/src/Handler/Customer/GenerateResetPasswordTokenHandler.php +++ b/src/Handler/Customer/GenerateResetPasswordTokenHandler.php @@ -29,9 +29,11 @@ public function __invoke(GenerateResetPasswordToken $generateResetPasswordToken) /** @var ShopUserInterface $user */ $user = $this->userRepository->findOneByEmail($email); - if (null !== $user) { - $user->setPasswordResetToken($this->tokenGenerator->generate()); - $user->setPasswordRequestedAt(new \DateTime()); + if (null === $user) { + return; } + $user->setPasswordResetToken($this->tokenGenerator->generate()); + $user->setPasswordRequestedAt(new \DateTime()); + } } diff --git a/src/Handler/Customer/SendResetPasswordTokenHandler.php b/src/Handler/Customer/SendResetPasswordTokenHandler.php index 7169b97d4..00ed4e521 100644 --- a/src/Handler/Customer/SendResetPasswordTokenHandler.php +++ b/src/Handler/Customer/SendResetPasswordTokenHandler.php @@ -31,13 +31,15 @@ public function __invoke(SendResetPasswordToken $resendResetPasswordToken): void /** @var ShopUserInterface $user */ $user = $this->userRepository->findOneByEmail($email); - if (null !== $user) { - Assert::notNull($user->getPasswordResetToken(), sprintf('User with %s email has not verification token defined.', $email)); - $this->sender->send( - Emails::EMAIL_RESET_PASSWORD_TOKEN, - [$email], - ['user' => $user, 'channelCode' => $resendResetPasswordToken->channelCode()] - ); + if (null === $user) { + return; } + Assert::notNull($user->getPasswordResetToken(), sprintf('User with %s email has not verification token defined.', $email)); + $this->sender->send( + Emails::EMAIL_RESET_PASSWORD_TOKEN, + [$email], + ['user' => $user, 'channelCode' => $resendResetPasswordToken->channelCode()] + ); } + } From e19b3a3dfe6605e4ab3e4b3c5722d91e3274ce70 Mon Sep 17 00:00:00 2001 From: Amr Ezzat <3mr3zzat@gmail.com> Date: Sat, 11 Jan 2020 18:52:59 +0200 Subject: [PATCH 12/27] Add Specs ResetPassword --- .../GenerateResetPasswordTokenHandlerSpec.php | 11 +++++++++++ .../Customer/SendResetPasswordTokenHandlerSpec.php | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php b/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php index edd664d9e..a22ef9492 100644 --- a/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php +++ b/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php @@ -38,4 +38,15 @@ function it_handles_generating_user_verification_token( $this(new GenerateResetPasswordToken('example@customer.com')); } + + function it_continues_if_user_not_found( + UserRepositoryInterface $userRepository, + GeneratorInterface $tokenGenerator, + ShopUserInterface $user + ): void { + $userRepository->findOneByEmail('amr@amr.com')->willReturn(null); + $tokenGenerator->generate()->shouldNotBeCalled(); + $user->setPasswordResetToken('RANDOM_TOKEN')->shouldNotBeCalled(); + $user->setPasswordRequestedAt(Argument::type(\DateTime::class))->shouldNotBeCalled(); + } } diff --git a/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php b/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php index 1f3d01379..6ea679422 100644 --- a/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php +++ b/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php @@ -46,4 +46,13 @@ function it_throws_an_exception_if_user_has_not_verification_token( $this->shouldThrow(\InvalidArgumentException::class)->during('__invoke', [new SendResetPasswordToken('example@customer.com', 'WEB_GB')]); } + + function it_continues_if_user_not_found( + UserRepositoryInterface $userRepository, + SenderInterface $sender, + ShopUserInterface $user + ): void { + $userRepository->findOneByEmail('amr@amr.com')->willReturn(null); + $sender->send(Emails::EMAIL_RESET_PASSWORD_TOKEN, ['example@customer.com'], ['user' => $user, 'channelCode' => 'WEB_GB'])->shouldNotBeCalled(); + } } From 7667198d963e4f9b416a403d2788f830918ebb76 Mon Sep 17 00:00:00 2001 From: Amr Ezzat <3mr3zzat@gmail.com> Date: Thu, 16 Jan 2020 23:44:49 +0200 Subject: [PATCH 13/27] Fix SendResetPassword Specs --- spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php b/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php index 6ea679422..953ee2daf 100644 --- a/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php +++ b/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php @@ -5,6 +5,7 @@ namespace spec\Sylius\ShopApiPlugin\Handler\Customer; use PhpSpec\ObjectBehavior; +use Prophecy\Argument; use Sylius\Component\Core\Model\ShopUserInterface; use Sylius\Component\Mailer\Sender\SenderInterface; use Sylius\Component\User\Repository\UserRepositoryInterface; @@ -53,6 +54,6 @@ function it_continues_if_user_not_found( ShopUserInterface $user ): void { $userRepository->findOneByEmail('amr@amr.com')->willReturn(null); - $sender->send(Emails::EMAIL_RESET_PASSWORD_TOKEN, ['example@customer.com'], ['user' => $user, 'channelCode' => 'WEB_GB'])->shouldNotBeCalled(); + $sender->send(Argument::any())->shouldNotBeCalled(); } } From 3117dc81db298896e8fc93df50d826c18670b498 Mon Sep 17 00:00:00 2001 From: Amr Ezzat <3mr3zzat@gmail.com> Date: Fri, 17 Jan 2020 11:28:47 +0200 Subject: [PATCH 14/27] Fix Composer --- src/Handler/Customer/GenerateResetPasswordTokenHandler.php | 1 - src/Handler/Customer/SendResetPasswordTokenHandler.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Handler/Customer/GenerateResetPasswordTokenHandler.php b/src/Handler/Customer/GenerateResetPasswordTokenHandler.php index 2c16b0565..4cf29f834 100644 --- a/src/Handler/Customer/GenerateResetPasswordTokenHandler.php +++ b/src/Handler/Customer/GenerateResetPasswordTokenHandler.php @@ -34,6 +34,5 @@ public function __invoke(GenerateResetPasswordToken $generateResetPasswordToken) } $user->setPasswordResetToken($this->tokenGenerator->generate()); $user->setPasswordRequestedAt(new \DateTime()); - } } diff --git a/src/Handler/Customer/SendResetPasswordTokenHandler.php b/src/Handler/Customer/SendResetPasswordTokenHandler.php index 00ed4e521..ed7bbaf31 100644 --- a/src/Handler/Customer/SendResetPasswordTokenHandler.php +++ b/src/Handler/Customer/SendResetPasswordTokenHandler.php @@ -41,5 +41,4 @@ public function __invoke(SendResetPasswordToken $resendResetPasswordToken): void ['user' => $user, 'channelCode' => $resendResetPasswordToken->channelCode()] ); } - } From 2a79e434518e168609316103031120aacbbfe861 Mon Sep 17 00:00:00 2001 From: Amr Ezzat <3mr3zzat@gmail.com> Date: Sat, 18 Jan 2020 17:32:01 +0200 Subject: [PATCH 15/27] Add ValidationViewFactory last argument --- .../Customer/RequestPasswordResettingAction.php | 10 +++++++--- src/Resources/config/services/actions/customer.xml | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Controller/Customer/RequestPasswordResettingAction.php b/src/Controller/Customer/RequestPasswordResettingAction.php index e294820d5..5a7161a54 100644 --- a/src/Controller/Customer/RequestPasswordResettingAction.php +++ b/src/Controller/Customer/RequestPasswordResettingAction.php @@ -38,17 +38,21 @@ final class RequestPasswordResettingAction public function __construct( ViewHandlerInterface $viewHandler, MessageBusInterface $bus, - ValidationErrorViewFactoryInterface $validationErrorViewFactory, ChannelContextInterface $channelContext, CommandProviderInterface $generateResetPasswordTokenCommandProvider, - ChannelBasedCommandProviderInterface $sendResetPasswordTokenCommandProvider + ChannelBasedCommandProviderInterface $sendResetPasswordTokenCommandProvider, + ?ValidationErrorViewFactoryInterface $validationErrorViewFactory ) { + if (null !== $validationErrorViewFactory) { + @trigger_error('Passing ValidationErrorViewFactory as the fourth argument is deprecated', \E_USER_DEPRECATED); + } + $this->viewHandler = $viewHandler; $this->bus = $bus; - $this->validationErrorViewFactory = $validationErrorViewFactory; $this->channelContext = $channelContext; $this->generateResetPasswordTokenCommandProvider = $generateResetPasswordTokenCommandProvider; $this->sendResetPasswordTokenCommandProvider = $sendResetPasswordTokenCommandProvider; + $this->validationErrorViewFactory = $validationErrorViewFactory; } public function __invoke(Request $request): Response diff --git a/src/Resources/config/services/actions/customer.xml b/src/Resources/config/services/actions/customer.xml index 261c40ba6..dda8ed916 100644 --- a/src/Resources/config/services/actions/customer.xml +++ b/src/Resources/config/services/actions/customer.xml @@ -42,10 +42,10 @@ > - + Date: Sat, 18 Jan 2020 17:58:16 +0200 Subject: [PATCH 16/27] Make Handlers throw exceptions --- .../Customer/RequestPasswordResettingAction.php | 10 ++++++++-- .../Customer/GenerateResetPasswordTokenHandler.php | 3 ++- src/Handler/Customer/SendResetPasswordTokenHandler.php | 7 +++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Controller/Customer/RequestPasswordResettingAction.php b/src/Controller/Customer/RequestPasswordResettingAction.php index 5a7161a54..f9d56907a 100644 --- a/src/Controller/Customer/RequestPasswordResettingAction.php +++ b/src/Controller/Customer/RequestPasswordResettingAction.php @@ -13,6 +13,8 @@ use Sylius\ShopApiPlugin\Factory\ValidationErrorViewFactoryInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\Messenger\Exception\HandlerFailedException; use Symfony\Component\Messenger\MessageBusInterface; final class RequestPasswordResettingAction @@ -66,8 +68,12 @@ public function __invoke(Request $request): Response Response::HTTP_BAD_REQUEST )); } - $this->bus->dispatch($this->generateResetPasswordTokenCommandProvider->getCommand($request)); - $this->bus->dispatch($this->sendResetPasswordTokenCommandProvider->getCommand($request, $channel)); + + try { + $this->bus->dispatch($this->generateResetPasswordTokenCommandProvider->getCommand($request)); + $this->bus->dispatch($this->sendResetPasswordTokenCommandProvider->getCommand($request, $channel)); + } catch (HandlerFailedException $notFoundHttpException) { + } return $this->viewHandler->handle(View::create(null, Response::HTTP_NO_CONTENT)); } diff --git a/src/Handler/Customer/GenerateResetPasswordTokenHandler.php b/src/Handler/Customer/GenerateResetPasswordTokenHandler.php index 4cf29f834..601cc480d 100644 --- a/src/Handler/Customer/GenerateResetPasswordTokenHandler.php +++ b/src/Handler/Customer/GenerateResetPasswordTokenHandler.php @@ -8,6 +8,7 @@ use Sylius\Component\User\Repository\UserRepositoryInterface; use Sylius\Component\User\Security\Generator\GeneratorInterface; use Sylius\ShopApiPlugin\Command\Customer\GenerateResetPasswordToken; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class GenerateResetPasswordTokenHandler { @@ -30,7 +31,7 @@ public function __invoke(GenerateResetPasswordToken $generateResetPasswordToken) /** @var ShopUserInterface $user */ $user = $this->userRepository->findOneByEmail($email); if (null === $user) { - return; + throw new NotFoundHttpException('User with given email does not exist!'); } $user->setPasswordResetToken($this->tokenGenerator->generate()); $user->setPasswordRequestedAt(new \DateTime()); diff --git a/src/Handler/Customer/SendResetPasswordTokenHandler.php b/src/Handler/Customer/SendResetPasswordTokenHandler.php index ed7bbaf31..9dfea590c 100644 --- a/src/Handler/Customer/SendResetPasswordTokenHandler.php +++ b/src/Handler/Customer/SendResetPasswordTokenHandler.php @@ -9,6 +9,7 @@ use Sylius\Component\User\Repository\UserRepositoryInterface; use Sylius\ShopApiPlugin\Command\Customer\SendResetPasswordToken; use Sylius\ShopApiPlugin\Mailer\Emails; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Webmozart\Assert\Assert; final class SendResetPasswordTokenHandler @@ -32,9 +33,11 @@ public function __invoke(SendResetPasswordToken $resendResetPasswordToken): void /** @var ShopUserInterface $user */ $user = $this->userRepository->findOneByEmail($email); if (null === $user) { - return; + throw new NotFoundHttpException('User with given email does not exist!'); + } + if (null === $user->getPasswordResetToken()) { + throw new NotFoundHttpException(sprintf('User with %s email has not verification token defined.', $email)); } - Assert::notNull($user->getPasswordResetToken(), sprintf('User with %s email has not verification token defined.', $email)); $this->sender->send( Emails::EMAIL_RESET_PASSWORD_TOKEN, [$email], From d73537bc8a5cccc74c30e92cd506d0cbeddcbb68 Mon Sep 17 00:00:00 2001 From: Amr Ezzat <3mr3zzat@gmail.com> Date: Sat, 18 Jan 2020 18:18:49 +0200 Subject: [PATCH 17/27] Add Specs --- .../GenerateResetPasswordTokenHandlerSpec.php | 13 +++++-------- .../Customer/SendResetPasswordTokenHandlerSpec.php | 14 ++++++-------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php b/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php index a22ef9492..387a52640 100644 --- a/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php +++ b/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php @@ -11,6 +11,7 @@ use Sylius\Component\User\Security\Generator\GeneratorInterface; use Sylius\ShopApiPlugin\Command\Customer\GenerateResetPasswordToken; use Sylius\ShopApiPlugin\Handler\Customer\GenerateResetPasswordTokenHandler; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class GenerateResetPasswordTokenHandlerSpec extends ObjectBehavior { @@ -39,14 +40,10 @@ function it_handles_generating_user_verification_token( $this(new GenerateResetPasswordToken('example@customer.com')); } - function it_continues_if_user_not_found( - UserRepositoryInterface $userRepository, - GeneratorInterface $tokenGenerator, - ShopUserInterface $user + function it_throws_an_exception_if_user_has_not_been_found( + UserRepositoryInterface $userRepository ): void { - $userRepository->findOneByEmail('amr@amr.com')->willReturn(null); - $tokenGenerator->generate()->shouldNotBeCalled(); - $user->setPasswordResetToken('RANDOM_TOKEN')->shouldNotBeCalled(); - $user->setPasswordRequestedAt(Argument::type(\DateTime::class))->shouldNotBeCalled(); + $userRepository->findOneByEmail('example@customer.com')->willReturn(null); + $this->shouldThrow(NotFoundHttpException::class)->during('__invoke', [new GenerateResetPasswordToken('example@customer.com')]); } } diff --git a/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php b/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php index 953ee2daf..50e660d74 100644 --- a/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php +++ b/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php @@ -5,13 +5,13 @@ namespace spec\Sylius\ShopApiPlugin\Handler\Customer; use PhpSpec\ObjectBehavior; -use Prophecy\Argument; use Sylius\Component\Core\Model\ShopUserInterface; use Sylius\Component\Mailer\Sender\SenderInterface; use Sylius\Component\User\Repository\UserRepositoryInterface; use Sylius\ShopApiPlugin\Command\Customer\SendResetPasswordToken; use Sylius\ShopApiPlugin\Handler\Customer\SendResetPasswordTokenHandler; use Sylius\ShopApiPlugin\Mailer\Emails; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class SendResetPasswordTokenHandlerSpec extends ObjectBehavior { @@ -45,15 +45,13 @@ function it_throws_an_exception_if_user_has_not_verification_token( $userRepository->findOneByEmail('example@customer.com')->willReturn($user); $user->getPasswordResetToken()->willReturn(null); - $this->shouldThrow(\InvalidArgumentException::class)->during('__invoke', [new SendResetPasswordToken('example@customer.com', 'WEB_GB')]); + $this->shouldThrow(NotFoundHttpException::class)->during('__invoke', [new SendResetPasswordToken('example@customer.com', 'WEB_GB')]); } - function it_continues_if_user_not_found( - UserRepositoryInterface $userRepository, - SenderInterface $sender, - ShopUserInterface $user + function it_throws_an_exception_if_user_has_not_been_found( + UserRepositoryInterface $userRepository ): void { - $userRepository->findOneByEmail('amr@amr.com')->willReturn(null); - $sender->send(Argument::any())->shouldNotBeCalled(); + $userRepository->findOneByEmail('example@customer.com')->willReturn(null); + $this->shouldThrow(NotFoundHttpException::class)->during('__invoke', [new SendResetPasswordToken('example@customer.com', 'WEB_GB')]); } } From 5036738de410cdf5e0fa3bf51c06b01171340698 Mon Sep 17 00:00:00 2001 From: Amr Ezzat <3mr3zzat@gmail.com> Date: Sat, 18 Jan 2020 18:19:10 +0200 Subject: [PATCH 18/27] Remove Unused classes --- src/Controller/Customer/RequestPasswordResettingAction.php | 1 - src/Handler/Customer/SendResetPasswordTokenHandler.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Controller/Customer/RequestPasswordResettingAction.php b/src/Controller/Customer/RequestPasswordResettingAction.php index f9d56907a..d2c9d203b 100644 --- a/src/Controller/Customer/RequestPasswordResettingAction.php +++ b/src/Controller/Customer/RequestPasswordResettingAction.php @@ -13,7 +13,6 @@ use Sylius\ShopApiPlugin\Factory\ValidationErrorViewFactoryInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Messenger\Exception\HandlerFailedException; use Symfony\Component\Messenger\MessageBusInterface; diff --git a/src/Handler/Customer/SendResetPasswordTokenHandler.php b/src/Handler/Customer/SendResetPasswordTokenHandler.php index 9dfea590c..0f26946fd 100644 --- a/src/Handler/Customer/SendResetPasswordTokenHandler.php +++ b/src/Handler/Customer/SendResetPasswordTokenHandler.php @@ -10,7 +10,6 @@ use Sylius\ShopApiPlugin\Command\Customer\SendResetPasswordToken; use Sylius\ShopApiPlugin\Mailer\Emails; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Webmozart\Assert\Assert; final class SendResetPasswordTokenHandler { From 7fca470add2876a8aeef92df2a00ba3f8c5f212a Mon Sep 17 00:00:00 2001 From: Amr Ezzat <3mr3zzat@gmail.com> Date: Tue, 21 Jan 2020 22:00:55 +0200 Subject: [PATCH 19/27] Create UserNotFound Exception --- .../RequestPasswordResettingAction.php | 11 ++++++++-- src/Exception/UserNotFoundException.php | 20 +++++++++++++++++++ .../GenerateResetPasswordTokenHandler.php | 4 ++-- .../SendResetPasswordTokenHandler.php | 7 ++++--- 4 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 src/Exception/UserNotFoundException.php diff --git a/src/Controller/Customer/RequestPasswordResettingAction.php b/src/Controller/Customer/RequestPasswordResettingAction.php index d2c9d203b..e7de42f2d 100644 --- a/src/Controller/Customer/RequestPasswordResettingAction.php +++ b/src/Controller/Customer/RequestPasswordResettingAction.php @@ -10,6 +10,7 @@ use Sylius\Component\Core\Model\ChannelInterface; use Sylius\ShopApiPlugin\CommandProvider\ChannelBasedCommandProviderInterface; use Sylius\ShopApiPlugin\CommandProvider\CommandProviderInterface; +use Sylius\ShopApiPlugin\Exception\UserNotFoundException; use Sylius\ShopApiPlugin\Factory\ValidationErrorViewFactoryInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -43,7 +44,8 @@ public function __construct( CommandProviderInterface $generateResetPasswordTokenCommandProvider, ChannelBasedCommandProviderInterface $sendResetPasswordTokenCommandProvider, ?ValidationErrorViewFactoryInterface $validationErrorViewFactory - ) { + ) + { if (null !== $validationErrorViewFactory) { @trigger_error('Passing ValidationErrorViewFactory as the fourth argument is deprecated', \E_USER_DEPRECATED); } @@ -71,7 +73,12 @@ public function __invoke(Request $request): Response try { $this->bus->dispatch($this->generateResetPasswordTokenCommandProvider->getCommand($request)); $this->bus->dispatch($this->sendResetPasswordTokenCommandProvider->getCommand($request, $channel)); - } catch (HandlerFailedException $notFoundHttpException) { + } catch (HandlerFailedException $exception) { + $previousException = $exception->getPrevious(); + if ($previousException instanceof UserNotFoundException) { + return $this->viewHandler->handle(View::create(null, Response::HTTP_NO_CONTENT)); + } + throw $exception; } return $this->viewHandler->handle(View::create(null, Response::HTTP_NO_CONTENT)); diff --git a/src/Exception/UserNotFoundException.php b/src/Exception/UserNotFoundException.php new file mode 100644 index 000000000..ff057bb54 --- /dev/null +++ b/src/Exception/UserNotFoundException.php @@ -0,0 +1,20 @@ +userRepository->findOneByEmail($email); if (null === $user) { - throw new NotFoundHttpException('User with given email does not exist!'); + throw UserNotFoundException::withEmail($email); } $user->setPasswordResetToken($this->tokenGenerator->generate()); $user->setPasswordRequestedAt(new \DateTime()); diff --git a/src/Handler/Customer/SendResetPasswordTokenHandler.php b/src/Handler/Customer/SendResetPasswordTokenHandler.php index 0f26946fd..b7215031e 100644 --- a/src/Handler/Customer/SendResetPasswordTokenHandler.php +++ b/src/Handler/Customer/SendResetPasswordTokenHandler.php @@ -8,8 +8,9 @@ use Sylius\Component\Mailer\Sender\SenderInterface; use Sylius\Component\User\Repository\UserRepositoryInterface; use Sylius\ShopApiPlugin\Command\Customer\SendResetPasswordToken; +use Sylius\ShopApiPlugin\Exception\UserNotFoundException; use Sylius\ShopApiPlugin\Mailer\Emails; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use InvalidArgumentException; final class SendResetPasswordTokenHandler { @@ -32,10 +33,10 @@ public function __invoke(SendResetPasswordToken $resendResetPasswordToken): void /** @var ShopUserInterface $user */ $user = $this->userRepository->findOneByEmail($email); if (null === $user) { - throw new NotFoundHttpException('User with given email does not exist!'); + throw UserNotFoundException::withEmail($email); } if (null === $user->getPasswordResetToken()) { - throw new NotFoundHttpException(sprintf('User with %s email has not verification token defined.', $email)); + throw new InvalidArgumentException(sprintf('User with %s email has not verification token defined.', $email)); } $this->sender->send( Emails::EMAIL_RESET_PASSWORD_TOKEN, From f4abe0775181f69417a43da927d81f69b9d1f803 Mon Sep 17 00:00:00 2001 From: Amr Ezzat <31932378+Amr3zzat@users.noreply.github.com> Date: Wed, 22 Jan 2020 23:16:04 +0200 Subject: [PATCH 20/27] Update src/Controller/Customer/RequestPasswordResettingAction.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Łukasz Chruściel --- src/Controller/Customer/RequestPasswordResettingAction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controller/Customer/RequestPasswordResettingAction.php b/src/Controller/Customer/RequestPasswordResettingAction.php index e7de42f2d..6dae2df27 100644 --- a/src/Controller/Customer/RequestPasswordResettingAction.php +++ b/src/Controller/Customer/RequestPasswordResettingAction.php @@ -44,7 +44,7 @@ public function __construct( CommandProviderInterface $generateResetPasswordTokenCommandProvider, ChannelBasedCommandProviderInterface $sendResetPasswordTokenCommandProvider, ?ValidationErrorViewFactoryInterface $validationErrorViewFactory - ) + ) { { if (null !== $validationErrorViewFactory) { @trigger_error('Passing ValidationErrorViewFactory as the fourth argument is deprecated', \E_USER_DEPRECATED); From a35aec7a00c98b267320ae4ed131829d250f33df Mon Sep 17 00:00:00 2001 From: Amr Ezzat <31932378+Amr3zzat@users.noreply.github.com> Date: Wed, 22 Jan 2020 23:16:42 +0200 Subject: [PATCH 21/27] Update src/Controller/Customer/RequestPasswordResettingAction.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Łukasz Chruściel --- src/Controller/Customer/RequestPasswordResettingAction.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Controller/Customer/RequestPasswordResettingAction.php b/src/Controller/Customer/RequestPasswordResettingAction.php index 6dae2df27..af1a0e352 100644 --- a/src/Controller/Customer/RequestPasswordResettingAction.php +++ b/src/Controller/Customer/RequestPasswordResettingAction.php @@ -45,7 +45,6 @@ public function __construct( ChannelBasedCommandProviderInterface $sendResetPasswordTokenCommandProvider, ?ValidationErrorViewFactoryInterface $validationErrorViewFactory ) { - { if (null !== $validationErrorViewFactory) { @trigger_error('Passing ValidationErrorViewFactory as the fourth argument is deprecated', \E_USER_DEPRECATED); } From 97e8ed2c870154a37905c99eb93d7b1bcd2d0b25 Mon Sep 17 00:00:00 2001 From: Amr Ezzat <31932378+Amr3zzat@users.noreply.github.com> Date: Wed, 22 Jan 2020 23:17:03 +0200 Subject: [PATCH 22/27] Update src/Exception/UserNotFoundException.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Łukasz Chruściel --- src/Exception/UserNotFoundException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Exception/UserNotFoundException.php b/src/Exception/UserNotFoundException.php index ff057bb54..df953bcee 100644 --- a/src/Exception/UserNotFoundException.php +++ b/src/Exception/UserNotFoundException.php @@ -5,7 +5,7 @@ namespace Sylius\ShopApiPlugin\Exception; -class UserNotFoundException extends \InvalidArgumentException +final class UserNotFoundException extends \InvalidArgumentException { public static function occur(): self { From bd3ba0092f16e6915dacdb8fe9a171b761d54a5c Mon Sep 17 00:00:00 2001 From: Amr Ezzat <31932378+Amr3zzat@users.noreply.github.com> Date: Wed, 22 Jan 2020 23:17:17 +0200 Subject: [PATCH 23/27] Update src/Exception/UserNotFoundException.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Łukasz Chruściel --- src/Exception/UserNotFoundException.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Exception/UserNotFoundException.php b/src/Exception/UserNotFoundException.php index df953bcee..c90d01545 100644 --- a/src/Exception/UserNotFoundException.php +++ b/src/Exception/UserNotFoundException.php @@ -16,5 +16,4 @@ public static function withEmail(string $email): self { return new self(sprintf('User with email %s has not been found.', $email)); } - } From 0ed1096e5a74de173413f77570914e6cf254598d Mon Sep 17 00:00:00 2001 From: Amr Ezzat <31932378+Amr3zzat@users.noreply.github.com> Date: Wed, 22 Jan 2020 23:17:30 +0200 Subject: [PATCH 24/27] Update src/Exception/UserNotFoundException.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Łukasz Chruściel --- src/Exception/UserNotFoundException.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Exception/UserNotFoundException.php b/src/Exception/UserNotFoundException.php index c90d01545..4dae4fb60 100644 --- a/src/Exception/UserNotFoundException.php +++ b/src/Exception/UserNotFoundException.php @@ -4,7 +4,6 @@ namespace Sylius\ShopApiPlugin\Exception; - final class UserNotFoundException extends \InvalidArgumentException { public static function occur(): self From cd178269c404a8e45a983fdb7ad218f0943ec611 Mon Sep 17 00:00:00 2001 From: Amr Ezzat <3mr3zzat@gmail.com> Date: Wed, 22 Jan 2020 23:30:46 +0200 Subject: [PATCH 25/27] Update Specs --- .../Customer/GenerateResetPasswordTokenHandlerSpec.php | 4 ++-- .../Handler/Customer/SendResetPasswordTokenHandlerSpec.php | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php b/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php index 387a52640..504dd6ebf 100644 --- a/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php +++ b/spec/Handler/Customer/GenerateResetPasswordTokenHandlerSpec.php @@ -10,8 +10,8 @@ use Sylius\Component\User\Repository\UserRepositoryInterface; use Sylius\Component\User\Security\Generator\GeneratorInterface; use Sylius\ShopApiPlugin\Command\Customer\GenerateResetPasswordToken; +use Sylius\ShopApiPlugin\Exception\UserNotFoundException; use Sylius\ShopApiPlugin\Handler\Customer\GenerateResetPasswordTokenHandler; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; final class GenerateResetPasswordTokenHandlerSpec extends ObjectBehavior { @@ -44,6 +44,6 @@ function it_throws_an_exception_if_user_has_not_been_found( UserRepositoryInterface $userRepository ): void { $userRepository->findOneByEmail('example@customer.com')->willReturn(null); - $this->shouldThrow(NotFoundHttpException::class)->during('__invoke', [new GenerateResetPasswordToken('example@customer.com')]); + $this->shouldThrow(UserNotFoundException::class)->during('__invoke', [new GenerateResetPasswordToken('example@customer.com')]); } } diff --git a/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php b/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php index 50e660d74..68e0055f9 100644 --- a/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php +++ b/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php @@ -9,9 +9,10 @@ use Sylius\Component\Mailer\Sender\SenderInterface; use Sylius\Component\User\Repository\UserRepositoryInterface; use Sylius\ShopApiPlugin\Command\Customer\SendResetPasswordToken; +use Sylius\ShopApiPlugin\Exception\UserNotFoundException; use Sylius\ShopApiPlugin\Handler\Customer\SendResetPasswordTokenHandler; use Sylius\ShopApiPlugin\Mailer\Emails; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use InvalidArgumentException; final class SendResetPasswordTokenHandlerSpec extends ObjectBehavior { @@ -45,13 +46,13 @@ function it_throws_an_exception_if_user_has_not_verification_token( $userRepository->findOneByEmail('example@customer.com')->willReturn($user); $user->getPasswordResetToken()->willReturn(null); - $this->shouldThrow(NotFoundHttpException::class)->during('__invoke', [new SendResetPasswordToken('example@customer.com', 'WEB_GB')]); + $this->shouldThrow(InvalidArgumentException::class)->during('__invoke', [new SendResetPasswordToken('example@customer.com', 'WEB_GB')]); } function it_throws_an_exception_if_user_has_not_been_found( UserRepositoryInterface $userRepository ): void { $userRepository->findOneByEmail('example@customer.com')->willReturn(null); - $this->shouldThrow(NotFoundHttpException::class)->during('__invoke', [new SendResetPasswordToken('example@customer.com', 'WEB_GB')]); + $this->shouldThrow(UserNotFoundException::class)->during('__invoke', [new SendResetPasswordToken('example@customer.com', 'WEB_GB')]); } } From 1c09aa6a0bfb0ef96a029fbc80b01c4a2b6f3887 Mon Sep 17 00:00:00 2001 From: Amr Ezzat <3mr3zzat@gmail.com> Date: Wed, 22 Jan 2020 23:32:52 +0200 Subject: [PATCH 26/27] Code Style --- spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php | 2 +- src/Controller/Customer/RequestPasswordResettingAction.php | 1 + src/Handler/Customer/SendResetPasswordTokenHandler.php | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php b/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php index 68e0055f9..7f82e57cf 100644 --- a/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php +++ b/spec/Handler/Customer/SendResetPasswordTokenHandlerSpec.php @@ -4,6 +4,7 @@ namespace spec\Sylius\ShopApiPlugin\Handler\Customer; +use InvalidArgumentException; use PhpSpec\ObjectBehavior; use Sylius\Component\Core\Model\ShopUserInterface; use Sylius\Component\Mailer\Sender\SenderInterface; @@ -12,7 +13,6 @@ use Sylius\ShopApiPlugin\Exception\UserNotFoundException; use Sylius\ShopApiPlugin\Handler\Customer\SendResetPasswordTokenHandler; use Sylius\ShopApiPlugin\Mailer\Emails; -use InvalidArgumentException; final class SendResetPasswordTokenHandlerSpec extends ObjectBehavior { diff --git a/src/Controller/Customer/RequestPasswordResettingAction.php b/src/Controller/Customer/RequestPasswordResettingAction.php index af1a0e352..f79b845d2 100644 --- a/src/Controller/Customer/RequestPasswordResettingAction.php +++ b/src/Controller/Customer/RequestPasswordResettingAction.php @@ -77,6 +77,7 @@ public function __invoke(Request $request): Response if ($previousException instanceof UserNotFoundException) { return $this->viewHandler->handle(View::create(null, Response::HTTP_NO_CONTENT)); } + throw $exception; } diff --git a/src/Handler/Customer/SendResetPasswordTokenHandler.php b/src/Handler/Customer/SendResetPasswordTokenHandler.php index b7215031e..fad32be9d 100644 --- a/src/Handler/Customer/SendResetPasswordTokenHandler.php +++ b/src/Handler/Customer/SendResetPasswordTokenHandler.php @@ -4,13 +4,13 @@ namespace Sylius\ShopApiPlugin\Handler\Customer; +use InvalidArgumentException; use Sylius\Component\Core\Model\ShopUserInterface; use Sylius\Component\Mailer\Sender\SenderInterface; use Sylius\Component\User\Repository\UserRepositoryInterface; use Sylius\ShopApiPlugin\Command\Customer\SendResetPasswordToken; use Sylius\ShopApiPlugin\Exception\UserNotFoundException; use Sylius\ShopApiPlugin\Mailer\Emails; -use InvalidArgumentException; final class SendResetPasswordTokenHandler { From 5ac484e6dd95eee4a2dc3530019dded7350e7491 Mon Sep 17 00:00:00 2001 From: Amr Ezzat <3mr3zzat@gmail.com> Date: Thu, 30 Jan 2020 00:26:27 +0200 Subject: [PATCH 27/27] Fix tests --- src/Handler/Customer/GenerateResetPasswordTokenHandler.php | 2 +- src/Handler/Customer/SendResetPasswordTokenHandler.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Handler/Customer/GenerateResetPasswordTokenHandler.php b/src/Handler/Customer/GenerateResetPasswordTokenHandler.php index 507371947..1f40afe01 100644 --- a/src/Handler/Customer/GenerateResetPasswordTokenHandler.php +++ b/src/Handler/Customer/GenerateResetPasswordTokenHandler.php @@ -28,7 +28,7 @@ public function __invoke(GenerateResetPasswordToken $generateResetPasswordToken) { $email = $generateResetPasswordToken->email(); - /** @var ShopUserInterface $user */ + /** @var ShopUserInterface|null $user */ $user = $this->userRepository->findOneByEmail($email); if (null === $user) { throw UserNotFoundException::withEmail($email); diff --git a/src/Handler/Customer/SendResetPasswordTokenHandler.php b/src/Handler/Customer/SendResetPasswordTokenHandler.php index fad32be9d..c9b268ac2 100644 --- a/src/Handler/Customer/SendResetPasswordTokenHandler.php +++ b/src/Handler/Customer/SendResetPasswordTokenHandler.php @@ -30,7 +30,7 @@ public function __invoke(SendResetPasswordToken $resendResetPasswordToken): void { $email = $resendResetPasswordToken->email(); - /** @var ShopUserInterface $user */ + /** @var ShopUserInterface|null $user */ $user = $this->userRepository->findOneByEmail($email); if (null === $user) { throw UserNotFoundException::withEmail($email);