src/Controller/AccountController.php line 422

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Enterprise License (PEL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PEL
  13.  */
  14. namespace App\Controller;
  15. use App\EventListener\AuthenticationLoginListener;
  16. use App\Form\LoginFormType;
  17. use App\Form\RegistrationFormHandler;
  18. use App\Form\RegistrationFormType;
  19. use App\Model\Customer;
  20. use App\Services\NewsletterDoubleOptInService;
  21. use App\Services\PasswordRecoveryService;
  22. use CustomerManagementFrameworkBundle\CustomerProvider\CustomerProviderInterface;
  23. use CustomerManagementFrameworkBundle\CustomerSaveValidator\Exception\DuplicateCustomerException;
  24. use CustomerManagementFrameworkBundle\Model\CustomerInterface;
  25. use CustomerManagementFrameworkBundle\Security\Authentication\LoginManagerInterface;
  26. use CustomerManagementFrameworkBundle\Security\OAuth\Exception\AccountNotLinkedException;
  27. use CustomerManagementFrameworkBundle\Security\OAuth\OAuthRegistrationHandler;
  28. use CustomerManagementFrameworkBundle\Security\SsoIdentity\SsoIdentityServiceInterface;
  29. use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
  30. use HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken;
  31. use Pimcore\Bundle\EcommerceFrameworkBundle\Factory;
  32. use Pimcore\Bundle\EcommerceFrameworkBundle\OrderManager\Order\Listing\Filter\CustomerObject;
  33. use Pimcore\DataObject\Consent\Service;
  34. use Pimcore\Translation\Translator;
  35. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
  36. use Symfony\Component\HttpFoundation\RedirectResponse;
  37. use Symfony\Component\HttpFoundation\Request;
  38. use Symfony\Component\HttpFoundation\Response;
  39. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  40. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  41. use Symfony\Component\Routing\Annotation\Route;
  42. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  43. use Symfony\Component\Security\Core\User\UserInterface;
  44. use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
  45. use Symfony\Component\Uid\Uuid;
  46. /**
  47.  * Class AccountController
  48.  *
  49.  * Controller that handles all account functionality, including register, login and connect to SSO profiles
  50.  */
  51. class AccountController extends BaseController
  52. {
  53.     /**
  54.      * @Route("/account/login", name="account-login")
  55.      *
  56.      * @param AuthenticationUtils $authenticationUtils
  57.      * @param OAuthRegistrationHandler $oAuthHandler
  58.      * @param SessionInterface $session
  59.      * @param Request $request
  60.      * @param UserInterface|null $user
  61.      *
  62.      * @return Response|RedirectResponse
  63.      */
  64.     public function loginAction(
  65.         AuthenticationUtils $authenticationUtils,
  66.         OAuthRegistrationHandler $oAuthHandler,
  67.         SessionInterface $session,
  68.         Request $request,
  69.         UserInterface $user null
  70.     ): RedirectResponse|Response
  71.     {
  72.         //redirect user to index page if logged in
  73.         if ($user && $this->isGranted('ROLE_USER')) {
  74.             return $this->redirectToRoute('portal-page');
  75.         }
  76.         // get the login error if there is one
  77.         $error $authenticationUtils->getLastAuthenticationError();
  78.         // OAuth handling - the OAuth authenticator is configured to return to the login page on errors
  79.         // (see failure_path configuration) - therefore we can fetch the last authentication error
  80.         // here. If the error is an AccountNotLinkedException (as thrown by our user provider) save the
  81.         // OAuth token to the session and redirect to registration with a special key which can be used
  82.         // to load the token to prepopulate the registration form with account data.
  83.         /*if ($error instanceof AccountNotLinkedException) {
  84.             // this can be anything - for simplicity we just use an UUID as it is unique and random
  85.             $registrationKey = (string) Uuid::v4()->toRfc4122();
  86.             $oAuthHandler->saveToken($registrationKey, $error->getToken());
  87.             return $this->redirectToRoute('account-register', [
  88.                 'registrationKey' => $registrationKey
  89.             ]);
  90.         }*/
  91.         // last username entered by the user
  92.         $lastUsername $authenticationUtils->getLastUsername();
  93.         $formData = [
  94.             '_username' => $lastUsername
  95.         ];
  96.         $form $this->createForm(LoginFormType::class, $formData, [
  97.             'action' => $this->generateUrl('account-login'),
  98.         ]);
  99.         //store referer in session to get redirected after login
  100.         /*if (!$request->get('no-referer-redirect')) {
  101.             $session->set('_security.demo_frontend.target_path', $request->headers->get('referer'));
  102.         }*/
  103.         return $this->render('account/login.html.twig', [
  104.             'form' => $form->createView(),
  105.             'error' => $error,
  106.             'hideBreadcrumbs' => true
  107.         ]);
  108.     }
  109.     /**
  110.      * If registration is called with a registration key, the key will be used to look for an existing OAuth token in
  111.      * the session. This OAuth token will be used to fetch user info which can be used to pre-populate the form and to
  112.      * link a SSO identity to the created customer object.
  113.      *
  114.      * This could be further separated into services, but was kept as single method for demonstration purposes as the
  115.      * registration process is different on every project.
  116.      *
  117.      * @Route("/account/register", name="account-register")
  118.      *
  119.      * @param Request $request
  120.      * @param CustomerProviderInterface $customerProvider
  121.      * @param OAuthRegistrationHandler $oAuthHandler
  122.      * @param LoginManagerInterface $loginManager
  123.      * @param RegistrationFormHandler $registrationFormHandler
  124.      * @param SessionInterface $session
  125.      * @param AuthenticationLoginListener $authenticationLoginListener
  126.      * @param Translator $translator
  127.      * @param Service $consentService
  128.      * @param UrlGeneratorInterface $urlGenerator
  129.      * @param NewsletterDoubleOptInService $newsletterDoubleOptInService
  130.      * @param UserInterface|null $user
  131.      *
  132.      * @return Response|RedirectResponse
  133.      */
  134.     public function registerAction(
  135.         Request $request,
  136.         CustomerProviderInterface $customerProvider,
  137.         OAuthRegistrationHandler $oAuthHandler,
  138.         LoginManagerInterface $loginManager,
  139.         RegistrationFormHandler $registrationFormHandler,
  140.         SessionInterface $session,
  141.         AuthenticationLoginListener $authenticationLoginListener,
  142.         Translator $translator,
  143.         Service $consentService,
  144.         UrlGeneratorInterface $urlGenerator,
  145.         NewsletterDoubleOptInService $newsletterDoubleOptInService,
  146.         UserInterface $user null
  147.     ) {
  148.         return $this->redirectToRoute('portal-page');
  149.         //redirect user to index page if logged in
  150.         /*if ($user && $this->isGranted('ROLE_USER')) {
  151.             return $this->redirectToRoute('account-index');
  152.         }*/
  153.         //$registrationKey = $request->get('registrationKey');
  154.         // create a new, empty customer instance
  155.         ///** @var CustomerInterface|\Pimcore\Model\DataObject\Customer $customer */
  156.         //$customer = $customerProvider->create();
  157.         ///** @var OAuthToken $oAuthToken */
  158.         //$oAuthToken = null;
  159.         ///** @var UserResponseInterface $oAuthUserInfo */
  160.         //$oAuthUserInfo = null;
  161.         // load previously stored token from the session and try to load user profile
  162.         // from provider
  163.         /*if (null !== $registrationKey) {
  164.             $oAuthToken = $oAuthHandler->loadToken($registrationKey);
  165.             $oAuthUserInfo = $oAuthHandler->loadUserInformation($oAuthToken);
  166.         }*/
  167.         /*if (null !== $oAuthUserInfo) {
  168.             // try to load a customer with the given identity from our storage. if this succeeds, we can't register
  169.             // the customer and should either log in the existing identity or show an error. for simplicity, we just
  170.             // throw an exception here.
  171.             // this shouldn't happen as the login would log in the user if found
  172.             if ($oAuthHandler->getCustomerFromUserResponse($oAuthUserInfo)) {
  173.                 throw new \RuntimeException('Customer is already registered');
  174.             }
  175.         }*/
  176.         // the registration form handler is just a utility class to map pimcore object data to form
  177.         // and vice versa.
  178.         /*$formData = $registrationFormHandler->buildFormData($customer);
  179.         $hidePassword = false;
  180.         if (null !== $oAuthToken) {
  181.             $formData = $this->mergeOAuthFormData($formData, $oAuthUserInfo);
  182.             $hidePassword = true;
  183.         }*/
  184.         // build the registration form and pre-fill it with customer data
  185.         /*$form = $this->createForm(RegistrationFormType::class, $formData, ['hidePassword' => $hidePassword]);
  186.         $form->handleRequest($request);*/
  187.         /*$errors = [];
  188.         if ($form->isSubmitted() && $form->isValid()) {
  189.             $registrationFormHandler->updateCustomerFromForm($customer, $form);
  190.             $customer->setCustomerLanguage($request->getLocale());
  191.             $customer->setActive(true);
  192.             try {
  193.                 $customer->save();
  194.                 if ($form->getData()['newsletter']) {
  195.                     $consentService->giveConsent($customer, 'newsletter', $translator->trans('general.newsletter'));
  196.                     $newsletterDoubleOptInService->sendDoubleOptInMail($customer, $this->document->getProperty('newsletter_confirm_mail'));
  197.                 }
  198.                 if ($form->getData()['profiling']) {
  199.                     $consentService->giveConsent($customer, 'profiling', $translator->trans('general.profiling'));
  200.                 }
  201.                 // add SSO identity from OAuth data
  202.                 if (null !== $oAuthUserInfo) {
  203.                     $oAuthHandler->connectSsoIdentity($customer, $oAuthUserInfo);
  204.                 }
  205.                 //check if special redirect is necessary
  206.                 if ($session->get('referrer')) {
  207.                     $response = $this->redirect($session->get('referrer'));
  208.                     $session->remove('referrer');
  209.                 } else {
  210.                     $response = $this->redirectToRoute('account-index');
  211.                 }
  212.                 // log user in manually
  213.                 // pass response to login manager as it adds potential remember me cookies
  214.                 $loginManager->login($customer, $request, $response);
  215.                 //do ecommerce framework login
  216.                 $authenticationLoginListener->doEcommerceFrameworkLogin($customer);
  217.                 return $response;
  218.             } catch (DuplicateCustomerException $e) {
  219.                 $errors[] = $translator->trans(
  220.                     'account.customer-already-exists',
  221.                     [
  222.                         $customer->getEmail(),
  223.                         $urlGenerator->generate('account-password-send-recovery', ['email' => $customer->getEmail()])
  224.                     ]
  225.                 );
  226.             } catch (\Exception $e) {
  227.                 $errors[] = $e->getMessage();
  228.             }
  229.         }*/
  230.         /*
  231.         if ($form->isSubmitted() && !$form->isValid()) {
  232.             foreach ($form->getErrors() as $error) {
  233.                 $errors[] = $error->getMessage();
  234.             }
  235.         }*/
  236.         // re-save user info to session as we need it in subsequent requests (e.g. after form errors) or
  237.         // when form is rendered for the first time
  238.         /*if (null !== $registrationKey && null !== $oAuthToken) {
  239.             $oAuthHandler->saveToken($registrationKey, $oAuthToken);
  240.         }*/
  241.         /*return $this->render('account/register.html.twig', [
  242.             'customer' => $customer,
  243.             'form' => $form->createView(),
  244.             'errors' => $errors,
  245.             'hideBreadcrumbs' => true,
  246.             'hidePassword' => $hidePassword
  247.         ]);*/
  248.     }
  249.     /**
  250.      * Special route for connecting to social profiles that saves referrer in session for later
  251.      * redirect to that referrer
  252.      *
  253.      * @param Request $request
  254.      * @param SessionInterface $session
  255.      * @param $service
  256.      *
  257.      * @return Response
  258.      * @Route("/auth/oauth/referrerLogin/{service}", name="app_auth_oauth_login_referrer")
  259.      */
  260.     public function connectAction(Request $requestSessionInterface $session$service)
  261.     {
  262.         // we overwrite this route to store user's referrer in the session
  263.         $session->set('referrer'$request->headers->get('referer'));
  264.         return $this->forward('HWIOAuthBundle:Connect:redirectToService', ['service' => $service]);
  265.     }
  266.     /**
  267.      * Connects an already logged in user to an auth provider
  268.      *
  269.      * @Route("/oauth/connect/{service}", name="app_auth_oauth_connect")
  270.      * @Security("is_granted('ROLE_USER')")
  271.      *
  272.      * @param Request $request
  273.      * @param OAuthRegistrationHandler $oAuthHandler
  274.      * @param UserInterface $user
  275.      * @param string $service
  276.      *
  277.      * @return RedirectResponse
  278.      */
  279.     public function oAuthConnectAction(
  280.         Request $request,
  281.         OAuthRegistrationHandler $oAuthHandler,
  282.         UserInterface $user,
  283.         string $service
  284.     ) {
  285.         $resourceOwner $oAuthHandler->getResourceOwner($service);
  286.         $redirectUrl $this->generateUrl('app_auth_oauth_connect', [
  287.             'service' => $service
  288.         ], UrlGeneratorInterface::ABSOLUTE_URL);
  289.         // redirect to authorization
  290.         if (!$resourceOwner->handles($request)) {
  291.             $authorizationUrl $oAuthHandler->getAuthorizationUrl($request$service$redirectUrl);
  292.             return $this->redirect($authorizationUrl);
  293.         }
  294.         // get access token from URL
  295.         $accessToken $resourceOwner->getAccessToken($request$redirectUrl);
  296.         // e.g. user cancelled auth on provider side
  297.         if (null === $accessToken) {
  298.             return $this->redirectToRoute('account-index');
  299.         }
  300.         $oAuthUserInfo $resourceOwner->getUserInformation($accessToken);
  301.         // we don't want to allow linking an OAuth account to multiple customers
  302.         if ($oAuthHandler->getCustomerFromUserResponse($oAuthUserInfo)) {
  303.             throw new \RuntimeException('There\'s already a customer registered with this provider identity');
  304.         }
  305.         // create a SSO identity object and save it to the user
  306.         $oAuthHandler->connectSsoIdentity($user$oAuthUserInfo);
  307.         // redirect to secure page which should now list the newly linked profile
  308.         return $this->redirectToRoute('account-index');
  309.     }
  310.     /**
  311.      *
  312.      * @param array $formData
  313.      * @param UserResponseInterface $userInformation
  314.      *
  315.      * @return array
  316.      */
  317.     private function mergeOAuthFormData(
  318.         array $formData,
  319.         UserResponseInterface $userInformation
  320.     ): array {
  321.         return array_replace([
  322.             'firstname' => $userInformation->getFirstName(),
  323.             'lastname' => $userInformation->getLastName(),
  324.             'email' => $userInformation->getEmail()
  325.         ], $formData);
  326.     }
  327.     /**
  328.      * Index page for account - it is restricted to ROLE_USER via security annotation
  329.      *
  330.      * @Route("/account/index", name="account-index")
  331.      * @Security("is_granted('ROLE_USER')")
  332.      *
  333.      * @param SsoIdentityServiceInterface $identityService
  334.      * @param UserInterface|null $user
  335.      *
  336.      * @return Response
  337.      */
  338.     public function indexAction(SsoIdentityServiceInterface $identityServiceUserInterface $user null): Response
  339.     {
  340.         return $this->redirectToRoute('portal-page');
  341.         /*
  342.         $blacklist = [];
  343.         foreach ($identityService->getSsoIdentities($user) as $identity) {
  344.             $blacklist[] = $identity->getProvider();
  345.         }*/
  346.         //$orderManager = Factory::getInstance()->getOrderManager();
  347.         //$orderList = $orderManager->createOrderList();
  348.         //$orderList->addFilter(new CustomerObject($user));
  349.         //$orderList->setOrder('orderDate DESC');
  350.         /*return $this->render('account/index.html.twig', [
  351.             'blacklist' => $blacklist,
  352.             //'orderList' => $orderList,
  353.             'hideBreadcrumbs' => true
  354.         ]);
  355.         */
  356.     }
  357.     /**
  358.      * @Route("/account/send-password-recovery", name="account-password-send-recovery")
  359.      *
  360.      * @param Request $request
  361.      * @param PasswordRecoveryService $service
  362.      * @param Translator $translator
  363.      *
  364.      * @return Response
  365.      *
  366.      * @throws \Exception
  367.      */
  368.     public function sendPasswordRecoveryMailAction(Request $requestPasswordRecoveryService $serviceTranslator $translator): Response
  369.     {
  370.         if ($request->isMethod(Request::METHOD_POST)) {
  371.             try {
  372.                 $customer $service->sendRecoveryMail($request->get('email'''), $this->document->getProperty('password_reset_mail'));
  373.                 if (!$customer instanceof CustomerInterface) {
  374.                     throw new \Exception('Invalid Customer');
  375.                 }
  376.                 $this->addFlash('success'$translator->trans('account.reset-mail-sent-when-possible'));
  377.             } catch (\Exception $e) {
  378.                 $this->addFlash('danger'$e->getMessage());
  379.             }
  380.             return $this->redirectToRoute('account-login', ['no-referer-redirect' => true]);
  381.         }
  382.         return $this->render('account/send_password_recovery_mail.html.twig', [
  383.             'hideBreadcrumbs' => true,
  384.             'emailPrefill' => $request->get('email')
  385.         ]);
  386.     }
  387.     /**
  388.      * @Route("/account/reset-password", name="account-reset-password")
  389.      *
  390.      * @param Request $request
  391.      * @param PasswordRecoveryService $service
  392.      * @param Translator $translator
  393.      *
  394.      * @return Response|RedirectResponse
  395.      */
  396.     public function resetPasswordAction(Request $requestPasswordRecoveryService $serviceTranslator $translator): RedirectResponse|Response
  397.     {
  398.         $token $request->get('token');
  399.         $customer $service->getCustomerByToken($token);
  400.         if (!$customer) {
  401.             //TODO render error page
  402.             throw new NotFoundHttpException('Invalid token');
  403.         }
  404.         if ($request->isMethod(Request::METHOD_POST)) {
  405.             $newPassword $request->get('password');
  406.             $service->setPassword($token$newPassword);
  407.             $this->addFlash('success'$translator->trans('account.password-reset-successful'));
  408.             return $this->redirectToRoute('account-login', ['no-referer-redirect' => true]);
  409.         }
  410.         return $this->render('account/reset_password.html.twig', [
  411.             'hideBreadcrumbs' => true,
  412.             'token' => $token,
  413.             'email' => $customer->getEmail()
  414.         ]);
  415.     }
  416. }