AbstractPreAuthenticatedAuthenticator.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Security\Http\Authenticator;
  11. use Psr\Log\LoggerInterface;
  12. use Symfony\Component\HttpFoundation\Request;
  13. use Symfony\Component\HttpFoundation\Response;
  14. use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
  15. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  16. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  17. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  18. use Symfony\Component\Security\Core\Exception\BadCredentialsException;
  19. use Symfony\Component\Security\Core\User\UserProviderInterface;
  20. use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PreAuthenticatedUserBadge;
  21. use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
  22. use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
  23. use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
  24. /**
  25. * The base authenticator for authenticators to use pre-authenticated
  26. * requests (e.g. using certificates).
  27. *
  28. * @author Wouter de Jong <wouter@wouterj.nl>
  29. * @author Fabien Potencier <fabien@symfony.com>
  30. *
  31. * @internal
  32. * @experimental in 5.2
  33. */
  34. abstract class AbstractPreAuthenticatedAuthenticator implements InteractiveAuthenticatorInterface
  35. {
  36. private $userProvider;
  37. private $tokenStorage;
  38. private $firewallName;
  39. private $logger;
  40. public function __construct(UserProviderInterface $userProvider, TokenStorageInterface $tokenStorage, string $firewallName, ?LoggerInterface $logger = null)
  41. {
  42. $this->userProvider = $userProvider;
  43. $this->tokenStorage = $tokenStorage;
  44. $this->firewallName = $firewallName;
  45. $this->logger = $logger;
  46. }
  47. /**
  48. * Returns the username of the pre-authenticated user.
  49. *
  50. * This authenticator is skipped if null is returned or a custom
  51. * BadCredentialsException is thrown.
  52. */
  53. abstract protected function extractUsername(Request $request): ?string;
  54. public function supports(Request $request): ?bool
  55. {
  56. try {
  57. $username = $this->extractUsername($request);
  58. } catch (BadCredentialsException $e) {
  59. $this->clearToken($e);
  60. if (null !== $this->logger) {
  61. $this->logger->debug('Skipping pre-authenticated authenticator as a BadCredentialsException is thrown.', ['exception' => $e, 'authenticator' => static::class]);
  62. }
  63. return false;
  64. }
  65. if (null === $username) {
  66. if (null !== $this->logger) {
  67. $this->logger->debug('Skipping pre-authenticated authenticator no username could be extracted.', ['authenticator' => static::class]);
  68. }
  69. return false;
  70. }
  71. $request->attributes->set('_pre_authenticated_username', $username);
  72. return true;
  73. }
  74. public function authenticate(Request $request): PassportInterface
  75. {
  76. return new SelfValidatingPassport(new UserBadge($request->attributes->get('_pre_authenticated_username'), function ($username) {
  77. return $this->userProvider->loadUserByUsername($username);
  78. }), [new PreAuthenticatedUserBadge()]);
  79. }
  80. public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface
  81. {
  82. return new PreAuthenticatedToken($passport->getUser(), null, $firewallName, $passport->getUser()->getRoles());
  83. }
  84. public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
  85. {
  86. return null; // let the original request continue
  87. }
  88. public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
  89. {
  90. $this->clearToken($exception);
  91. return null;
  92. }
  93. public function isInteractive(): bool
  94. {
  95. return true;
  96. }
  97. private function clearToken(AuthenticationException $exception): void
  98. {
  99. $token = $this->tokenStorage->getToken();
  100. if ($token instanceof PreAuthenticatedToken && $this->firewallName === $token->getFirewallName()) {
  101. $this->tokenStorage->setToken(null);
  102. if (null !== $this->logger) {
  103. $this->logger->info('Cleared pre-authenticated token due to an exception.', ['exception' => $exception]);
  104. }
  105. }
  106. }
  107. }