context listener: hardening user provider handling

This commit is contained in:
Christian Flothmann 2017-03-04 12:27:34 +01:00
parent 5898ec2c5e
commit 0fb09293fd
2 changed files with 111 additions and 1 deletions

View File

@ -150,6 +150,8 @@ class ContextListener implements ListenerInterface
return $token;
}
$userNotFoundByProvider = false;
foreach ($this->userProviders as $provider) {
try {
$refreshedUser = $provider->refreshUser($user);
@ -167,10 +169,14 @@ class ContextListener implements ListenerInterface
$this->logger->warning('Username could not be found in the selected user provider.', array('username' => $e->getUsername(), 'provider' => get_class($provider)));
}
return;
$userNotFoundByProvider = true;
}
}
if ($userNotFoundByProvider) {
return;
}
throw new \RuntimeException(sprintf('There is no user provider for user "%s".', get_class($user)));
}
}

View File

@ -17,10 +17,17 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\User;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Firewall\ContextListener;
use Symfony\Component\EventDispatcher\EventDispatcher;
@ -238,6 +245,40 @@ class ContextListenerTest extends TestCase
$listener->handle($event);
}
public function testTryAllUserProvidersUntilASupportingUserProviderIsFound()
{
$tokenStorage = new TokenStorage();
$refreshedUser = new User('foobar', 'baz');
$this->handleEventWithPreviousSession($tokenStorage, array(new NotSupportingUserProvider(), new SupportingUserProvider($refreshedUser)));
$this->assertSame($refreshedUser, $tokenStorage->getToken()->getUser());
}
public function testNextSupportingUserProviderIsTriedIfPreviousSupportingUserProviderDidNotLoadTheUser()
{
$tokenStorage = new TokenStorage();
$refreshedUser = new User('foobar', 'baz');
$this->handleEventWithPreviousSession($tokenStorage, array(new SupportingUserProvider(), new SupportingUserProvider($refreshedUser)));
$this->assertSame($refreshedUser, $tokenStorage->getToken()->getUser());
}
public function testTokenIsSetToNullIfNoUserWasLoadedByTheRegisteredUserProviders()
{
$tokenStorage = new TokenStorage();
$this->handleEventWithPreviousSession($tokenStorage, array(new NotSupportingUserProvider(), new SupportingUserProvider()));
$this->assertNull($tokenStorage->getToken());
}
/**
* @expectedException \RuntimeException
*/
public function testRuntimeExceptionIsThrownIfNoSupportingUserProviderWasRegistered()
{
$this->handleEventWithPreviousSession(new TokenStorage(), array(new NotSupportingUserProvider(), new NotSupportingUserProvider()));
}
protected function runSessionOnKernelResponse($newToken, $original = null)
{
$session = new Session(new MockArraySessionStorage());
@ -265,4 +306,67 @@ class ContextListenerTest extends TestCase
return $session;
}
private function handleEventWithPreviousSession(TokenStorageInterface $tokenStorage, array $userProviders)
{
$session = new Session(new MockArraySessionStorage());
$session->set('_security_context_key', serialize(new UsernamePasswordToken(new User('foo', 'bar'), '', 'context_key')));
$request = new Request();
$request->setSession($session);
$request->cookies->set('MOCKSESSID', true);
$listener = new ContextListener($tokenStorage, $userProviders, 'context_key');
$listener->handle(new GetResponseEvent($this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), $request, HttpKernelInterface::MASTER_REQUEST));
}
}
class NotSupportingUserProvider implements UserProviderInterface
{
public function loadUserByUsername($username)
{
throw new UsernameNotFoundException();
}
public function refreshUser(UserInterface $user)
{
throw new UnsupportedUserException();
}
public function supportsClass($class)
{
return false;
}
}
class SupportingUserProvider implements UserProviderInterface
{
private $refreshedUser;
public function __construct(User $refreshedUser = null)
{
$this->refreshedUser = $refreshedUser;
}
public function loadUserByUsername($username)
{
}
public function refreshUser(UserInterface $user)
{
if (!$user instanceof User) {
throw new UnsupportedUserException();
}
if (null === $this->refreshedUser) {
throw new UsernameNotFoundException();
}
return $this->refreshedUser;
}
public function supportsClass($class)
{
return 'Symfony\Component\Security\Core\User\User' === $class;
}
}