/src/Symfony/Component/Security/Http/Firewall/ContextListener.php

https://github.com/Exercise/symfony · PHP · 161 lines · 103 code · 26 blank · 32 comment · 19 complexity · 4dee920244aa0e05533d70d8522765f3 MD5 · raw file

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