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

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