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('hide_user_not_found')->defaultTrue()->end()
|
||||||
->booleanNode('always_authenticate_before_granting')->defaultFalse()->end()
|
->booleanNode('always_authenticate_before_granting')->defaultFalse()->end()
|
||||||
->booleanNode('erase_credentials')->defaultTrue()->end()
|
->booleanNode('erase_credentials')->defaultTrue()->end()
|
||||||
|
->booleanNode('guard_authentication_manager')->defaultFalse()->end()
|
||||||
->arrayNode('access_decision_manager')
|
->arrayNode('access_decision_manager')
|
||||||
->addDefaultsIfNotSet()
|
->addDefaultsIfNotSet()
|
||||||
->children()
|
->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>
|
* @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)
|
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];
|
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()
|
public function getPosition()
|
||||||
{
|
{
|
||||||
return 'http';
|
return 'http';
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace Symfony\Bundle\SecurityBundle\DependencyInjection;
|
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\RememberMeFactory;
|
||||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
|
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
|
||||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface;
|
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface;
|
||||||
@ -52,6 +53,8 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
|
|||||||
private $userProviderFactories = [];
|
private $userProviderFactories = [];
|
||||||
private $statelessFirewallKeys = [];
|
private $statelessFirewallKeys = [];
|
||||||
|
|
||||||
|
private $guardAuthenticationManagerEnabled = false;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
foreach ($this->listenerPositions as $position) {
|
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.access.always_authenticate_before_granting', $config['always_authenticate_before_granting']);
|
||||||
$container->setParameter('security.authentication.hide_user_not_found', $config['hide_user_not_found']);
|
$container->setParameter('security.authentication.hide_user_not_found', $config['hide_user_not_found']);
|
||||||
|
|
||||||
|
$this->guardAuthenticationManagerEnabled = $config['guard_authentication_manager'];
|
||||||
|
|
||||||
$this->createFirewalls($config, $container);
|
$this->createFirewalls($config, $container);
|
||||||
$this->createAuthorization($config, $container);
|
$this->createAuthorization($config, $container);
|
||||||
$this->createRoleHierarchy($config, $container);
|
$this->createRoleHierarchy($config, $container);
|
||||||
@ -258,8 +263,13 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
|
|||||||
$authenticationProviders = array_map(function ($id) {
|
$authenticationProviders = array_map(function ($id) {
|
||||||
return new Reference($id);
|
return new Reference($id);
|
||||||
}, array_values(array_unique($authenticationProviders)));
|
}, 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
|
$container
|
||||||
->getDefinition('security.authentication.manager')
|
->getDefinition($authenticationManagerId)
|
||||||
->replaceArgument(0, new IteratorArgument($authenticationProviders))
|
->replaceArgument(0, new IteratorArgument($authenticationProviders))
|
||||||
;
|
;
|
||||||
|
|
||||||
@ -467,31 +477,27 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
|
|||||||
$key = str_replace('-', '_', $factory->getKey());
|
$key = str_replace('-', '_', $factory->getKey());
|
||||||
|
|
||||||
if (isset($firewall[$key])) {
|
if (isset($firewall[$key])) {
|
||||||
if (isset($firewall[$key]['provider'])) {
|
$userProvider = $this->getUserProvider($container, $id, $firewall, $key, $defaultProvider, $providerIds, $contextListenerId);
|
||||||
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;
|
|
||||||
|
|
||||||
if ('remember_me' === $key && $contextListenerId) {
|
if ($this->guardAuthenticationManagerEnabled) {
|
||||||
$container->getDefinition($contextListenerId)->addTag('security.remember_me_aware', ['id' => $id, 'provider' => 'none']);
|
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;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$authenticationProviders[$id.'_'.$key] = $authenticators;
|
||||||
}
|
}
|
||||||
} 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 {
|
} 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);
|
||||||
|
|
||||||
|
$listeners[] = new Reference($listenerId);
|
||||||
|
$authenticationProviders[] = $provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
list($provider, $listenerId, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint);
|
|
||||||
|
|
||||||
$listeners[] = new Reference($listenerId);
|
|
||||||
$authenticationProviders[] = $provider;
|
|
||||||
$hasListeners = true;
|
$hasListeners = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -504,6 +510,42 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
|
|||||||
return [$listeners, $defaultEntryPoint];
|
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)
|
private function createEncoders(array $encoders, ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
$encoderMap = [];
|
$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>
|
</service>
|
||||||
|
|
||||||
<!-- Authentication related services -->
|
<!-- 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 /> <!-- providers -->
|
||||||
<argument>%security.authentication.manager.erase_credentials%</argument>
|
<argument>%security.authentication.manager.erase_credentials%</argument>
|
||||||
<call method="setEventDispatcher">
|
<call method="setEventDispatcher">
|
||||||
<argument type="service" id="event_dispatcher" />
|
<argument type="service" id="event_dispatcher" />
|
||||||
</call>
|
</call>
|
||||||
</service>
|
</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="Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface" alias="security.authentication.manager" />
|
||||||
|
|
||||||
<service id="security.authentication.trust_resolver" class="Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver" />
|
<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\RegisterCsrfTokenClearingLogoutHandlerPass;
|
||||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\RegisterTokenUsageTrackingPass;
|
use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\RegisterTokenUsageTrackingPass;
|
||||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AnonymousFactory;
|
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\FormLoginFactory;
|
||||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginLdapFactory;
|
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginLdapFactory;
|
||||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\GuardAuthenticationFactory;
|
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\GuardAuthenticationFactory;
|
||||||
@ -63,6 +64,7 @@ class SecurityBundle extends Bundle
|
|||||||
$extension->addSecurityListenerFactory(new RemoteUserFactory());
|
$extension->addSecurityListenerFactory(new RemoteUserFactory());
|
||||||
$extension->addSecurityListenerFactory(new GuardAuthenticationFactory());
|
$extension->addSecurityListenerFactory(new GuardAuthenticationFactory());
|
||||||
$extension->addSecurityListenerFactory(new AnonymousFactory());
|
$extension->addSecurityListenerFactory(new AnonymousFactory());
|
||||||
|
$extension->addSecurityListenerFactory(new CustomAuthenticatorFactory());
|
||||||
|
|
||||||
$extension->addUserProviderFactory(new InMemoryFactory());
|
$extension->addUserProviderFactory(new InMemoryFactory());
|
||||||
$extension->addUserProviderFactory(new LdapFactory());
|
$extension->addUserProviderFactory(new LdapFactory());
|
||||||
|
Reference in New Issue
Block a user