ContextListener.php 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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\Firewall;
  11. use Symfony\Component\HttpKernel\HttpKernelInterface;
  12. use Symfony\Component\HttpKernel\Log\LoggerInterface;
  13. use Symfony\Component\HttpKernel\Event\GetResponseEvent;
  14. use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
  15. use Symfony\Component\HttpKernel\KernelEvents;
  16. use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
  17. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  18. use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
  19. use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
  20. use Symfony\Component\Security\Core\SecurityContextInterface;
  21. use Symfony\Component\Security\Core\User\UserInterface;
  22. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  23. /**
  24. * ContextListener manages the SecurityContext persistence through a session.
  25. *
  26. * @author Fabien Potencier <fabien@symfony.com>
  27. * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  28. */
  29. class ContextListener implements ListenerInterface
  30. {
  31. private $context;
  32. private $contextKey;
  33. private $logger;
  34. private $userProviders;
  35. public function __construct(SecurityContextInterface $context, array $userProviders, $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null)
  36. {
  37. if (empty($contextKey)) {
  38. throw new \InvalidArgumentException('$contextKey must not be empty.');
  39. }
  40. $this->context = $context;
  41. $this->userProviders = $userProviders;
  42. $this->contextKey = $contextKey;
  43. $this->logger = $logger;
  44. if (null !== $dispatcher) {
  45. $dispatcher->addListener(KernelEvents::RESPONSE, array($this, 'onKernelResponse'));
  46. }
  47. }
  48. /**
  49. * Reads the SecurityContext from the session.
  50. *
  51. * @param GetResponseEvent $event A GetResponseEvent instance
  52. */
  53. public function handle(GetResponseEvent $event)
  54. {
  55. $request = $event->getRequest();
  56. $session = $request->hasPreviousSession() ? $request->getSession() : null;
  57. if (null === $session || null === $token = $session->get('_security_'.$this->contextKey)) {
  58. $this->context->setToken(null);
  59. } else {
  60. if (null !== $this->logger) {
  61. $this->logger->debug('Read SecurityContext from the session');
  62. }
  63. $token = unserialize($token);
  64. if (null !== $token) {
  65. $token = $this->refreshUser($token);
  66. }
  67. $this->context->setToken($token);
  68. }
  69. }
  70. /**
  71. * Writes the SecurityContext to the session.
  72. *
  73. * @param FilterResponseEvent $event A FilterResponseEvent instance
  74. */
  75. public function onKernelResponse(FilterResponseEvent $event)
  76. {
  77. if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
  78. return;
  79. }
  80. if (!$event->getRequest()->hasSession()) {
  81. return;
  82. }
  83. if (null === $token = $this->context->getToken()) {
  84. return;
  85. }
  86. if (null === $token || $token instanceof AnonymousToken) {
  87. return;
  88. }
  89. if (null !== $this->logger) {
  90. $this->logger->debug('Write SecurityContext in the session');
  91. }
  92. $event->getRequest()->getSession()->set('_security_'.$this->contextKey, serialize($token));
  93. }
  94. /**
  95. * Refreshes the user by reloading it from the user provider
  96. *
  97. * @param TokenInterface $token
  98. *
  99. * @return TokenInterface|null
  100. */
  101. private function refreshUser(TokenInterface $token)
  102. {
  103. $user = $token->getUser();
  104. if (!$user instanceof UserInterface) {
  105. return $token;
  106. }
  107. if (null !== $this->logger) {
  108. $this->logger->debug(sprintf('Reloading user from user provider.'));
  109. }
  110. foreach ($this->userProviders as $provider) {
  111. try {
  112. $token->setUser($provider->refreshUser($user));
  113. if (null !== $this->logger) {
  114. $this->logger->debug(sprintf('Username "%s" was reloaded from user provider.', $user->getUsername()));
  115. }
  116. return $token;
  117. } catch (UnsupportedUserException $unsupported) {
  118. // let's try the next user provider
  119. } catch (UsernameNotFoundException $notFound) {
  120. if (null !== $this->logger) {
  121. $this->logger->warn(sprintf('Username "%s" could not be found.', $user->getUsername()));
  122. }
  123. return null;
  124. }
  125. }
  126. throw new \RuntimeException(sprintf('There is no user provider for user "%s".', get_class($user)));
  127. }
  128. }