Skip to content
This repository has been archived by the owner on Apr 29, 2024. It is now read-only.

Commit

Permalink
[Security] Lazy load user providers
Browse files Browse the repository at this point in the history
  • Loading branch information
chalasr committed Jul 3, 2017
1 parent 27e4db1 commit 6c69687
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 14 deletions.
20 changes: 20 additions & 0 deletions Core/Tests/User/ChainUserProviderTest.php
Expand Up @@ -172,6 +172,26 @@ public function testSupportsClassWhenNotSupported()
$this->assertFalse($provider->supportsClass('foo'));
}

public function testAcceptsTraversable()
{
$provider1 = $this->getProvider();
$provider1
->expects($this->once())
->method('refreshUser')
->will($this->throwException(new UnsupportedUserException('unsupported')))
;

$provider2 = $this->getProvider();
$provider2
->expects($this->once())
->method('refreshUser')
->will($this->returnValue($account = $this->getAccount()))
;

$provider = new ChainUserProvider(new \ArrayObject(array($provider1, $provider2)));
$this->assertSame($account, $provider->refreshUser($this->getAccount()));
}

protected function getAccount()
{
return $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock();
Expand Down
9 changes: 8 additions & 1 deletion Core/User/ChainUserProvider.php
Expand Up @@ -26,7 +26,10 @@ class ChainUserProvider implements UserProviderInterface
{
private $providers;

public function __construct(array $providers)
/**
* @param iterable|UserProviderInterface[] $providers
*/
public function __construct($providers)
{
$this->providers = $providers;
}
Expand All @@ -36,6 +39,10 @@ public function __construct(array $providers)
*/
public function getProviders()
{
if ($this->providers instanceof \Traversable) {
return iterator_to_array($this->providers);
}

return $this->providers;
}

Expand Down
20 changes: 13 additions & 7 deletions Http/Firewall/ContextListener.php
Expand Up @@ -44,18 +44,20 @@ class ContextListener implements ListenerInterface
private $registered;
private $trustResolver;

public function __construct(TokenStorageInterface $tokenStorage, array $userProviders, $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, AuthenticationTrustResolverInterface $trustResolver = null)
/**
* @param TokenStorageInterface $tokenStorage
* @param iterable|UserProviderInterface[] $userProviders
* @param string $contextKey
* @param LoggerInterface|null $logger
* @param EventDispatcherInterface|null $dispatcher
* @param AuthenticationTrustResolverInterface|null $trustResolver
*/
public function __construct(TokenStorageInterface $tokenStorage, $userProviders, $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, AuthenticationTrustResolverInterface $trustResolver = null)
{
if (empty($contextKey)) {
throw new \InvalidArgumentException('$contextKey must not be empty.');
}

foreach ($userProviders as $userProvider) {
if (!$userProvider instanceof UserProviderInterface) {
throw new \InvalidArgumentException(sprintf('User provider "%s" must implement "Symfony\Component\Security\Core\User\UserProviderInterface".', get_class($userProvider)));
}
}

$this->tokenStorage = $tokenStorage;
$this->userProviders = $userProviders;
$this->contextKey = $contextKey;
Expand Down Expand Up @@ -158,6 +160,10 @@ protected function refreshUser(TokenInterface $token)
$userNotFoundByProvider = false;

foreach ($this->userProviders as $provider) {
if (!$provider instanceof UserProviderInterface) {
throw new \InvalidArgumentException(sprintf('User provider "%s" must implement "%s".', get_class($provider), UserProviderInterface::class));
}

try {
$refreshedUser = $provider->refreshUser($user);
$token->setUser($refreshedUser);
Expand Down
17 changes: 11 additions & 6 deletions Http/Tests/Firewall/ContextListenerTest.php
Expand Up @@ -53,11 +53,7 @@ public function testItRequiresContextKey()
*/
public function testUserProvidersNeedToImplementAnInterface()
{
new ContextListener(
$this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock(),
array(new \stdClass()),
'key123'
);
$this->handleEventWithPreviousSession(new TokenStorage(), array(new \stdClass()));
}

public function testOnKernelResponseWillAddSession()
Expand Down Expand Up @@ -287,6 +283,15 @@ public function testRuntimeExceptionIsThrownIfNoSupportingUserProviderWasRegiste
$this->handleEventWithPreviousSession(new TokenStorage(), array(new NotSupportingUserProvider(), new NotSupportingUserProvider()));
}

public function testAcceptsProvidersAsTraversable()
{
$tokenStorage = new TokenStorage();
$refreshedUser = new User('foobar', 'baz');
$this->handleEventWithPreviousSession($tokenStorage, new \ArrayObject(array(new NotSupportingUserProvider(), new SupportingUserProvider($refreshedUser))));

$this->assertSame($refreshedUser, $tokenStorage->getToken()->getUser());
}

protected function runSessionOnKernelResponse($newToken, $original = null)
{
$session = new Session(new MockArraySessionStorage());
Expand Down Expand Up @@ -315,7 +320,7 @@ protected function runSessionOnKernelResponse($newToken, $original = null)
return $session;
}

private function handleEventWithPreviousSession(TokenStorageInterface $tokenStorage, array $userProviders)
private function handleEventWithPreviousSession(TokenStorageInterface $tokenStorage, $userProviders)
{
$session = new Session(new MockArraySessionStorage());
$session->set('_security_context_key', serialize(new UsernamePasswordToken(new User('foo', 'bar'), '', 'context_key')));
Expand Down

0 comments on commit 6c69687

Please sign in to comment.