8000 Integrated GuardAuthenticationManager in the SecurityBundle · symfony/symfony@9b7fddd · GitHub
[go: up one dir, main page]

Skip to content

Commit 9b7fddd

Browse files
committed
Integrated GuardAuthenticationManager in the SecurityBundle
1 parent a6890db commit 9b7fddd

File tree

8 files changed

+187
-23
lines changed

8 files changed

+187
-23
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ public function getConfigTreeBuilder()
7373
->booleanNode('hide_user_not_found')->defaultTrue()->end()
7474
->booleanNode('always_authenticate_before_granting')->defaultFalse()->end()
7575
->booleanNode('erase_credentials')->defaultTrue()->end()
76+
->booleanNode('guard_authentication_manager')->defaultFalse()->end()
7677
->arrayNode('access_decision_manager')
7778
->addDefaultsIfNotSet()
7879
->children()
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
13+
14+
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
15+
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
16+
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
18+
class CustomAuthenticatorFactory implements AuthenticatorFactoryInterface, SecurityFactoryInterface
19+
{
20+
public function create(ContainerBuilder $container, string $id, array $config, string $userProvider, ?string $defaultEntryPoint)
21+
{
22+
throw new \LogicException('Custom authenticators are not supported when "security.enable_authenticator_manager" is not set to true.');
23+
}
24+
25+
public function getPosition(): string
26+
{
27+
return 'pre_auth';
28+
}
29+
30+
public function getKey(): string
31+
{
32+
return 'custom_authenticator';
33+
}
34+
35+
/**
36+
* @param ArrayNodeDefinition $builder
37+
*/
38+
public function addConfiguration(NodeDefinition $builder)
39+
{
40+
$builder
41+
->fixXmlConfig('service')
42+
->children()
43+
->arrayNode('services')
44+
->info('An array of service ids for all of your "authenticators"')
45+
->requiresAtLeastOneElement()
46+
->prototype('scalar')->end()
47+
->end()
48+
->end()
49+
F438 ;
50+
}
51+
52+
public function createAuthenticator(ContainerBuilder $container, string $id, array $config, string $userProviderId): array
53+
{
54+
return $config['services'];
55+
}
56+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
13+
14+
use Symfony\Component\DependencyInjection\ContainerBuilder;
15+
16+
/**
17+
* @author Wouter de Jong <wouter@wouterj.nl>
18+
*/
19+
interface GuardFactoryInterface
20+
{
21+
/**
22+
* Creates the Guard service(s) for the provided configuration.
23+
*
24+
* @return string|string[] The Guard service ID(s) to be used by the firewall
25+
*/
26+
public function createGuard(ContainerBuilder $container, string $id, array $config, string $userProviderId);
27+
}

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
*
2222
* @author Fabien Potencier <fabien@symfony.com>
2323
*/
24-
class HttpBasicFactory implements SecurityFactoryInterface
24+
class HttpBasicFactory implements SecurityFactoryInterface, GuardFactoryInterface
2525
{
2626
public function create(ContainerBuilder $container, string $id, array $config, string $userProvider, ?string $defaultEntryPoint)
2727
{
@@ -46,6 +46,17 @@ public function create(ContainerBuilder $container, string $id, array $config, s
4646
return [$provider, $listenerId, $entryPointId];
4747
}
4848

49+
public function createGuard(ContainerBuilder $container, string $id, array $config, string $userProviderId): string
50+
{
51+
$authenticatorId = 'security.authenticator.http_basic.'.$id;
52+
$container
53+
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.http_basic'))
54+
->replaceArgument(0, $config['realm'])
55+
->replaceArgument(1, new Reference($userProviderId));
56+
57+
return $authenticatorId;
58+
}
59+
4960
public function getPosition()
5061
{
5162
return 'http';

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

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Bundle\SecurityBundle\DependencyInjection;
1313

14+
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\GuardFactoryInterface;
1415
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\RememberMeFactory;
1516
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
1617
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface;
@@ -52,6 +53,8 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
5253
private $userProviderFactories = [];
5354
private $statelessFirewallKeys = [];
5455

56+
private $guardAuthenticationManagerEnabled = false;
57+
5558
public function __construct()
5659
{
5760
foreach ($this->listenerPositions as $position) {
@@ -135,6 +138,8 @@ public function load(array $configs, ContainerBuilder $container)
135138
$container->setParameter('security.access.always_authenticate_before_granting', $config['always_authenticate_before_granting']);
136139
$container->setParameter('security.authentication.hide_user_not_found', $config['hide_user_not_found']);
137140

141+
$this->guardAuthenticationManagerEnabled = $config['guard_authentication_manager'];
142+
138143
$this->createFirewalls($config, $container);
139144
$this->createAuthorization($config, $container);
140145
$this->createRoleHierarchy($config, $container);
@@ -258,8 +263,13 @@ private function createFirewalls(array $config, ContainerBuilder $container)
258263
$authenticationProviders = array_map(function ($id) {
259264
return new Reference($id);
260265
}, array_values(array_unique($authenticationProviders)));
266+
$authenticationManagerId = 'security.authentication.manager.provider';
267+
if ($this->guardAuthenticationManagerEnabled) {
268+
$authenticationManagerId = 'security.authentication.manager.guard';
269+
$container->setAlias('security.authentication.manager', new Alias($authenticationManagerId));
270+
}
261271
$container
262-
->getDefinition('security.authentication.manager')
272+
->getDefinition($authenticationManagerId)
263273
->replaceArgument(0, new IteratorArgument($authenticationProviders))
264274
;
265275

@@ -467,31 +477,27 @@ private function createAuthenticationListeners(ContainerBuilder $container, stri
467477
$key = str_replace('-', '_', $factory->getKey());
468478

469479
if (isset($firewall[$key])) {
470-
if (isset($firewall[$key]['provider'])) {
471-
if (!isset($providerIds[$normalizedName = str_replace('-', '_', $firewall[$key]['provider'])])) {
472-
throw new InvalidConfigurationException(sprintf('Invalid firewall "%s": user provider "%s" not found.', $id, $firewall[$key]['provider']));
480+
$userProvider = $this->getUserProvider($container, $id, $firewall, $key, $defaultProvider, $providerIds, $contextListenerId);
481+
482+
if ($this->guardAuthenticationManagerEnabled) {
483+
if (!$factory instanceof GuardFactoryInterface) {
484+
throw new InvalidConfigurationException(sprintf('Cannot configure GuardAuthenticationManager as %s authentication does not support it, set security.guard_authentication_manager to `false`.', $key));
473485
}
474-
$userProvider = $providerIds[$normalizedName];
475-
} elseif ('remember_me' === $key || 'anonymous' === $key) {
476-
// RememberMeFactory will use the firewall secret when created, AnonymousAuthenticationListener does not load users.
477-
$userProvider = null;
478486

479-
if ('remember_me' === $key && $contextListenerId) {
480-
$container->getDefinition($contextListenerId)->addTag('security.remember_me_aware', ['id' => $id, 'provider' => 'none']);
487+
$authenticators = $factory->createGuard($container, $id, $firewall[$key], $userProvider);
488+
if (\is_array($authenticators)) {
489+
foreach ($authenticators as $i => $authenticator) {
490+
$authenticationProviders[$id.'_'.$key.$i] = $authenticator;
491+
}
492+
} else {
493+
$authenticationProviders[$id.'_'.$key] = $authenticators;
481494
}
482-
} elseif ($defaultProvider) {
483-
$userProvider = $defaultProvider;
484-
} elseif (empty($providerIds)) {
485-
$userProvider = sprintf('security.user.provider.missing.%s', $key);
486-
$container->setDefinition($userProvider, (new ChildDefinition('security.user.provider.missing'))->replaceArgument(0, $id));
487495
} else {
488-
throw new InvalidConfigurationException(sprintf('Not configuring explicitly the provider for the "%s" listener on "%s" firewall is ambiguous as there is more than one registered provider.', $key, $id));
489-
}
490-
491-
list($provider, $listenerId, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint);
496+
list($provider, $listenerId, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint);
492497

493-
$listeners[] = new Reference($listenerId);
494-
$authenticationProviders[] = $provider;
498+
$listeners[] = new Reference($listenerId);
499+
$authenticationProviders[] = $provider;
500+
}
495501
$hasListeners = true;
496502
}
497503
}
@@ -504,6 +510,42 @@ private function createAuthenticationListeners(ContainerBuilder $container, stri
504510
return [$listeners, $defaultEntryPoint];
505511
}
506512

513+
private function getUserProvider(ContainerBuilder $container, string $id, array $firewall, string $factoryKey, ?string $defaultProvider, array $providerIds, ?string $contextListenerId): ?string
514+
{
515+
if (isset($firewall[$factoryKey]['provider'])) {
516+
if (!isset($providerIds[$normalizedName = str_replace('-', '_', $firewall[$factoryKey]['provider'])])) {
517+
throw new InvalidConfigurationException(sprintf('Invalid firewall "%s": user provider "%s" not found.', $id, $firewall[$factoryKey]['provider']));
518+
}
519+
520+
return $providerIds[$normalizedName];
521+
}
522+
523+
if ('remember_me' === $factoryKey || 'anonymous' === $factoryKey) {
524+
if ('remember_me' === $factoryKey && $contextListenerId) {
525+
$container->getDefinition($contextListenerId)->addTag('security.remember_me_aware', ['id' => $id, 'provider' => 'none']);
526+
}
527+
528+
// RememberMeFactory will use the firewall secret when created
529+
return null;
530+
}
531+
532+
if ($defaultProvider) {
533+
return $defaultProvider;
534+
}
535+
536+
if (!$providerIds) {
537+
$userProvider = sprintf('security.user.provider.missing.%s', $factoryKey);
538+
$container->setDefinition(
539+
$userProvider,
540+
(new ChildDefinition('security.user.provider.missing'))->replaceArgument(0, $id)
541+
);
542+
543+
return $userProvider;
544+
}
545+
546+
throw new InvalidConfigurationException(sprintf('Not configuring explicitly the provider for the "%s" listener on "%s" firewall is ambiguous as there is more than one registered provider.', $factoryKey, $id));
547+
}
548+
507549
private function createEncoders(array $encoders, ContainerBuilder $container)
508550
{
509551
$encoderMap = [];
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" ?>
2+
<container xmlns="http://symfony.com/schema/dic/services"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
5+
6+
<services>
7+
<service id="security.authenticator.http_basic"
8+
class="Symfony\Component\Security\Core\Authentication\Authenticator\HttpBasicAuthenticator"
9+
abstract="true">
10+
<argument type="abstract">realm name</argument>
11+
<argument type="abstract">user provider</argument>
12+
<argument type="service" id="security.encoder_factory" />
13+
<argument type="service" id="logger" on-invalid="null" />
14+
</service>
15+
</services>
16+
</container>

src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,22 @@
4545
</service>
4646

4747
<!-- Authentication related services -->
48-
<service id="security.authentication.manager" class="Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager">
48+
<service id="security.authentication.manager.provider" class="Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager">
4949
<argument /> <!-- providers -->
5050
<argument>%security.authentication.manager.erase_credentials%</argument>
5151
<call method="setEventDispatcher">
5252
<argument type="service" id="event_dispatcher" />
5353
</call>
5454
</service>
55+
<service id="security.authentication.manager.guard" class="Symfony\Component\Security\Core\Authentication\GuardAuthenticationManager">
56+
<argument /> <!-- guard authenticators -->
57+
<argument /> <!-- User Checker -->
58+
<argument>%security.authentication.manager.erase_credentials%</argument>
59+
<call method="setEventDispatcher">
60+
<argument type="service" id="event_dispatcher" />
61+
</call>
62+
</service>
63+
<service id="security.authentication.manager" alias="security.authentication.manager.provider"/>
5564
<service id="Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface" alias="security.authentication.manager" />
5665

5766
<service id="security.authentication.trust_resolver" class="Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver" />

src/Symfony/Bundle/SecurityBundle/SecurityBundle.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\RegisterCsrfTokenClearingLogoutHandlerPass;
1818
use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\RegisterTokenUsageTrackingPass;
1919
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AnonymousFactory;
20+
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\CustomAuthenticatorFactory;
2021
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginFactory;
2122
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginLdapFactory;
2223
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\GuardAuthenticationFactory;
@@ -63,6 +64,7 @@ public function build(ContainerBuilder $container)
6364
$extension->addSecurityListenerFactory(new RemoteUserFactory());
6465
$extension->addSecurityListenerFactory(new GuardAuthenticationFactory());
6566
$extension->addSecurityListenerFactory(new AnonymousFactory());
67+
$extension->addSecurityListenerFactory(new CustomAuthenticatorFactory());
6668

6769
$extension->addUserProviderFactory(new InMemoryFactory());
6870
$extension->addUserProviderFactory(new LdapFactory());

0 commit comments

Comments
 (0)
0