vendor/pimcore/portal-engine/src/Service/Security/Authenticator/LoginAuthenticator.php line 86

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under following license:
  6.  * - Pimcore Commercial License (PCL)
  7.  *
  8.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  9.  *  @license    http://www.pimcore.org/license     PCL
  10.  */
  11. namespace Pimcore\Bundle\PortalEngineBundle\Service\Security\Authenticator;
  12. use Pimcore\Bundle\PortalEngineBundle\Enum\Permission;
  13. use Pimcore\Bundle\PortalEngineBundle\Event\Auth\LoginCheckPasswordEvent;
  14. use Pimcore\Bundle\PortalEngineBundle\Event\Auth\LoginGetUserEvent;
  15. use Pimcore\Bundle\PortalEngineBundle\Form\LoginForm;
  16. use Pimcore\Bundle\PortalEngineBundle\Model\DataObject\PortalUserInterface;
  17. use Pimcore\Bundle\PortalEngineBundle\Service\PortalConfig\PortalConfigService;
  18. use Pimcore\Bundle\PortalEngineBundle\Service\Security\Authentication\UserProvider;
  19. use Pimcore\Bundle\PortalEngineBundle\Service\Security\PermissionService;
  20. use Pimcore\Bundle\PortalEngineBundle\Service\StatisticsTracker\Elasticsearch\PortalUserLoginTracker;
  21. use Pimcore\Model\DataObject\ClassDefinition\Data\Password;
  22. use Pimcore\Model\DataObject\Concrete;
  23. use Pimcore\Model\Site;
  24. use Pimcore\Model\User;
  25. use Pimcore\Tool\Authentication;
  26. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  27. use Symfony\Component\Form\FormFactoryInterface;
  28. use Symfony\Component\HttpFoundation\RedirectResponse;
  29. use Symfony\Component\HttpFoundation\Request;
  30. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  31. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  32. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  33. use Symfony\Component\Security\Http\Authenticator\InteractiveAuthenticatorInterface;
  34. use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
  35. use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
  36. use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials;
  37. use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
  38. use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
  39. use Symfony\Component\Security\Http\Util\TargetPathTrait;
  40. class LoginAuthenticator extends AbstractAuthenticator implements AuthenticationEntryPointInterfaceInteractiveAuthenticatorInterface
  41. {
  42.     use TargetPathTrait;
  43.     /** @var FormFactoryInterface */
  44.     protected FormFactoryInterface $formFactory;
  45.     /** @var PortalUserLoginTracker */
  46.     protected PortalUserLoginTracker $portalUserLoginTracker;
  47.     /** @var EventDispatcherInterface */
  48.     protected EventDispatcherInterface $eventDispatcher;
  49.     /** @var UserProvider */
  50.     protected UserProvider $userProvider;
  51.     public function __construct(
  52.         FormFactoryInterface $formFactory,
  53.         PortalUserLoginTracker $portalUserLoginTracker,
  54.         EventDispatcherInterface $eventDispatcher,
  55.         UserProvider $userProvider,
  56.         UrlGeneratorInterface $urlGenerator,
  57.         PortalConfigService $portalConfigService,
  58.         PermissionService $permissionService
  59.     ) {
  60.         parent::__construct($urlGenerator$portalConfigService$permissionService);
  61.         $this->formFactory $formFactory;
  62.         $this->portalUserLoginTracker $portalUserLoginTracker;
  63.         $this->eventDispatcher $eventDispatcher;
  64.         $this->userProvider $userProvider;
  65.     }
  66.     /**
  67.      * Does the authenticator support the given Request?
  68.      *
  69.      * If this returns false, the authenticator will be skipped.
  70.      *
  71.      * @param Request $request
  72.      *
  73.      * @return bool|null
  74.      */
  75.     public function supports(Request $request): ?bool
  76.     {
  77.         if (!$this->portalConfigService->isPortalEngineSite()) {
  78.             return false;
  79.         }
  80.         if ('pimcore_portalengine_auth_login' !== $request->attributes->get('_route')) {
  81.             return false;
  82.         }
  83.         $loginForm $this->formFactory->create(LoginForm::class);
  84.         $loginForm->handleRequest($request);
  85.         return $loginForm->isSubmitted() && $loginForm->isValid();
  86.     }
  87.     /**
  88.      * Get the authentication credentials from the request as any an associate array.
  89.      * Check credentials in the passport through CustomCredentials
  90.      *
  91.      * @param Request $request
  92.      *
  93.      * @return Passport
  94.      */
  95.     public function authenticate(Request $request): Passport
  96.     {
  97.         $loginForm $this->formFactory->create(LoginForm::class);
  98.         $loginForm->handleRequest($request);
  99.         $credentials $loginForm->getData();
  100.         $event = new LoginGetUserEvent($credentials['username'], $credentials['password']);
  101.         $this->eventDispatcher->dispatch($event);
  102.         $passport = new Passport(
  103.             new UserBadge($credentials['username']),
  104.             new CustomCredentials(function ($credentials) {
  105.                 /** @var LoginGetUserEvent $event */
  106.                 $event $credentials['event'];
  107.                 if ($event->getPortalUserResolved()) {
  108.                     $user $event->getPortalUser();
  109.                 } else {
  110.                     $user null;
  111.                     try {
  112.                         $user $this->userProvider->loadUserByIdentifier($credentials['username']);
  113.                     } catch (\Exception $e) {
  114.                         // nothing to do
  115.                     }
  116.                 }
  117.                 if (empty($user) || !$user instanceof PortalUserInterface) {
  118.                     throw new AuthenticationException(sprintf('User with email %s not found.'$credentials['username']));
  119.                 }
  120.                 $event = new LoginCheckPasswordEvent($user$credentials['password']);
  121.                 $this->eventDispatcher->dispatch($event);
  122.                 if (is_bool($event->getLoginValid())) {
  123.                     $success $event->getLoginValid();
  124.                 } elseif ($user->getUsePimcoreUserPassword() && $user->getPimcoreUser()) {
  125.                     $pimcoreUser User::getById(intval($user->getPimcoreUser()));
  126.                     $success Authentication::isValidUser($pimcoreUser) && Authentication::verifyPassword($pimcoreUser$credentials['password']);
  127.                 } else {
  128.                     $fieldDefinition $user->getClass()->getFieldDefinition('portalPassword');
  129.                     $success $fieldDefinition instanceof Password && $user instanceof Concrete
  130.                         && $fieldDefinition->verifyPassword($credentials['password'], $user);
  131.                 }
  132.                 if ($success) {
  133.                     $portalId Site::getCurrentSite()->getRootId();
  134.                     $success $this->permissionService->isAllowed($userPermission::PORTAL_ACCESS Permission::PERMISSION_DELIMITER $portalId);
  135.                 }
  136.                 if (!$success) {
  137.                     throw new AuthenticationException('Password wrong');
  138.                 }
  139.                 return true;
  140.             }, ['username' => $credentials['username'], 'password' => $credentials['password'], 'event' => $event]),
  141.         );
  142.         if ($credentials['_remember_me'] ?? false) {
  143.             $passport->addBadge(new RememberMeBadge());
  144.         }
  145.         return $passport;
  146.     }
  147.     /**
  148.      * Called when authentication executed and was successful!
  149.      *
  150.      * This should return the Response sent back to the user, like a
  151.      * RedirectResponse to the last page they visited.
  152.      *
  153.      * If you return null, the current request will continue, and the user
  154.      * will be authenticated. This makes sense, for example, with an API.
  155.      *
  156.      * @param Request $request
  157.      * @param TokenInterface $token
  158.      * @param string $firewallName
  159.      *
  160.      * @return RedirectResponse|null
  161.      *
  162.      * @throws \Exception
  163.      */
  164.     public function onAuthenticationSuccess(Request $requestTokenInterface $tokenstring $firewallName): ?RedirectResponse
  165.     {
  166.         $this->portalUserLoginTracker->trackEvent(['user' => $token->getUser()]);
  167.         if ($targetPath $this->getTargetPath($request->getSession(), $firewallName)) {
  168.             if (!$this->isRestApiPath($targetPath)) {
  169.                 return new RedirectResponse($targetPath);
  170.             }
  171.         }
  172.         return new RedirectResponse('/');
  173.     }
  174.     public function isInteractive(): bool
  175.     {
  176.         return true;
  177.     }
  178. }