vendor/pimcore/pimcore/bundles/AdminBundle/Security/Guard/AdminAuthenticator.php line 190

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 Commercial License (PCL)
  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 PCL
  13.  */
  14. namespace Pimcore\Bundle\AdminBundle\Security\Guard;
  15. use Pimcore\Bundle\AdminBundle\Security\Authentication\Token\TwoFactorRequiredToken;
  16. use Pimcore\Bundle\AdminBundle\Security\BruteforceProtectionHandler;
  17. use Pimcore\Bundle\AdminBundle\Security\User\User;
  18. use Pimcore\Cache\Runtime;
  19. use Pimcore\Event\Admin\Login\LoginCredentialsEvent;
  20. use Pimcore\Event\Admin\Login\LoginFailedEvent;
  21. use Pimcore\Event\Admin\Login\LoginRedirectEvent;
  22. use Pimcore\Event\AdminEvents;
  23. use Pimcore\Model\User as UserModel;
  24. use Pimcore\Tool\Admin;
  25. use Pimcore\Tool\Authentication;
  26. use Pimcore\Tool\Session;
  27. use Psr\Log\LoggerAwareInterface;
  28. use Psr\Log\LoggerAwareTrait;
  29. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  30. use Symfony\Component\HttpFoundation\Cookie;
  31. use Symfony\Component\HttpFoundation\RedirectResponse;
  32. use Symfony\Component\HttpFoundation\Request;
  33. use Symfony\Component\HttpFoundation\Response;
  34. use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface;
  35. use Symfony\Component\Routing\RouterInterface;
  36. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  37. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  38. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  39. use Symfony\Component\Security\Core\User\UserInterface;
  40. use Symfony\Component\Security\Core\User\UserProviderInterface;
  41. use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
  42. use Symfony\Component\Security\Http\HttpUtils;
  43. use Symfony\Contracts\Translation\TranslatorInterface;
  44. /**
  45.  * @internal
  46.  */
  47. class AdminAuthenticator extends AbstractGuardAuthenticator implements LoggerAwareInterface
  48. {
  49.     use LoggerAwareTrait;
  50.     /**
  51.      * @var TokenStorageInterface
  52.      */
  53.     protected $tokenStorage;
  54.     /**
  55.      * @var RouterInterface
  56.      */
  57.     protected $router;
  58.     /**
  59.      * @var EventDispatcherInterface
  60.      */
  61.     protected $dispatcher;
  62.     /**
  63.      * @var TranslatorInterface
  64.      */
  65.     protected $translator;
  66.     /**
  67.      * @var HttpUtils
  68.      */
  69.     protected $httpUtils;
  70.     /**
  71.      * @var BruteforceProtectionHandler
  72.      */
  73.     protected $bruteforceProtectionHandler;
  74.     /**
  75.      * @var bool
  76.      */
  77.     protected $twoFactorRequired false;
  78.     /**
  79.      * @param TokenStorageInterface $tokenStorage
  80.      * @param RouterInterface $router
  81.      * @param EventDispatcherInterface $dispatcher
  82.      * @param TranslatorInterface $translator
  83.      * @param HttpUtils $httpUtils
  84.      * @param BruteforceProtectionHandler $bruteforceProtectionHandler
  85.      */
  86.     public function __construct(
  87.         TokenStorageInterface $tokenStorage,
  88.         RouterInterface $router,
  89.         EventDispatcherInterface $dispatcher,
  90.         TranslatorInterface $translator,
  91.         HttpUtils $httpUtils,
  92.         BruteforceProtectionHandler $bruteforceProtectionHandler
  93.     ) {
  94.         $this->tokenStorage $tokenStorage;
  95.         $this->router $router;
  96.         $this->dispatcher $dispatcher;
  97.         $this->translator $translator;
  98.         $this->httpUtils $httpUtils;
  99.         $this->bruteforceProtectionHandler $bruteforceProtectionHandler;
  100.     }
  101.     /**
  102.      * {@inheritdoc}
  103.      */
  104.     public function supports(Request $request)
  105.     {
  106.         return $request->attributes->get('_route') === 'pimcore_admin_login_check'
  107.             || Authentication::authenticateSession($request);
  108.     }
  109.     /**
  110.      * {@inheritdoc}
  111.      */
  112.     public function start(Request $requestAuthenticationException $authException null)
  113.     {
  114.         if ($request->isXmlHttpRequest()) {
  115.             // TODO use a JSON formatted error response?
  116.             $response = new Response('Session expired or unauthorized request. Please reload and try again!');
  117.             $response->setStatusCode(Response::HTTP_FORBIDDEN);
  118.             return $response;
  119.         }
  120.         $event = new LoginRedirectEvent('pimcore_admin_login', ['perspective' => strip_tags($request->get('perspective'''))]);
  121.         $this->dispatcher->dispatch($eventAdminEvents::LOGIN_REDIRECT);
  122.         $url $this->router->generate($event->getRouteName(), $event->getRouteParams());
  123.         return new RedirectResponse($url);
  124.     }
  125.     /**
  126.      * {@inheritdoc}
  127.      */
  128.     public function getCredentials(Request $request)
  129.     {
  130.         $credentials = [];
  131.         if ($request->attributes->get('_route') === 'pimcore_admin_login_check') {
  132.             $username $request->get('username');
  133.             if ($request->getMethod() === 'POST' && $request->get('password') && $username) {
  134.                 $this->bruteforceProtectionHandler->checkProtection($username);
  135.                 $credentials = [
  136.                     'username' => $username,
  137.                     'password' => $request->get('password'),
  138.                 ];
  139.             } elseif ($token $request->get('token')) {
  140.                 $this->bruteforceProtectionHandler->checkProtection();
  141.                 $credentials = [
  142.                     'token' => $token,
  143.                     'reset' => (bool) $request->get('reset'false),
  144.                 ];
  145.             } else {
  146.                 $this->bruteforceProtectionHandler->checkProtection();
  147.                 throw new AuthenticationException('Missing username or token');
  148.             }
  149.             $event = new LoginCredentialsEvent($request$credentials);
  150.             $this->dispatcher->dispatch($eventAdminEvents::LOGIN_CREDENTIALS);
  151.             return $event->getCredentials();
  152.         } else {
  153.             if ($pimcoreUser Authentication::authenticateSession($request)) {
  154.                 return [
  155.                     'user' => $pimcoreUser,
  156.                 ];
  157.             }
  158.         }
  159.         return $credentials;
  160.     }
  161.     /**
  162.      * {@inheritdoc}
  163.      */
  164.     public function getUser($credentialsUserProviderInterface $userProvider)
  165.     {
  166.         /** @var User|null $user */
  167.         $user null;
  168.         if (!is_array($credentials)) {
  169.             throw new AuthenticationException('Invalid credentials');
  170.         }
  171.         if (isset($credentials['user']) && $credentials['user'] instanceof UserModel) {
  172.             $user = new User($credentials['user']);
  173.             $session Session::getReadOnly();
  174.             if ($session->has('2fa_required') && $session->get('2fa_required') === true) {
  175.                 $this->twoFactorRequired true;
  176.             }
  177.         } else {
  178.             if (!isset($credentials['username']) && !isset($credentials['token'])) {
  179.                 throw new AuthenticationException('Missing username/token');
  180.             }
  181.             if (isset($credentials['password'])) {
  182.                 $pimcoreUser Authentication::authenticatePlaintext($credentials['username'], $credentials['password']);
  183.                 if ($pimcoreUser) {
  184.                     $user = new User($pimcoreUser);
  185.                 } else {
  186.                     // trigger LOGIN_FAILED event if user could not be authenticated via username/password
  187.                     $event = new LoginFailedEvent($credentials);
  188.                     $this->dispatcher->dispatch($eventAdminEvents::LOGIN_FAILED);
  189.                     if ($event->hasUser()) {
  190.                         $user = new User($event->getUser());
  191.                     } else {
  192.                         throw new AuthenticationException('Failed to authenticate with username and password');
  193.                     }
  194.                 }
  195.             } elseif (isset($credentials['token'])) {
  196.                 $pimcoreUser Authentication::authenticateToken($credentials['token']);
  197.                 if ($pimcoreUser) {
  198.                     //disable two factor authentication for token based credentials e.g. reset password, admin access links
  199.                     $pimcoreUser->setTwoFactorAuthentication('required'false);
  200.                     $user = new User($pimcoreUser);
  201.                 } else {
  202.                     throw new AuthenticationException('Failed to authenticate with username and token');
  203.                 }
  204.                 if ($credentials['reset']) {
  205.                     // save the information to session when the user want's to reset the password
  206.                     // this is because otherwise the old password is required => see also PIMCORE-1468
  207.                     Session::useSession(function (AttributeBagInterface $adminSession) {
  208.                         $adminSession->set('password_reset'true);
  209.                     });
  210.                 }
  211.             } else {
  212.                 throw new AuthenticationException('Invalid authentication method, must be either password or token');
  213.             }
  214.             if ($user && Authentication::isValidUser($user->getUser())) {
  215.                 $pimcoreUser $user->getUser();
  216.                 Session::useSession(function (AttributeBagInterface $adminSession) use ($pimcoreUser) {
  217.                     Session::regenerateId();
  218.                     $adminSession->set('user'$pimcoreUser);
  219.                     // this flag gets removed after successful authentication in \Pimcore\Bundle\AdminBundle\EventListener\TwoFactorListener
  220.                     if ($pimcoreUser->getTwoFactorAuthentication('required') && $pimcoreUser->getTwoFactorAuthentication('enabled')) {
  221.                         $adminSession->set('2fa_required'true);
  222.                     }
  223.                 });
  224.             }
  225.         }
  226.         return $user;
  227.     }
  228.     /**
  229.      * {@inheritdoc}
  230.      */
  231.     public function checkCredentials($credentialsUserInterface $user)
  232.     {
  233.         // we rely on getUser returning a valid user
  234.         if ($user instanceof User) {
  235.             return true;
  236.         }
  237.         return false;
  238.     }
  239.     /**
  240.      * {@inheritdoc}
  241.      */
  242.     public function onAuthenticationFailure(Request $requestAuthenticationException $exception)
  243.     {
  244.         $this->bruteforceProtectionHandler->addEntry($request->get('username'), $request);
  245.         $url $this->router->generate('pimcore_admin_login', [
  246.             'auth_failed' => 'true',
  247.         ]);
  248.         return new RedirectResponse($url);
  249.     }
  250.     /**
  251.      * {@inheritdoc}
  252.      */
  253.     public function onAuthenticationSuccess(Request $requestTokenInterface $token$providerKey)
  254.     {
  255.         /** @var UserModel $user */
  256.         $user $token->getUser()->getUser();
  257.         // set user language
  258.         $request->setLocale($user->getLanguage());
  259.         $this->translator->setLocale($user->getLanguage());
  260.         // set user on runtime cache for legacy compatibility
  261.         Runtime::set('pimcore_admin_user'$user);
  262.         if ($user->isAdmin()) {
  263.             if (Admin::isMaintenanceModeScheduledForLogin()) {
  264.                 Admin::activateMaintenanceMode(Session::getSessionId());
  265.                 Admin::unscheduleMaintenanceModeOnLogin();
  266.             }
  267.         }
  268.         // as we authenticate statelessly (short lived sessions) the authentication is called for
  269.         // every request. therefore we only redirect if we're on the login page
  270.         if (!in_array($request->attributes->get('_route'), [
  271.             'pimcore_admin_login',
  272.             'pimcore_admin_login_check',
  273.         ])) {
  274.             return null;
  275.         }
  276.         $url null;
  277.         if ($request->get('deeplink') && $request->get('deeplink') !== 'true') {
  278.             $url $this->router->generate('pimcore_admin_login_deeplink');
  279.             $url .= '?' $request->get('deeplink');
  280.         } else {
  281.             $url $this->router->generate('pimcore_admin_index', [
  282.                 '_dc' => time(),
  283.                 'perspective' => strip_tags($request->get('perspective')),
  284.             ]);
  285.         }
  286.         if ($url) {
  287.             $response = new RedirectResponse($url);
  288.             $response->headers->setCookie(new Cookie('pimcore_admin_sid'true0'/'nullfalsetrue));
  289.             return $response;
  290.         }
  291.         return null;
  292.     }
  293.     /**
  294.      * {@inheritdoc}
  295.      */
  296.     public function supportsRememberMe()
  297.     {
  298.         return false;
  299.     }
  300.     public function createAuthenticatedToken(UserInterface $user$providerKey)
  301.     {
  302.         if ($this->twoFactorRequired) {
  303.             return new TwoFactorRequiredToken(
  304.                 $user,
  305.                 $providerKey,
  306.                 $user->getRoles()
  307.             );
  308.         } else {
  309.             return parent::createAuthenticatedToken($user$providerKey);
  310.         }
  311.     }
  312. }