Integrated GuardAuthenticationManager in the SecurityBundle
This commit is contained in:
parent
a6890dbcf0
commit
9b7fddd10c
@ -73,6 +73,7 @@ class MainConfiguration implements ConfigurationInterface
|
||||
->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()
|
||||
|
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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\Config\Definition\Builder\ArrayNodeDefinition;
|
||||
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
class CustomAuthenticatorFactory implements AuthenticatorFactoryInterface, SecurityFactoryInterface
|
||||
{
|
||||
public function create(ContainerBuilder $container, string $id, array $config, string $userProvider, ?string $defaultEntryPoint)
|
||||
{
|
||||
throw new \LogicException('Custom authenticators are not supported when "security.enable_authenticator_manager" is not set to true.');
|
||||
}
|
||||
|
||||
public function getPosition(): string
|
||||
{
|
||||
return 'pre_auth';
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return 'custom_authenticator';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayNodeDefinition $builder
|
||||
*/
|
||||
public function addConfiguration(NodeDefinition $builder)
|
||||
{
|
||||
$builder
|
||||
->fixXmlConfig('service')
|
||||
->children()
|
||||
->arrayNode('services')
|
||||
->info('An array of service ids for all of your "authenticators"')
|
||||
->requiresAtLeastOneElement()
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
}
|
||||
|
||||
public function createAuthenticator(ContainerBuilder $container, string $id, array $config, string $userProviderId): array
|
||||
{
|
||||
return $config['services'];
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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 <wouter@wouterj.nl>
|
||||
*/
|
||||
interface GuardFactoryInterface
|
||||
{
|
||||
/**
|
||||
* Creates the Guard service(s) for the provided configuration.
|
||||
*
|
||||
* @return string|string[] The Guard service ID(s) to be used by the firewall
|
||||
*/
|
||||
public function createGuard(ContainerBuilder $container, string $id, array $config, string $userProviderId);
|
||||
}
|
@ -21,7 +21,7 @@ use Symfony\Component\DependencyInjection\Reference;
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class HttpBasicFactory implements SecurityFactoryInterface
|
||||
class HttpBasicFactory implements SecurityFactoryInterface, GuardFactoryInterface
|
||||
{
|
||||
public function create(ContainerBuilder $container, string $id, array $config, string $userProvider, ?string $defaultEntryPoint)
|
||||
{
|
||||
@ -46,6 +46,17 @@ class HttpBasicFactory implements SecurityFactoryInterface
|
||||
return [$provider, $listenerId, $entryPointId];
|
||||
}
|
||||
|
||||
public function createGuard(ContainerBuilder $container, string $id, array $config, string $userProviderId): string
|
||||
{
|
||||
$authenticatorId = 'security.authenticator.http_basic.'.$id;
|
||||
$container
|
||||
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.http_basic'))
|
||||
->replaceArgument(0, $config['realm'])
|
||||
->replaceArgument(1, new Reference($userProviderId));
|
||||
|
||||
return $authenticatorId;
|
||||
}
|
||||
|
||||
public function getPosition()
|
||||
{
|
||||
return 'http';
|
||||
|
@ -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;
|
||||
@ -52,6 +53,8 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
|
||||
private $userProviderFactories = [];
|
||||
private $statelessFirewallKeys = [];
|
||||
|
||||
private $guardAuthenticationManagerEnabled = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
foreach ($this->listenerPositions as $position) {
|
||||
@ -135,6 +138,8 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
|
||||
$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);
|
||||
@ -258,8 +263,13 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
|
||||
$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))
|
||||
;
|
||||
|
||||
@ -467,31 +477,27 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
|
||||
$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 = $providerIds[$normalizedName];
|
||||
} elseif ('remember_me' === $key || 'anonymous' === $key) {
|
||||
// RememberMeFactory will use the firewall secret when created, AnonymousAuthenticationListener does not load users.
|
||||
$userProvider = null;
|
||||
$userProvider = $this->getUserProvider($container, $id, $firewall, $key, $defaultProvider, $providerIds, $contextListenerId);
|
||||
|
||||
if ('remember_me' === $key && $contextListenerId) {
|
||||
$container->getDefinition($contextListenerId)->addTag('security.remember_me_aware', ['id' => $id, 'provider' => 'none']);
|
||||
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));
|
||||
}
|
||||
|
||||
$authenticators = $factory->createGuard($container, $id, $firewall[$key], $userProvider);
|
||||
if (\is_array($authenticators)) {
|
||||
foreach ($authenticators as $i => $authenticator) {
|
||||
$authenticationProviders[$id.'_'.$key.$i] = $authenticator;
|
||||
}
|
||||
} 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));
|
||||
$authenticationProviders[$id.'_'.$key] = $authenticators;
|
||||
}
|
||||
|
||||
} else {
|
||||
list($provider, $listenerId, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint);
|
||||
|
||||
$listeners[] = new Reference($listenerId);
|
||||
$authenticationProviders[] = $provider;
|
||||
}
|
||||
$hasListeners = true;
|
||||
}
|
||||
}
|
||||
@ -504,6 +510,42 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
|
||||
return [$listeners, $defaultEntryPoint];
|
||||
}
|
||||
|
||||
private function getUserProvider(ContainerBuilder $container, string $id, array $firewall, string $factoryKey, ?string $defaultProvider, array $providerIds, ?string $contextListenerId): ?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 || 'anonymous' === $factoryKey) {
|
||||
if ('remember_me' === $factoryKey && $contextListenerId) {
|
||||
$container->getDefinition($contextListenerId)->addTag('security.remember_me_aware', ['id' => $id, 'provider' => 'none']);
|
||||
}
|
||||
|
||||
// RememberMeFactory will use the firewall secret when created
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($defaultProvider) {
|
||||
return $defaultProvider;
|
||||
}
|
||||
|
||||
if (!$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 = [];
|
||||
|
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" ?>
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<services>
|
||||
<service id="security.authenticator.http_basic"
|
||||
class="Symfony\Component\Security\Core\Authentication\Authenticator\HttpBasicAuthenticator"
|
||||
abstract="true">
|
||||
<argument type="abstract">realm name</argument>
|
||||
<argument type="abstract">user provider</argument>
|
||||
<argument type="service" id="security.encoder_factory" />
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
@ -45,13 +45,22 @@
|
||||
</service>
|
||||
|
||||
<!-- Authentication related services -->
|
||||
<service id="security.authentication.manager" class="Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager">
|
||||
<service id="security.authentication.manager.provider" class="Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager">
|
||||
<argument /> <!-- providers -->
|
||||
<argument>%security.authentication.manager.erase_credentials%</argument>
|
||||
<call method="setEventDispatcher">
|
||||
<argument type="service" id="event_dispatcher" />
|
||||
</call>
|
||||
</service>
|
||||
<service id="security.authentication.manager.guard" class="Symfony\Component\Security\Core\Authentication\GuardAuthenticationManager">
|
||||
<argument /> <!-- guard authenticators -->
|
||||
<argument /> <!-- User Checker -->
|
||||
<argument>%security.authentication.manager.erase_credentials%</argument>
|
||||
<call method="setEventDispatcher">
|
||||
<argument type="service" id="event_dispatcher" />
|
||||
</call>
|
||||
</service>
|
||||
<service id="security.authentication.manager" alias="security.authentication.manager.provider"/>
|
||||
<service id="Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface" alias="security.authentication.manager" />
|
||||
|
||||
<service id="security.authentication.trust_resolver" class="Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver" />
|
||||
|
@ -17,6 +17,7 @@ use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\AddSessionDomainC
|
||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\RegisterCsrfTokenClearingLogoutHandlerPass;
|
||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\RegisterTokenUsageTrackingPass;
|
||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AnonymousFactory;
|
||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\CustomAuthenticatorFactory;
|
||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginFactory;
|
||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginLdapFactory;
|
||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\GuardAuthenticationFactory;
|
||||
@ -63,6 +64,7 @@ class SecurityBundle extends Bundle
|
||||
$extension->addSecurityListenerFactory(new RemoteUserFactory());
|
||||
$extension->addSecurityListenerFactory(new GuardAuthenticationFactory());
|
||||
$extension->addSecurityListenerFactory(new AnonymousFactory());
|
||||
$extension->addSecurityListenerFactory(new CustomAuthenticatorFactory());
|
||||
|
||||
$extension->addUserProviderFactory(new InMemoryFactory());
|
||||
$extension->addUserProviderFactory(new LdapFactory());
|
||||
|
Reference in New Issue
Block a user