From b4857679f842b7057fd60ca10a78a18c12aa0fc5 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Sun, 8 Sep 2019 15:55:27 +0200 Subject: [PATCH] Integrated GuardAuthenticationManager in the FrameworkBundle --- .../DependencyInjection/MainConfiguration.php | 1 + .../Factory/GuardFactoryInterface.php | 27 +++++++ .../Security/Factory/HttpBasicFactory.php | 13 +++- .../DependencyInjection/SecurityExtension.php | 75 ++++++++++++++----- .../Resources/config/authenticators.xml | 16 ++++ .../Resources/config/security.xml | 11 ++- 6 files changed, 122 insertions(+), 21 deletions(-) create mode 100644 src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/GuardFactoryInterface.php create mode 100644 src/Symfony/Bundle/SecurityBundle/Resources/config/authenticators.xml diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php index 1e1e97ccb5b3f..e91e505c2edfa 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php @@ -74,6 +74,7 @@ public function getConfigTreeBuilder() ->booleanNode('hide_user_not_found')->defaultTrue()->end() ->booleanNode('always_authenticate_before_granting')->defaultFalse()->end() ->booleanNode('erase_credentials')->defaultTrue()->end() + ->booleanNode('guard_authentication_manager')->defaultFalse()->end() ->arrayNode('access_decision_manager') ->addDefaultsIfNotSet() ->children() diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/GuardFactoryInterface.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/GuardFactoryInterface.php new file mode 100644 index 0000000000000..adb68d97bbfc1 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/GuardFactoryInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * @author Wouter de Jong + */ +interface GuardFactoryInterface +{ + /** + * Creates the Guard service for the provided configuration. + * + * @return string The Guard service ID to be used by the firewall + */ + public function createGuard(ContainerBuilder $container, string $id, array $config, string $userProviderId): string; +} diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php index f3b5bc167e64e..2058a7e77d184 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php @@ -21,7 +21,7 @@ * * @author Fabien Potencier */ -class HttpBasicFactory implements SecurityFactoryInterface +class HttpBasicFactory implements SecurityFactoryInterface, GuardFactoryInterface { public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint) { @@ -46,6 +46,17 @@ public function create(ContainerBuilder $container, $id, $config, $userProvider, return [$provider, $listenerId, $entryPointId]; } + public function createGuard(ContainerBuilder $container, string $id, array $config, string $userProviderId): string + { + $authenticatorId = 'security.authenticator.basic.'.$id; + $container + ->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.basic')) + ->replaceArgument(0, $config['realm']) + ->replaceArgument(1, new Reference($userProviderId)); + + return $authenticatorId; + } + public function getPosition() { return 'http'; diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 2a38636ac8d2c..ca47becf519af 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\SecurityBundle\DependencyInjection; +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\GuardFactoryInterface; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\RememberMeFactory; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface; @@ -53,6 +54,8 @@ class SecurityExtension extends Extension implements PrependExtensionInterface private $userProviderFactories = []; private $statelessFirewallKeys = []; + private $guardAuthenticationManagerEnabled = false; + public function __construct() { foreach ($this->listenerPositions as $position) { @@ -140,6 +143,8 @@ public function load(array $configs, ContainerBuilder $container) $container->setParameter('security.access.always_authenticate_before_granting', $config['always_authenticate_before_granting']); $container->setParameter('security.authentication.hide_user_not_found', $config['hide_user_not_found']); + $this->guardAuthenticationManagerEnabled = $config['guard_authentication_manager']; + $this->createFirewalls($config, $container); $this->createAuthorization($config, $container); $this->createRoleHierarchy($config, $container); @@ -262,8 +267,13 @@ private function createFirewalls(array $config, ContainerBuilder $container) $authenticationProviders = array_map(function ($id) { return new Reference($id); }, array_values(array_unique($authenticationProviders))); + $authenticationManagerId = 'security.authentication.manager.provider'; + if ($this->guardAuthenticationManagerEnabled) { + $authenticationManagerId = 'security.authentication.manager.guard'; + $container->setAlias('security.authentication.manager', new Alias($authenticationManagerId)); + } $container - ->getDefinition('security.authentication.manager') + ->getDefinition($authenticationManagerId) ->replaceArgument(0, new IteratorArgument($authenticationProviders)) ; @@ -462,27 +472,20 @@ private function createAuthenticationListeners(ContainerBuilder $container, stri $key = str_replace('-', '_', $factory->getKey()); if (isset($firewall[$key])) { - if (isset($firewall[$key]['provider'])) { - if (!isset($providerIds[$normalizedName = str_replace('-', '_', $firewall[$key]['provider'])])) { - throw new InvalidConfigurationException(sprintf('Invalid firewall "%s": user provider "%s" not found.', $id, $firewall[$key]['provider'])); + $userProvider = $this->getUserProvider($container, $id, $firewall, $key, $defaultProvider, $providerIds); + + if ($this->guardAuthenticationManagerEnabled) { + if (!$factory instanceof GuardFactoryInterface) { + throw new InvalidConfigurationException(sprintf('Cannot configure GuardAuthenticationManager as %s authentication does not support it, set security.guard_authentication_manager to `false`.', $key)); } - $userProvider = $providerIds[$normalizedName]; - } elseif ('remember_me' === $key) { - // RememberMeFactory will use the firewall secret when created - $userProvider = null; - } elseif ($defaultProvider) { - $userProvider = $defaultProvider; - } elseif (empty($providerIds)) { - $userProvider = sprintf('security.user.provider.missing.%s', $key); - $container->setDefinition($userProvider, (new ChildDefinition('security.user.provider.missing'))->replaceArgument(0, $id)); - } else { - 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)); - } - list($provider, $listenerId, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint); + $authenticationProviders[$id.'_'.$key] = $factory->createGuard($container, $id, $firewall[$key], $userProvider); + } else { + list($provider, $listenerId, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint); - $listeners[] = new Reference($listenerId); - $authenticationProviders[] = $provider; + $listeners[] = new Reference($listenerId); + $authenticationProviders[] = $provider; + } $hasListeners = true; } } @@ -519,6 +522,40 @@ private function createAuthenticationListeners(ContainerBuilder $container, stri return [$listeners, $defaultEntryPoint]; } + private function getUserProvider(ContainerBuilder $container, string $id, array $firewall, string $factoryKey, ?string $defaultProvider, array $providerIds): ?string + { + if (isset($firewall[$factoryKey]['provider'])) { + if (!isset($providerIds[$normalizedName = str_replace('-', '_', $firewall[$factoryKey]['provider'])])) { + throw new InvalidConfigurationException( + sprintf('Invalid firewall "%s": user provider "%s" not found.', $id, $firewall[$factoryKey]['provider']) + ); + } + + return $providerIds[$normalizedName]; + } + + if ('remember_me' === $factoryKey) { + // RememberMeFactory will use the firewall secret when created + return null; + } + + if ($defaultProvider) { + return $defaultProvider; + } + + if (empty($providerIds)) { + $userProvider = sprintf('security.user.provider.missing.%s', $factoryKey); + $container->setDefinition( + $userProvider, + (new ChildDefinition('security.user.provider.missing'))->replaceArgument(0, $id) + ); + + return $userProvider; + } + + 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)); + } + private function createEncoders(array $encoders, ContainerBuilder $container) { $encoderMap = []; diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/authenticators.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/authenticators.xml new file mode 100644 index 0000000000000..33de87324b831 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/authenticators.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index b1263b057b31d..c7e45a1a05ffd 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -40,13 +40,22 @@ - + %security.authentication.manager.erase_credentials% + + + + %security.authentication.manager.erase_credentials% + + + + +