vendor/ibexa/user/src/bundle/Controller/PasswordResetController.php line 68

Open in your IDE?
  1. <?php
  2. /**
  3. * @copyright Copyright (C) Ibexa AS. All rights reserved.
  4. * @license For full copyright and license information view LICENSE file distributed with this source code.
  5. */
  6. declare(strict_types=1);
  7. namespace Ibexa\Bundle\User\Controller;
  8. use DateInterval;
  9. use DateTime;
  10. use Ibexa\Bundle\User\Type\UserForgotPasswordReason;
  11. use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException;
  12. use Ibexa\Contracts\Core\Repository\PermissionResolver;
  13. use Ibexa\Contracts\Core\Repository\UserService;
  14. use Ibexa\Contracts\Core\Repository\Values\User\User;
  15. use Ibexa\Contracts\Core\Repository\Values\User\UserTokenUpdateStruct;
  16. use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
  17. use Ibexa\Contracts\User\PasswordReset\NotifierInterface;
  18. use Ibexa\User\ExceptionHandler\ActionResultHandler;
  19. use Ibexa\User\Form\Data\UserPasswordResetData;
  20. use Ibexa\User\Form\Factory\FormFactory;
  21. use Ibexa\User\View\ForgotPassword\FormView;
  22. use Ibexa\User\View\ForgotPassword\LoginView;
  23. use Ibexa\User\View\ForgotPassword\SuccessView;
  24. use Ibexa\User\View\ResetPassword\FormView as UserResetPasswordFormView;
  25. use Ibexa\User\View\ResetPassword\InvalidLinkView;
  26. use Ibexa\User\View\ResetPassword\SuccessView as UserResetPasswordSuccessView;
  27. use Symfony\Component\HttpFoundation\Request;
  28. use Symfony\Component\HttpFoundation\Response;
  29. class PasswordResetController extends Controller
  30. {
  31. private FormFactory $formFactory;
  32. private UserService $userService;
  33. private ActionResultHandler $actionResultHandler;
  34. private PermissionResolver $permissionResolver;
  35. private ConfigResolverInterface $configResolver;
  36. private NotifierInterface $passwordResetMailer;
  37. public function __construct(
  38. FormFactory $formFactory,
  39. UserService $userService,
  40. ActionResultHandler $actionResultHandler,
  41. PermissionResolver $permissionResolver,
  42. ConfigResolverInterface $configResolver,
  43. NotifierInterface $passwordResetMailer
  44. ) {
  45. $this->formFactory = $formFactory;
  46. $this->userService = $userService;
  47. $this->actionResultHandler = $actionResultHandler;
  48. $this->permissionResolver = $permissionResolver;
  49. $this->configResolver = $configResolver;
  50. $this->passwordResetMailer = $passwordResetMailer;
  51. }
  52. /**
  53. * @return \Ibexa\User\View\ForgotPassword\FormView|\Ibexa\User\View\ForgotPassword\SuccessView|\Symfony\Component\HttpFoundation\RedirectResponse
  54. *
  55. * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType
  56. */
  57. public function userForgotPasswordAction(Request $request, ?string $reason = null)
  58. {
  59. $form = $this->formFactory->forgotUserPassword();
  60. $form->handleRequest($request);
  61. if ($form->isSubmitted() && $form->isValid()) {
  62. $data = $form->getData();
  63. $users = $this->userService->loadUsersByEmail($data->getEmail());
  64. /** Because it is possible to have multiple user accounts with same email address we must gain a user login. */
  65. if (\count($users) > 1) {
  66. return $this->redirectToRoute('ibexa.user.forgot_password.login');
  67. }
  68. if (!empty($users)) {
  69. /** @var \Ibexa\Contracts\Core\Repository\Values\User\User $user */
  70. $user = reset($users);
  71. $token = $this->updateUserToken($user);
  72. $this->passwordResetMailer->sendMessage($user, $token);
  73. }
  74. return new SuccessView(null);
  75. }
  76. return new FormView(null, [
  77. 'form_forgot_user_password' => $form->createView(),
  78. 'reason' => $reason,
  79. 'userForgotPasswordReasonMigration' => UserForgotPasswordReason::MIGRATION,
  80. ]);
  81. }
  82. /**
  83. * @param \Symfony\Component\HttpFoundation\Request $request
  84. *
  85. * @return \Ibexa\User\View\ForgotPassword\LoginView|\Ibexa\User\View\ForgotPassword\SuccessView
  86. *
  87. * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType
  88. */
  89. public function userForgotPasswordLoginAction(Request $request)
  90. {
  91. $form = $this->formFactory->forgotUserPasswordWithLogin();
  92. $form->handleRequest($request);
  93. if ($form->isSubmitted() && $form->isValid()) {
  94. $data = $form->getData();
  95. try {
  96. $user = $this->userService->loadUserByLogin($data->getLogin());
  97. } catch (NotFoundException $e) {
  98. $user = null;
  99. }
  100. if (!$user || \count($this->userService->loadUsersByEmail($user->email)) < 2) {
  101. return new SuccessView(null);
  102. }
  103. $token = $this->updateUserToken($user);
  104. $this->passwordResetMailer->sendMessage($user, $token);
  105. return new SuccessView(null);
  106. }
  107. return new LoginView(null, [
  108. 'form_forgot_user_password_with_login' => $form->createView(),
  109. ]);
  110. }
  111. /**
  112. * @param \Symfony\Component\HttpFoundation\Request $request
  113. * @param string $hashKey
  114. *
  115. * @return \Ibexa\User\View\ResetPassword\FormView|\Ibexa\User\View\ResetPassword\InvalidLinkView|\Ibexa\User\View\ResetPassword\SuccessView
  116. *
  117. * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType
  118. */
  119. public function userResetPasswordAction(Request $request, string $hashKey)
  120. {
  121. $response = new Response();
  122. $response->headers->set('X-Robots-Tag', 'noindex');
  123. try {
  124. $user = $this->userService->loadUserByToken($hashKey);
  125. } catch (NotFoundException $e) {
  126. $view = new InvalidLinkView(null);
  127. $view->setResponse($response);
  128. return $view;
  129. }
  130. $userPasswordResetData = new UserPasswordResetData();
  131. $form = $this->formFactory->resetUserPassword(
  132. $userPasswordResetData,
  133. null,
  134. $user->getContentType(),
  135. $user
  136. );
  137. $form->handleRequest($request);
  138. if ($form->isSubmitted() && $form->isValid()) {
  139. try {
  140. $currentUser = $this->permissionResolver->getCurrentUserReference();
  141. $this->permissionResolver->setCurrentUserReference($user);
  142. } catch (NotFoundException $e) {
  143. $view = new InvalidLinkView(null);
  144. $view->setResponse($response);
  145. return $view;
  146. }
  147. $data = $form->getData();
  148. try {
  149. $this->userService->updateUserPassword($user, $data->getNewPassword());
  150. $this->userService->expireUserToken($hashKey);
  151. $this->permissionResolver->setCurrentUserReference($currentUser);
  152. $view = new UserResetPasswordSuccessView(null);
  153. $view->setResponse($response);
  154. return $view;
  155. } catch (\Exception $e) {
  156. $this->actionResultHandler->error($e->getMessage());
  157. }
  158. }
  159. $view = new UserResetPasswordFormView(null, [
  160. 'form_reset_user_password' => $form->createView(),
  161. ]);
  162. $view->setResponse($response);
  163. return $view;
  164. }
  165. /**
  166. * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user
  167. *
  168. * @return string
  169. *
  170. * @throws \Exception
  171. */
  172. private function updateUserToken(User $user): string
  173. {
  174. $struct = new UserTokenUpdateStruct();
  175. $struct->hashKey = bin2hex(random_bytes(16));
  176. $date = new DateTime();
  177. $date->add(new DateInterval($this->configResolver->getParameter('security.token_interval_spec')));
  178. $struct->time = $date;
  179. $this->userService->updateUserToken($user, $struct);
  180. return $struct->hashKey;
  181. }
  182. }
  183. class_alias(PasswordResetController::class, 'EzSystems\EzPlatformUserBundle\Controller\PasswordResetController');