context listener: hardening user provider handling
This commit is contained in:
parent
5898ec2c5e
commit
0fb09293fd
@ -150,6 +150,8 @@ class ContextListener implements ListenerInterface
|
|||||||
return $token;
|
return $token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$userNotFoundByProvider = false;
|
||||||
|
|
||||||
foreach ($this->userProviders as $provider) {
|
foreach ($this->userProviders as $provider) {
|
||||||
try {
|
try {
|
||||||
$refreshedUser = $provider->refreshUser($user);
|
$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)));
|
$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)));
|
throw new \RuntimeException(sprintf('There is no user provider for user "%s".', get_class($user)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,17 @@ use Symfony\Component\HttpFoundation\Response;
|
|||||||
use Symfony\Component\HttpFoundation\Session\Session;
|
use Symfony\Component\HttpFoundation\Session\Session;
|
||||||
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
||||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
||||||
|
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||||
use Symfony\Component\HttpKernel\KernelEvents;
|
use Symfony\Component\HttpKernel\KernelEvents;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
|
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\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\Security\Http\Firewall\ContextListener;
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||||
|
|
||||||
@ -238,6 +245,40 @@ class ContextListenerTest extends TestCase
|
|||||||
$listener->handle($event);
|
$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)
|
protected function runSessionOnKernelResponse($newToken, $original = null)
|
||||||
{
|
{
|
||||||
$session = new Session(new MockArraySessionStorage());
|
$session = new Session(new MockArraySessionStorage());
|
||||||
@ -265,4 +306,67 @@ class ContextListenerTest extends TestCase
|
|||||||
|
|
||||||
return $session;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user