vendor/contao/core-bundle/src/Security/Authentication/Token/TokenChecker.php line 151

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4.  * This file is part of Contao.
  5.  *
  6.  * (c) Leo Feyer
  7.  *
  8.  * @license LGPL-3.0-or-later
  9.  */
  10. namespace Contao\CoreBundle\Security\Authentication\Token;
  11. use Contao\BackendUser;
  12. use Contao\FrontendUser;
  13. use Doctrine\DBAL\Connection;
  14. use Symfony\Bundle\SecurityBundle\Security\FirewallConfig;
  15. use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
  16. use Symfony\Component\HttpFoundation\Request;
  17. use Symfony\Component\HttpFoundation\RequestStack;
  18. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  19. use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
  20. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  21. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  22. use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
  23. use Symfony\Component\Security\Http\FirewallMapInterface;
  24. class TokenChecker
  25. {
  26.     private const FRONTEND_FIREWALL 'contao_frontend';
  27.     private const BACKEND_FIREWALL 'contao_backend';
  28.     private RequestStack $requestStack;
  29.     private FirewallMapInterface $firewallMap;
  30.     private TokenStorageInterface $tokenStorage;
  31.     private SessionInterface $session;
  32.     private AuthenticationTrustResolverInterface $trustResolver;
  33.     private VoterInterface $roleVoter;
  34.     private Connection $connection;
  35.     private array $previewLinks;
  36.     /**
  37.      * @internal
  38.      */
  39.     public function __construct(RequestStack $requestStackFirewallMapInterface $firewallMapTokenStorageInterface $tokenStorageSessionInterface $sessionAuthenticationTrustResolverInterface $trustResolverVoterInterface $roleVoterConnection $connection)
  40.     {
  41.         $this->requestStack $requestStack;
  42.         $this->firewallMap $firewallMap;
  43.         $this->tokenStorage $tokenStorage;
  44.         $this->session $session;
  45.         $this->trustResolver $trustResolver;
  46.         $this->roleVoter $roleVoter;
  47.         $this->connection $connection;
  48.     }
  49.     /**
  50.      * Checks if a front end user is authenticated.
  51.      */
  52.     public function hasFrontendUser(): bool
  53.     {
  54.         $token $this->getToken(self::FRONTEND_FIREWALL);
  55.         return null !== $token && VoterInterface::ACCESS_GRANTED === $this->roleVoter->vote($tokennull, ['ROLE_MEMBER']);
  56.     }
  57.     /**
  58.      * Checks if a back end user is authenticated.
  59.      */
  60.     public function hasBackendUser(): bool
  61.     {
  62.         $token $this->getToken(self::BACKEND_FIREWALL);
  63.         return null !== $token && VoterInterface::ACCESS_GRANTED === $this->roleVoter->vote($tokennull, ['ROLE_USER']);
  64.     }
  65.     /**
  66.      * Gets the front end username from the session.
  67.      */
  68.     public function getFrontendUsername(): ?string
  69.     {
  70.         $token $this->getToken(self::FRONTEND_FIREWALL);
  71.         if (null === $token || !$token->getUser() instanceof FrontendUser) {
  72.             return null;
  73.         }
  74.         return $token->getUser()->getUserIdentifier();
  75.     }
  76.     /**
  77.      * Gets the back end username from the session.
  78.      */
  79.     public function getBackendUsername(): ?string
  80.     {
  81.         $token $this->getToken(self::BACKEND_FIREWALL);
  82.         if (null === $token || !$token->getUser() instanceof BackendUser) {
  83.             return null;
  84.         }
  85.         return $token->getUser()->getUserIdentifier();
  86.     }
  87.     /**
  88.      * Tells whether the front end preview can be accessed.
  89.      */
  90.     public function canAccessPreview(): bool
  91.     {
  92.         if ($this->hasBackendUser()) {
  93.             return true;
  94.         }
  95.         $token $this->getToken(self::FRONTEND_FIREWALL);
  96.         if (!$token instanceof FrontendPreviewToken) {
  97.             return false;
  98.         }
  99.         if (null === $token->getPreviewLinkId()) {
  100.             return false;
  101.         }
  102.         return $this->isValidPreviewLink($token);
  103.     }
  104.     /**
  105.      * Tells whether the front end preview can show unpublished fragments.
  106.      */
  107.     public function isPreviewMode(): bool
  108.     {
  109.         $request $this->requestStack->getMainRequest();
  110.         if (null === $request || !$request->attributes->get('_preview'false) || !$this->canAccessPreview()) {
  111.             return false;
  112.         }
  113.         $token $this->getToken(self::FRONTEND_FIREWALL);
  114.         return $token instanceof FrontendPreviewToken && $token->showUnpublished();
  115.     }
  116.     private function getToken(string $context): ?TokenInterface
  117.     {
  118.         $token $this->getTokenFromStorage($context);
  119.         if (null === $token) {
  120.             $token $this->getTokenFromSession('_security_'.$context);
  121.         }
  122.         if (!$token instanceof TokenInterface || !$token->isAuthenticated()) {
  123.             return null;
  124.         }
  125.         if ($this->trustResolver->isAnonymous($token)) {
  126.             return null;
  127.         }
  128.         return $token;
  129.     }
  130.     private function getTokenFromStorage(string $context): ?TokenInterface
  131.     {
  132.         $request $this->requestStack->getMainRequest();
  133.         if (!$this->firewallMap instanceof FirewallMap || null === $request) {
  134.             return null;
  135.         }
  136.         $config $this->firewallMap->getFirewallConfig($request);
  137.         if (!$config instanceof FirewallConfig || $config->getContext() !== $context) {
  138.             return null;
  139.         }
  140.         return $this->tokenStorage->getToken();
  141.     }
  142.     private function getTokenFromSession(string $sessionKey): ?TokenInterface
  143.     {
  144.         if (!$this->session->isStarted()) {
  145.             $request $this->requestStack->getMainRequest();
  146.             if (!$request || !$request->hasPreviousSession()) {
  147.                 return null;
  148.             }
  149.         }
  150.         // This will start the session if Request::hasPreviousSession() was true
  151.         if (!$this->session->has($sessionKey)) {
  152.             return null;
  153.         }
  154.         $token unserialize($this->session->get($sessionKey), ['allowed_classes' => true]);
  155.         if (!$token instanceof TokenInterface) {
  156.             return null;
  157.         }
  158.         return $token;
  159.     }
  160.     private function isValidPreviewLink(FrontendPreviewToken $token): bool
  161.     {
  162.         $id $token->getPreviewLinkId();
  163.         if (!isset($this->previewLinks[$id])) {
  164.             $this->previewLinks[$id] = $this->connection->fetchAssociative(
  165.                 "
  166.                     SELECT
  167.                         url,
  168.                         showUnpublished,
  169.                         restrictToUrl
  170.                     FROM tl_preview_link
  171.                     WHERE
  172.                         id = :id
  173.                         AND published = '1'
  174.                         AND expiresAt > UNIX_TIMESTAMP()
  175.                 ",
  176.                 ['id' => $id],
  177.             );
  178.         }
  179.         $previewLink $this->previewLinks[$id];
  180.         if (!$previewLink) {
  181.             return false;
  182.         }
  183.         if ((bool) $previewLink['showUnpublished'] !== $token->showUnpublished()) {
  184.             return false;
  185.         }
  186.         if (!$previewLink['restrictToUrl']) {
  187.             return true;
  188.         }
  189.         $request $this->requestStack->getMainRequest();
  190.         return $request && strtok($request->getUri(), '?') === strtok(Request::create($previewLink['url'])->getUri(), '?');
  191.     }
  192. }