From 92fe172c9b3bbc48bfd75b57ddcbc411241f9ba8 Mon Sep 17 00:00:00 2001 From: Ilya Vasilenko Date: Thu, 20 Jun 2024 10:58:39 +0500 Subject: [PATCH] password reset --- app/config/packages/security.yaml | 4 + app/config/services.yaml | 12 +++ app/src/Controller/AuthController.php | 30 ++++++++ .../Service/Action/Classes/ResetPassword.php | 63 ++++++++++++++++ .../Action/Classes/ResetPasswordCode.php | 73 +++++++++++++++++++ .../Action/Classes/SendRegisterCode.php | 1 - .../Action/Classes/SendResetPasswordCode.php | 46 ++++++++++++ app/src/Service/Dto/BaseDto.php | 6 +- .../Dto/Classes/ResetPasswordCodeDto.php | 44 +++++++++++ .../Classes/Code/PasswordCodeSendService.php | 23 ++++++ 10 files changed, 297 insertions(+), 5 deletions(-) create mode 100644 app/src/Service/Action/Classes/ResetPassword.php create mode 100644 app/src/Service/Action/Classes/ResetPasswordCode.php create mode 100644 app/src/Service/Action/Classes/SendResetPasswordCode.php create mode 100644 app/src/Service/Dto/Classes/ResetPasswordCodeDto.php create mode 100644 app/src/Service/Send/Classes/Code/PasswordCodeSendService.php diff --git a/app/config/packages/security.yaml b/app/config/packages/security.yaml index e1644bc..198b9ae 100644 --- a/app/config/packages/security.yaml +++ b/app/config/packages/security.yaml @@ -49,6 +49,10 @@ security: - { path: ^/api/register/send, roles: ROLE_USER } - { path: ^/api/register/check, roles: ROLE_USER } + - { path: ^/api/password/reset, roles: ROLE_USER } + - { path: ^/api/password/send, roles: PUBLIC_ACCESS } + - { path: ^/api/password/reset/check, roles: PUBLIC_ACCESS } + - { path: ^/api/profile/recovery, roles: PUBLIC_ACCESS } - { path: ^/api/profile/recovery/check, roles: PUBLIC_ACCESS } - { path: ^/api, roles: ROLE_CONFIRMED } diff --git a/app/config/services.yaml b/app/config/services.yaml index 0a7f6ed..4fe458b 100644 --- a/app/config/services.yaml +++ b/app/config/services.yaml @@ -38,6 +38,12 @@ services: App\Service\Action\ActionServiceInterface $sendRegisterService: '@App\Service\Action\Classes\SendRegisterCode' + App\Service\Action\ActionServiceInterface $sendPasswordCodeService: '@App\Service\Action\Classes\SendResetPasswordCode' + + App\Service\Action\ActionServiceInterface $resetPasswordCodeService: '@App\Service\Action\Classes\ResetPasswordCode' + + App\Service\Action\ActionServiceInterface $resetPasswordService: '@App\Service\Action\Classes\ResetPassword' + App\Service\Action\ActionServiceInterface: '@App\Service\Action\Classes\None' @@ -48,6 +54,10 @@ services: App\Service\Dto\DtoServiceInterface $recoveryCodeDto: '@App\Service\Dto\Classes\RecoveryCodeDto' + App\Service\Dto\DtoServiceInterface $passwordResetDto: '@App\Service\Dto\Classes\ResetPasswordCodeDto' + + App\Service\Dto\DtoServiceInterface $passwordDto: '@App\Service\Dto\Classes\ChangePasswordDto' + App\Service\Dto\DtoServiceInterface $recoveryDto: '@App\Service\Dto\Classes\RecoveryDto' App\Service\Dto\DtoServiceInterface: '@App\Service\Dto\Classes\NoneDto' @@ -71,6 +81,8 @@ services: App\Service\Send\SendServiceInterface $recoveryCodeSendService: '@App\Service\Send\Classes\Code\RecoveryCodeSendService' + App\Service\Send\SendServiceInterface $passwordCodeSendService: '@App\Service\Send\Classes\Code\PasswordCodeSendService' + # События JWT авторизации acme_api.event.authentication_success_listener: class: App\Listeners\JwtListener diff --git a/app/src/Controller/AuthController.php b/app/src/Controller/AuthController.php index 72e3782..9135e2a 100644 --- a/app/src/Controller/AuthController.php +++ b/app/src/Controller/AuthController.php @@ -3,8 +3,11 @@ namespace App\Controller; use App\Service\Action\ActionServiceInterface; +use App\Service\Dto\Classes\ChangePasswordDto; +use App\Service\Dto\Classes\RecoveryDto; use App\Service\Dto\Classes\RegisterCodeDto; use App\Service\Dto\Classes\RegisterDto; +use App\Service\Dto\Classes\ResetPasswordCodeDto; use App\Service\Response\Classes\Response; use Nelmio\ApiDocBundle\Annotation\Model; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -52,4 +55,31 @@ class AuthController extends AbstractController { return $checkRegisterService->getResponse(); } + + #[Route('/password/reset', name: 'password_reset', methods: ['POST'])] + #[OA\RequestBody( + content: new OA\JsonContent(ref: new Model(type: ChangePasswordDto::class)) + )] + public function resetPassword(ActionServiceInterface $resetPasswordService): JsonResponse + { + return $resetPasswordService->getResponse(); + } + + #[Route('/password/send', name: 'password_send', methods: ['POST'])] + #[OA\RequestBody( + content: new OA\JsonContent(ref: new Model(type: RecoveryDto::class)) + )] + public function sendResetPassword(ActionServiceInterface $sendPasswordCodeService): JsonResponse + { + return $sendPasswordCodeService->getResponse(); + } + + #[Route('/password/reset/check', name: 'password_reset_check', methods: ['POST'])] + #[OA\RequestBody( + content: new OA\JsonContent(ref: new Model(type: ResetPasswordCodeDto::class)) + )] + public function resetCheckPassword(ActionServiceInterface $resetPasswordCodeService): JsonResponse + { + return $resetPasswordCodeService->getResponse(); + } } diff --git a/app/src/Service/Action/Classes/ResetPassword.php b/app/src/Service/Action/Classes/ResetPassword.php new file mode 100644 index 0000000..ec611ab --- /dev/null +++ b/app/src/Service/Action/Classes/ResetPassword.php @@ -0,0 +1,63 @@ +user = $security->getUser(); + parent::__construct($response); + } + + public function runAction(): void + { + /** @var ChangePasswordDto $dto */ + $dto = $this->passwordDto->getClass(); + + if ($this->passwordHasher->isPasswordValid($this->user, $dto->oldPassword)) { + $hashedPassword = $this->passwordHasher->hashPassword( + $this->user, + $dto->password ?: '' + ); + $this->user->setPassword($hashedPassword); + + try { + $em = $this->doctrine->getManager(); + $em->persist($this->user); + $em->flush(); + $this->response->addMessage('Пароль изменен'); + } catch (\Exception $exception) { + $this->response->addError('Ошибка изменения пароля'); + } + } else { + $this->response->addError('Текущий пароль неверен'); + } + } + + public function validate(): bool + { + if ($this->user === null) { + $this->response->addError('Вы не авторизованы'); + return false; + } + return $this->passwordDto->validate($this->response); + } +} \ No newline at end of file diff --git a/app/src/Service/Action/Classes/ResetPasswordCode.php b/app/src/Service/Action/Classes/ResetPasswordCode.php new file mode 100644 index 0000000..bc5ecd4 --- /dev/null +++ b/app/src/Service/Action/Classes/ResetPasswordCode.php @@ -0,0 +1,73 @@ +passwordResetDto->getClass(); + /** @var User $userExists */ + $userExists = $this->doctrine->getRepository(User::class) + ->findOneByUniq($dto->email, $dto->phoneNumber); + + if ($userExists !== null) { + $currentDate = new \DateTime(); + $code = $dto->code; + $registerCode = $userExists->getRegisterCode(); + if ($registerCode === null) { + $this->response->addError('Код подтверждения не отправлен'); + } else { + if ($registerCodeDate = $registerCode->getDate()) { + if ($registerCode->getCode() === $code && $currentDate->getTimestamp() < $registerCodeDate->getTimestamp()) { + try { + $hashedPassword = $this->passwordHasher->hashPassword( + $userExists, + $dto->password ?: '' + ); + $userExists->setPassword($hashedPassword); + $em = $this->doctrine->getManager(); + $em->persist($userExists); + $em->remove($registerCode); + $em->flush(); + $this->response->addMessage('Пароль изменен'); + } catch (\Exception $exception) { + $this->response->addError('Ошибка изменения пароля'); + } + } else { + $this->response->addError('Код недействителен'); + } + } else { + $this->response->addError('Код недействителен'); + } + } + } else { + $this->response->addError('Пользователь не найден'); + } + } + + public function validate(): bool + { + return $this->passwordResetDto->validate($this->response); + } +} \ No newline at end of file diff --git a/app/src/Service/Action/Classes/SendRegisterCode.php b/app/src/Service/Action/Classes/SendRegisterCode.php index 27666d3..8796d95 100644 --- a/app/src/Service/Action/Classes/SendRegisterCode.php +++ b/app/src/Service/Action/Classes/SendRegisterCode.php @@ -6,7 +6,6 @@ use App\Entity\User; use App\Service\Action\BaseActionService; use App\Service\Response\ResponseServiceInterface; use App\Service\Send\Classes\Code\RegisterCodeSendService; -use App\Service\Send\Classes\CodeSendService; use App\Service\Send\SendServiceInterface; use Symfony\Bundle\SecurityBundle\Security; diff --git a/app/src/Service/Action/Classes/SendResetPasswordCode.php b/app/src/Service/Action/Classes/SendResetPasswordCode.php new file mode 100644 index 0000000..7426d9b --- /dev/null +++ b/app/src/Service/Action/Classes/SendResetPasswordCode.php @@ -0,0 +1,46 @@ +recoveryDto->getClass(); + /** @var User $userExists */ + $userExists = $this->doctrine->getRepository(User::class) + ->findOneByUniq($dto->email, $dto->phoneNumber); + + if ($userExists !== null) { + $this->passwordCodeSendService->setUser($userExists); + $this->passwordCodeSendService->setResponse($this->response); + $this->passwordCodeSendService->send(); + } else { + $this->response->addError('Пользователь не найден'); + } + } + + public function validate(): bool + { + return $this->recoveryDto->validate($this->response); + } +} \ No newline at end of file diff --git a/app/src/Service/Dto/BaseDto.php b/app/src/Service/Dto/BaseDto.php index 47a3786..0a149d4 100644 --- a/app/src/Service/Dto/BaseDto.php +++ b/app/src/Service/Dto/BaseDto.php @@ -84,19 +84,17 @@ abstract class BaseDto implements DtoServiceInterface */ public function validate(ResponseServiceInterface $response): bool { - $apiResponse = $response->getResponse(); - $bValid = true; if ($classObj = $this->getClass()) { $aErrors = $this->validator->validate($classObj); if (count($aErrors) > 0) { foreach ($aErrors as $error) { - $apiResponse->addError($error->getMessage()); + $response->addError($error->getMessage()); } $bValid = false; } } else { - $apiResponse->addError("Данные не получены"); + $response->addError("Данные не получены"); $bValid = false; } diff --git a/app/src/Service/Dto/Classes/ResetPasswordCodeDto.php b/app/src/Service/Dto/Classes/ResetPasswordCodeDto.php new file mode 100644 index 0000000..302631a --- /dev/null +++ b/app/src/Service/Dto/Classes/ResetPasswordCodeDto.php @@ -0,0 +1,44 @@ +Уважаемый {surname} {name} {patronymic} +
Ваш код для восстановления пароля: {code}
+
Время действия кода: {time}
+ HTML; + } + +} \ No newline at end of file -- GitLab