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

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.     public function isFrontendFirewall(): bool
  117.     {
  118.         return self::FRONTEND_FIREWALL === $this->getFirewallContext();
  119.     }
  120.     public function isBackendFirewall(): bool
  121.     {
  122.         return self::BACKEND_FIREWALL === $this->getFirewallContext();
  123.     }
  124.     private function getToken(string $context): ?TokenInterface
  125.     {
  126.         $token $this->getTokenFromStorage($context);
  127.         if (null === $token) {
  128.             $token $this->getTokenFromSession('_security_'.$context);
  129.         }
  130.         if (!$token instanceof TokenInterface || !$token->isAuthenticated()) {
  131.             return null;
  132.         }
  133.         if ($this->trustResolver->isAnonymous($token)) {
  134.             return null;
  135.         }
  136.         return $token;
  137.     }
  138.     private function getTokenFromStorage(string $context): ?TokenInterface
  139.     {
  140.         if ($this->getFirewallContext() !== $context) {
  141.             return null;
  142.         }
  143.         return $this->tokenStorage->getToken();
  144.     }
  145.     private function getFirewallContext(): ?string
  146.     {
  147.         $request $this->requestStack->getMainRequest();
  148.         if (!$this->firewallMap instanceof FirewallMap || null === $request) {
  149.             return null;
  150.         }
  151.         $config $this->firewallMap->getFirewallConfig($request);
  152.         if (!$config instanceof FirewallConfig) {
  153.             return null;
  154.         }
  155.         return $config->getContext();
  156.     }
  157.     private function getTokenFromSession(string $sessionKey): ?TokenInterface
  158.     {
  159.         if (!$this->session->isStarted()) {
  160.             $request $this->requestStack->getMainRequest();
  161.             if (!$request || !$request->hasPreviousSession()) {
  162.                 return null;
  163.             }
  164.         }
  165.         // This will start the session if Request::hasPreviousSession() was true
  166.         if (!$this->session->has($sessionKey)) {
  167.             return null;
  168.         }
  169.         $token unserialize($this->session->get($sessionKey), ['allowed_classes' => true]);
  170.         if (!$token instanceof TokenInterface) {
  171.             return null;
  172.         }
  173.         return $token;
  174.     }
  175.     private function isValidPreviewLink(FrontendPreviewToken $token): bool
  176.     {
  177.         $id $token->getPreviewLinkId();
  178.         if (!isset($this->previewLinks[$id])) {
  179.             $this->previewLinks[$id] = $this->connection->fetchAssociative(
  180.                 "
  181.                     SELECT
  182.                         url,
  183.                         showUnpublished,
  184.                         restrictToUrl
  185.                     FROM tl_preview_link
  186.                     WHERE
  187.                         id = :id
  188.                         AND published = '1'
  189.                         AND expiresAt > UNIX_TIMESTAMP()
  190.                 ",
  191.                 ['id' => $id],
  192.             );
  193.         }
  194.         $previewLink $this->previewLinks[$id];
  195.         if (!$previewLink) {
  196.             return false;
  197.         }
  198.         if ((bool) $previewLink['showUnpublished'] !== $token->showUnpublished()) {
  199.             return false;
  200.         }
  201.         if (!$previewLink['restrictToUrl']) {
  202.             return true;
  203.         }
  204.         $request $this->requestStack->getMainRequest();
  205.         return $request && strtok($request->getUri(), '?') === strtok(Request::create($previewLink['url'])->getUri(), '?');
  206.     }
  207. }