vendor/shopware/storefront/Controller/AuthController.php line 81

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Storefront\Controller;
  3. use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
  4. use Shopware\Core\Checkout\Customer\Exception\BadCredentialsException;
  5. use Shopware\Core\Checkout\Customer\Exception\CustomerAuthThrottledException;
  6. use Shopware\Core\Checkout\Customer\Exception\CustomerNotFoundByHashException;
  7. use Shopware\Core\Checkout\Customer\Exception\CustomerNotFoundException;
  8. use Shopware\Core\Checkout\Customer\Exception\CustomerRecoveryHashExpiredException;
  9. use Shopware\Core\Checkout\Customer\Exception\InactiveCustomerException;
  10. use Shopware\Core\Checkout\Customer\SalesChannel\AbstractLoginRoute;
  11. use Shopware\Core\Checkout\Customer\SalesChannel\AbstractLogoutRoute;
  12. use Shopware\Core\Checkout\Customer\SalesChannel\AbstractResetPasswordRoute;
  13. use Shopware\Core\Checkout\Customer\SalesChannel\AbstractSendPasswordRecoveryMailRoute;
  14. use Shopware\Core\Content\Category\Exception\CategoryNotFoundException;
  15. use Shopware\Core\Framework\Context;
  16. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  17. use Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException;
  18. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  19. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  20. use Shopware\Core\Framework\RateLimiter\Exception\RateLimitExceededException;
  21. use Shopware\Core\Framework\Routing\Annotation\RouteScope;
  22. use Shopware\Core\Framework\Routing\Annotation\Since;
  23. use Shopware\Core\Framework\Routing\Exception\MissingRequestParameterException;
  24. use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
  25. use Shopware\Core\Framework\Validation\Exception\ConstraintViolationException;
  26. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  27. use Shopware\Storefront\Framework\Routing\Annotation\NoStore;
  28. use Shopware\Storefront\Framework\Routing\RequestTransformer;
  29. use Shopware\Storefront\Page\Account\Login\AccountGuestLoginPageLoadedHook;
  30. use Shopware\Storefront\Page\Account\Login\AccountLoginPageLoadedHook;
  31. use Shopware\Storefront\Page\Account\Login\AccountLoginPageLoader;
  32. use Symfony\Component\HttpFoundation\Request;
  33. use Symfony\Component\HttpFoundation\Response;
  34. use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
  35. use Symfony\Component\Routing\Annotation\Route;
  36. /**
  37.  * @RouteScope(scopes={"storefront"})
  38.  */
  39. class AuthController extends StorefrontController
  40. {
  41.     private AccountLoginPageLoader $loginPageLoader;
  42.     private EntityRepositoryInterface $customerRecoveryRepository;
  43.     private AbstractSendPasswordRecoveryMailRoute $sendPasswordRecoveryMailRoute;
  44.     private AbstractResetPasswordRoute $resetPasswordRoute;
  45.     private AbstractLoginRoute $loginRoute;
  46.     private AbstractLogoutRoute $logoutRoute;
  47.     private CartService $cartService;
  48.     public function __construct(
  49.         AccountLoginPageLoader $loginPageLoader,
  50.         EntityRepositoryInterface $customerRecoveryRepository,
  51.         AbstractSendPasswordRecoveryMailRoute $sendPasswordRecoveryMailRoute,
  52.         AbstractResetPasswordRoute $resetPasswordRoute,
  53.         AbstractLoginRoute $loginRoute,
  54.         AbstractLogoutRoute $logoutRoute,
  55.         CartService $cartService
  56.     ) {
  57.         $this->loginPageLoader $loginPageLoader;
  58.         $this->customerRecoveryRepository $customerRecoveryRepository;
  59.         $this->sendPasswordRecoveryMailRoute $sendPasswordRecoveryMailRoute;
  60.         $this->resetPasswordRoute $resetPasswordRoute;
  61.         $this->loginRoute $loginRoute;
  62.         $this->logoutRoute $logoutRoute;
  63.         $this->cartService $cartService;
  64.     }
  65.     /**
  66.      * @Since("6.0.0.0")
  67.      * @Route("/account/login", name="frontend.account.login.page", methods={"GET"})
  68.      * @NoStore
  69.      */
  70.     public function loginPage(Request $requestRequestDataBag $dataSalesChannelContext $context): Response
  71.     {
  72.         /** @var string $redirect */
  73.         $redirect $request->get('redirectTo''frontend.account.home.page');
  74.         $customer $context->getCustomer();
  75.         if ($customer !== null && $customer->getGuest() === false) {
  76.             $request->request->set('redirectTo'$redirect);
  77.             return $this->createActionResponse($request);
  78.         }
  79.         $page $this->loginPageLoader->load($request$context);
  80.         $this->hook(new AccountLoginPageLoadedHook($page$context));
  81.         return $this->renderStorefront('@Storefront/storefront/page/account/register/index.html.twig', [
  82.             'redirectTo' => $redirect,
  83.             'redirectParameters' => $request->get('redirectParameters'json_encode([])),
  84.             'page' => $page,
  85.             'loginError' => (bool) $request->get('loginError'),
  86.             'waitTime' => $request->get('waitTime'),
  87.             'errorSnippet' => $request->get('errorSnippet'),
  88.             'data' => $data,
  89.         ]);
  90.     }
  91.     /**
  92.      * @Since("6.3.4.1")
  93.      * @Route("/account/guest/login", name="frontend.account.guest.login.page", methods={"GET"})
  94.      * @NoStore
  95.      */
  96.     public function guestLoginPage(Request $requestSalesChannelContext $context): Response
  97.     {
  98.         /** @var string $redirect */
  99.         $redirect $request->get('redirectTo''frontend.account.home.page');
  100.         $customer $context->getCustomer();
  101.         if ($customer !== null) {
  102.             $request->request->set('redirectTo'$redirect);
  103.             return $this->createActionResponse($request);
  104.         }
  105.         $waitTime = (int) $request->get('waitTime');
  106.         if ($waitTime) {
  107.             $this->addFlash(self::INFO$this->trans('account.loginThrottled', ['%seconds%' => $waitTime]));
  108.         }
  109.         if ((bool) $request->get('loginError')) {
  110.             $this->addFlash(self::DANGER$this->trans('account.orderGuestLoginWrongCredentials'));
  111.         }
  112.         $page $this->loginPageLoader->load($request$context);
  113.         $this->hook(new AccountGuestLoginPageLoadedHook($page$context));
  114.         return $this->renderStorefront('@Storefront/storefront/page/account/guest-auth.html.twig', [
  115.             'redirectTo' => $redirect,
  116.             'redirectParameters' => $request->get('redirectParameters'json_encode([])),
  117.             'page' => $page,
  118.         ]);
  119.     }
  120.     /**
  121.      * @Since("6.0.0.0")
  122.      * @Route("/account/logout", name="frontend.account.logout.page", methods={"GET"})
  123.      */
  124.     public function logout(Request $requestSalesChannelContext $contextRequestDataBag $dataBag): Response
  125.     {
  126.         if ($context->getCustomer() === null) {
  127.             return $this->redirectToRoute('frontend.account.login.page');
  128.         }
  129.         try {
  130.             $this->logoutRoute->logout($context$dataBag);
  131.             $this->addFlash(self::SUCCESS$this->trans('account.logoutSucceeded'));
  132.             $parameters = [];
  133.         } catch (ConstraintViolationException $formViolations) {
  134.             $parameters = ['formViolations' => $formViolations];
  135.         }
  136.         return $this->redirectToRoute('frontend.account.login.page'$parameters);
  137.     }
  138.     /**
  139.      * @Since("6.0.0.0")
  140.      * @Route("/account/login", name="frontend.account.login", methods={"POST"}, defaults={"XmlHttpRequest"=true})
  141.      */
  142.     public function login(Request $requestRequestDataBag $dataSalesChannelContext $context): Response
  143.     {
  144.         $customer $context->getCustomer();
  145.         if ($customer !== null && $customer->getGuest() === false) {
  146.             return $this->createActionResponse($request);
  147.         }
  148.         try {
  149.             $token $this->loginRoute->login($data$context)->getToken();
  150.             if (!empty($token)) {
  151.                 $this->addCartErrors($this->cartService->getCart($token$context));
  152.                 return $this->createActionResponse($request);
  153.             }
  154.         } catch (BadCredentialsException UnauthorizedHttpException InactiveCustomerException CustomerAuthThrottledException $e) {
  155.             if ($e instanceof InactiveCustomerException) {
  156.                 $errorSnippet $e->getSnippetKey();
  157.             }
  158.             if ($e instanceof CustomerAuthThrottledException) {
  159.                 $waitTime $e->getWaitTime();
  160.             }
  161.         }
  162.         $data->set('password'null);
  163.         return $this->forwardToRoute(
  164.             'frontend.account.login.page',
  165.             [
  166.                 'loginError' => true,
  167.                 'errorSnippet' => $errorSnippet ?? null,
  168.                 'waitTime' => $waitTime ?? null,
  169.             ]
  170.         );
  171.     }
  172.     /**
  173.      * @Since("6.1.0.0")
  174.      * @Route("/account/recover", name="frontend.account.recover.page", methods={"GET"})
  175.      *
  176.      * @throws CategoryNotFoundException
  177.      * @throws InconsistentCriteriaIdsException
  178.      * @throws MissingRequestParameterException
  179.      */
  180.     public function recoverAccountForm(Request $requestSalesChannelContext $context): Response
  181.     {
  182.         $page $this->loginPageLoader->load($request$context);
  183.         return $this->renderStorefront('@Storefront/storefront/page/account/profile/recover-password.html.twig', [
  184.             'page' => $page,
  185.         ]);
  186.     }
  187.     /**
  188.      * @Since("6.1.0.0")
  189.      * @Route("/account/recover", name="frontend.account.recover.request", methods={"POST"})
  190.      */
  191.     public function generateAccountRecovery(Request $requestRequestDataBag $dataSalesChannelContext $context): Response
  192.     {
  193.         try {
  194.             $data->get('email')
  195.                 ->set('storefrontUrl'$request->attributes->get(RequestTransformer::STOREFRONT_URL));
  196.             $this->sendPasswordRecoveryMailRoute->sendRecoveryMail(
  197.                 $data->get('email')->toRequestDataBag(),
  198.                 $context,
  199.                 false
  200.             );
  201.             $this->addFlash(self::SUCCESS$this->trans('account.recoveryMailSend'));
  202.         } catch (CustomerNotFoundException $e) {
  203.             $this->addFlash(self::SUCCESS$this->trans('account.recoveryMailSend'));
  204.         } catch (InconsistentCriteriaIdsException $e) {
  205.             $this->addFlash(self::DANGER$this->trans('error.message-default'));
  206.         } catch (RateLimitExceededException $e) {
  207.             $this->addFlash(self::INFO$this->trans('error.rateLimitExceeded', ['%seconds%' => $e->getWaitTime()]));
  208.         }
  209.         return $this->redirectToRoute('frontend.account.recover.page');
  210.     }
  211.     /**
  212.      * @Since("6.1.0.0")
  213.      * @Route("/account/recover/password", name="frontend.account.recover.password.page", methods={"GET"})
  214.      *
  215.      * @throws CategoryNotFoundException
  216.      * @throws InconsistentCriteriaIdsException
  217.      * @throws MissingRequestParameterException
  218.      */
  219.     public function resetPasswordForm(Request $requestSalesChannelContext $context): Response
  220.     {
  221.         $page $this->loginPageLoader->load($request$context);
  222.         $hash $request->get('hash');
  223.         if (!$hash) {
  224.             $this->addFlash(self::DANGER$this->trans('account.passwordHashNotFound'));
  225.             return $this->redirectToRoute('frontend.account.recover.request');
  226.         }
  227.         $customerHashCriteria = new Criteria();
  228.         $customerHashCriteria->addFilter(new EqualsFilter('hash'$hash));
  229.         $customerRecovery $this->customerRecoveryRepository
  230.             ->search($customerHashCriteria$context->getContext())
  231.             ->first();
  232.         if ($customerRecovery === null) {
  233.             $this->addFlash(self::DANGER$this->trans('account.passwordHashNotFound'));
  234.             return $this->redirectToRoute('frontend.account.recover.request');
  235.         }
  236.         if (!$this->checkHash($hash$context->getContext())) {
  237.             $this->addFlash(self::DANGER$this->trans('account.passwordHashExpired'));
  238.             return $this->redirectToRoute('frontend.account.recover.request');
  239.         }
  240.         return $this->renderStorefront('@Storefront/storefront/page/account/profile/reset-password.html.twig', [
  241.             'page' => $page,
  242.             'hash' => $hash,
  243.             'formViolations' => $request->get('formViolations'),
  244.         ]);
  245.     }
  246.     /**
  247.      * @Since("6.1.0.0")
  248.      * @Route("/account/recover/password", name="frontend.account.recover.password.reset", methods={"POST"})
  249.      *
  250.      * @throws InconsistentCriteriaIdsException
  251.      */
  252.     public function resetPassword(RequestDataBag $dataSalesChannelContext $context): Response
  253.     {
  254.         $hash $data->get('password')->get('hash');
  255.         try {
  256.             $pw $data->get('password');
  257.             $this->resetPasswordRoute->resetPassword($pw->toRequestDataBag(), $context);
  258.             $this->addFlash(self::SUCCESS$this->trans('account.passwordChangeSuccess'));
  259.         } catch (ConstraintViolationException $formViolations) {
  260.             $this->addFlash(self::DANGER$this->trans('account.passwordChangeNoSuccess'));
  261.             return $this->forwardToRoute(
  262.                 'frontend.account.recover.password.page',
  263.                 ['hash' => $hash'formViolations' => $formViolations'passwordFormViolation' => true]
  264.             );
  265.         } catch (CustomerNotFoundByHashException $e) {
  266.             $this->addFlash(self::DANGER$this->trans('account.passwordChangeNoSuccess'));
  267.             return $this->forwardToRoute('frontend.account.recover.request');
  268.         } catch (CustomerRecoveryHashExpiredException $e) {
  269.             $this->addFlash(self::DANGER$this->trans('account.passwordHashExpired'));
  270.             return $this->forwardToRoute('frontend.account.recover.request');
  271.         }
  272.         return $this->redirectToRoute('frontend.account.profile.page');
  273.     }
  274.     private function checkHash(string $hashContext $context): bool
  275.     {
  276.         $criteria = new Criteria();
  277.         $criteria->addFilter(new EqualsFilter('hash'$hash));
  278.         $recovery $this->customerRecoveryRepository->search($criteria$context)->first();
  279.         $validDateTime = (new \DateTime())->sub(new \DateInterval('PT2H'));
  280.         return $recovery && $validDateTime $recovery->getCreatedAt();
  281.     }
  282. }