Skip to content

Commit e52986c

Browse files
Robin Chalaschalasr
authored andcommitted
[Security] Lazy load user providers
1 parent 5a3abf5 commit e52986c

File tree

6 files changed

+56
-26
lines changed

6 files changed

+56
-26
lines changed

src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ private function createFirewalls($config, ContainerBuilder $container)
245245
foreach ($providerIds as $userProviderId) {
246246
$userProviders[] = new Reference($userProviderId);
247247
}
248-
$arguments[1] = $userProviders;
248+
$arguments[1] = new IteratorArgument($userProviders);
249249
$definition->setArguments($arguments);
250250

251251
$customUserChecker = false;
@@ -613,7 +613,7 @@ private function createUserDaoProvider($name, $provider, ContainerBuilder $conta
613613

614614
$container
615615
->setDefinition($name, new ChildDefinition('security.user.provider.chain'))
616-
->addArgument($providers);
616+
->addArgument(new IteratorArgument($providers));
617617

618618
return $name;
619619
}

src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
1516
use Symfony\Component\DependencyInjection\Reference;
1617
use Symfony\Bundle\SecurityBundle\SecurityBundle;
1718
use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension;
@@ -57,10 +58,10 @@ public function testUserProviders()
5758
$this->assertEquals(array(), array_diff($providers, $expectedProviders));
5859

5960
// chain provider
60-
$this->assertEquals(array(array(
61+
$this->assertEquals(array(new IteratorArgument(array(
6162
new Reference('security.user.provider.concrete.service'),
6263
new Reference('security.user.provider.concrete.basic'),
63-
)), $container->getDefinition('security.user.provider.concrete.chain')->getArguments());
64+
))), $container->getDefinition('security.user.provider.concrete.chain')->getArguments());
6465
}
6566

6667
public function testFirewalls()

src/Symfony/Component/Security/Core/Tests/User/ChainUserProviderTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,26 @@ public function testSupportsClassWhenNotSupported()
172172
$this->assertFalse($provider->supportsClass('foo'));
173173
}
174174

175+
public function testAcceptsTraversable()
176+
{
177+
$provider1 = $this->getProvider();
178+
$provider1
179+
->expects($this->once())
180+
->method('refreshUser')
181+
->will($this->throwException(new UnsupportedUserException('unsupported')))
182+
;
183+
184+
$provider2 = $this->getProvider();
185+
$provider2
186+
->expects($this->once())
187+
->method('refreshUser')
188+
->will($this->returnValue($account = $this->getAccount()))
189+
;
190+
191+
$provider = new ChainUserProvider(new \ArrayObject(array($provider1, $provider2)));
192+
$this->assertSame($account, $provider->refreshUser($this->getAccount()));
193+
}
194+
175195
protected function getAccount()
176196
{
177197
return $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock();

src/Symfony/Component/Security/Core/User/ChainUserProvider.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ class ChainUserProvider implements UserProviderInterface
2626
{
2727
private $providers;
2828

29-
public function __construct(array $providers)
29+
/**
30+
* @param iterable|UserProviderInterface[] $providers
31+
*/
32+
public function __construct($providers)
3033
{
3134
$this->providers = $providers;
3235
}
@@ -36,6 +39,10 @@ public function __construct(array $providers)
3639
*/
3740
public function getProviders()
3841
{
42+
if ($this->providers instanceof \Traversable) {
43+
return iterator_to_array($this->providers);
44+
}
45+
3946
return $this->providers;
4047
}
4148

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

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,20 @@ class ContextListener implements ListenerInterface
4444
private $registered;
4545
private $trustResolver;
4646

47-
public function __construct(TokenStorageInterface $tokenStorage, array $userProviders, $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, AuthenticationTrustResolverInterface $trustResolver = null)
47+
/**
48+
* @param TokenStorageInterface $tokenStorage
49+
* @param iterable|UserProviderInterface[] $userProviders
50+
* @param string $contextKey
51+
* @param LoggerInterface|null $logger
52+
* @param EventDispatcherInterface|null $dispatcher
53+
* @param AuthenticationTrustResolverInterface|null $trustResolver
54+
*/
55+
public function __construct(TokenStorageInterface $tokenStorage, $userProviders, $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, AuthenticationTrustResolverInterface $trustResolver = null)
4856
{
4957
if (empty($contextKey)) {
5058
throw new \InvalidArgumentException('$contextKey must not be empty.');
5159
}
5260

53-
foreach ($userProviders as $userProvider) {
54-
if (!$userProvider instanceof UserProviderInterface) {
55-
throw new \InvalidArgumentException(sprintf('User provider "%s" must implement "Symfony\Component\Security\Core\User\UserProviderInterface".', get_class($userProvider)));
56-
}
57-
}
58-
5961
$this->tokenStorage = $tokenStorage;
6062
$this->userProviders = $userProviders;
6163
$this->contextKey = $contextKey;
@@ -158,6 +160,10 @@ protected function refreshUser(TokenInterface $token)
158160
$userNotFoundByProvider = false;
159161

160162
foreach ($this->userProviders as $provider) {
163+
if (!$provider instanceof UserProviderInterface) {
164+
throw new \InvalidArgumentException(sprintf('User provider "%s" must implement "Symfony\Component\Security\Core\User\UserProviderInterface".', get_class($userProvider)));
165+
}
166+
161167
try {
162168
$refreshedUser = $provider->refreshUser($user);
163169
$token->setUser($refreshedUser);

src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,6 @@ public function testItRequiresContextKey()
4747
);
4848
}
4949

50-
/**
51-
* @expectedException \InvalidArgumentException
52-
* @expectedExceptionMessage User provider "stdClass" must implement "Symfony\Component\Security\Core\User\UserProviderInterface
53-
*/
54-
public function testUserProvidersNeedToImplementAnInterface()
55-
{
56-
new ContextListener(
57-
$this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock(),
58-
array(new \stdClass()),
59-
'key123'
60-
);
61-
}
62-
6350
public function testOnKernelResponseWillAddSession()
6451
{
6552
$session = $this->runSessionOnKernelResponse(
@@ -287,6 +274,15 @@ public function testRuntimeExceptionIsThrownIfNoSupportingUserProviderWasRegiste
287274
$this->handleEventWithPreviousSession(new TokenStorage(), array(new NotSupportingUserProvider(), new NotSupportingUserProvider()));
288275
}
289276

277+
public function testAcceptsProvidersAsTraversable()
278+
{
279+
$tokenStorage = new TokenStorage();
280+
$refreshedUser = new User('foobar', 'baz');
281+
$this->handleEventWithPreviousSession($tokenStorage, new \ArrayObject(array(new NotSupportingUserProvider(), new SupportingUserProvider($refreshedUser))));
282+
283+
$this->assertSame($refreshedUser, $tokenStorage->getToken()->getUser());
284+
}
285+
290286
protected function runSessionOnKernelResponse($newToken, $original = null)
291287
{
292288
$session = new Session(new MockArraySessionStorage());
@@ -315,7 +311,7 @@ protected function runSessionOnKernelResponse($newToken, $original = null)
315311
return $session;
316312
}
317313

318-
private function handleEventWithPreviousSession(TokenStorageInterface $tokenStorage, array $userProviders)
314+
private function handleEventWithPreviousSession(TokenStorageInterface $tokenStorage, $userProviders)
319315
{
320316
$session = new Session(new MockArraySessionStorage());
321317
$session->set('_security_context_key', serialize(new UsernamePasswordToken(new User('foo', 'bar'), '', 'context_key')));

0 commit comments

Comments
 (0)