Use one AuthenticatorManager per firewall

This commit is contained in:
Wouter de Jong 2020-02-29 01:49:11 +01:00
parent 09bed16d3d
commit 44cc76fec2
5 changed files with 95 additions and 21 deletions

View File

@ -23,6 +23,7 @@ use Symfony\Component\Config\FileLocator;
use Symfony\Component\Console\Application; use Symfony\Component\Console\Application;
use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
@ -278,19 +279,16 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
$mapDef->replaceArgument(0, ServiceLocatorTagPass::register($container, $contextRefs)); $mapDef->replaceArgument(0, ServiceLocatorTagPass::register($container, $contextRefs));
$mapDef->replaceArgument(1, new IteratorArgument($map)); $mapDef->replaceArgument(1, new IteratorArgument($map));
// add authentication providers to authentication manager if (!$this->authenticatorManagerEnabled) {
$authenticationProviders = array_map(function ($id) { // add authentication providers to authentication manager
return new Reference($id); $authenticationProviders = array_map(function ($id) {
}, array_unique($authenticationProviders)); return new Reference($id);
$authenticationManagerId = 'security.authentication.manager.provider'; }, array_unique($authenticationProviders));
if ($this->authenticatorManagerEnabled) {
$authenticationManagerId = 'security.authentication.manager.authenticator'; $container
$container->setAlias('security.authentication.manager', new Alias($authenticationManagerId)); ->getDefinition('security.authentication.manager')
->replaceArgument(0, new IteratorArgument($authenticationProviders));
} }
$container
->getDefinition($authenticationManagerId)
->replaceArgument(0, new IteratorArgument($authenticationProviders))
;
// register an autowire alias for the UserCheckerInterface if no custom user checker service is configured // register an autowire alias for the UserCheckerInterface if no custom user checker service is configured
if (!$customUserChecker) { if (!$customUserChecker) {
@ -441,17 +439,28 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
$authenticationProviders = array_merge($authenticationProviders, $firewallAuthenticationProviders); $authenticationProviders = array_merge($authenticationProviders, $firewallAuthenticationProviders);
if ($this->authenticatorManagerEnabled) { if ($this->authenticatorManagerEnabled) {
// authenticator manager
$authenticators = array_map(function ($id) {
return new Reference($id);
}, $firewallAuthenticationProviders);
$container
->setDefinition($managerId = 'security.authenticator.manager.'.$id, new ChildDefinition('security.authentication.manager.authenticator'))
->replaceArgument(0, $authenticators)
;
$managerLocator = $container->getDefinition('security.authenticator.managers_locator');
$managerLocator->replaceArgument(0, array_merge($managerLocator->getArgument(0), [$id => new ServiceClosureArgument(new Reference($managerId))]));
// authenticator manager listener // authenticator manager listener
$container $container
->setDefinition('security.firewall.authenticator.'.$id.'.locator', new ChildDefinition('security.firewall.authenticator.locator')) ->setDefinition('security.firewall.authenticator.'.$id.'.locator', new ChildDefinition('security.firewall.authenticator.locator'))
->setArguments([array_map(function ($id) { ->setArguments([$authenticators])
return new Reference($id);
}, $firewallAuthenticationProviders)])
->addTag('container.service_locator') ->addTag('container.service_locator')
; ;
$container $container
->setDefinition('security.firewall.authenticator.'.$id, new ChildDefinition('security.firewall.authenticator')) ->setDefinition('security.firewall.authenticator.'.$id, new ChildDefinition('security.firewall.authenticator'))
->replaceArgument(0, new Reference($managerId))
->replaceArgument(2, new Reference('security.firewall.authenticator.'.$id.'.locator')) ->replaceArgument(2, new Reference('security.firewall.authenticator.'.$id.'.locator'))
->replaceArgument(3, $id) ->replaceArgument(3, $id)
; ;

View File

@ -6,7 +6,10 @@
<services> <services>
<!-- Manager --> <!-- Manager -->
<service id="security.authentication.manager.authenticator" class="Symfony\Component\Security\Http\Authentication\AuthenticatorManager"> <service id="security.authentication.manager.authenticator"
class="Symfony\Component\Security\Http\Authentication\AuthenticatorManager"
abstract="true"
>
<argument type="abstract">authenticators</argument> <argument type="abstract">authenticators</argument>
<argument type="service" id="event_dispatcher" /> <argument type="service" id="event_dispatcher" />
<argument>%security.authentication.manager.erase_credentials%</argument> <argument>%security.authentication.manager.erase_credentials%</argument>
@ -14,6 +17,18 @@
<argument type="service" id="event_dispatcher" /> <argument type="service" id="event_dispatcher" />
</call> </call>
</service> </service>
<service id="security.authenticator.managers_locator"
class="Symfony\Component\DependencyInjection\ServiceLocator">
<argument type="collection" />
</service>
<service id="security.authentication.manager"
class="Symfony\Bundle\SecurityBundle\Security\FirewallAwareAuthenticatorManager">
<argument type="service" id="security.firewall.map" />
<argument type="service" id="security.authenticator.managers_locator" />
<argument type="service" id="request_stack" />
</service>
<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.authenticator_handler" <service id="security.authenticator_handler"
@ -35,7 +50,7 @@
class="Symfony\Bundle\SecurityBundle\EventListener\LazyAuthenticatorManagerListener" class="Symfony\Bundle\SecurityBundle\EventListener\LazyAuthenticatorManagerListener"
abstract="true"> abstract="true">
<tag name="monolog.logger" channel="security" /> <tag name="monolog.logger" channel="security" />
<argument type="service" id="security.authentication.manager" /> <argument type="abstract">authenticator manager</argument>
<argument type="service" id="security.authenticator_handler" /> <argument type="service" id="security.authenticator_handler" />
<argument/> <!-- authenticator locator --> <argument/> <!-- authenticator locator -->
<argument/> <!-- provider key --> <argument/> <!-- provider key -->

View File

@ -0,0 +1,48 @@
<?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\Security;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\LogicException;
/**
* A decorator that delegates all method calls to the authenticator
* manager of the current firewall.
*
* @author Wouter de Jong <wouter@wouterj.nl>
*/
class FirewallAwareAuthenticatorManager implements AuthenticationManagerInterface
{
private $firewallMap;
private $authenticatorManagers;
private $requestStack;
public function __construct(FirewallMap $firewallMap, ServiceLocator $authenticatorManagers, RequestStack $requestStack)
{
$this->firewallMap = $firewallMap;
$this->authenticatorManagers = $authenticatorManagers;
$this->requestStack = $requestStack;
}
public function authenticate(TokenInterface $token)
{
$firewallConfig = $this->firewallMap->getFirewallConfig($this->requestStack->getMasterRequest());
if (null === $firewallConfig) {
throw new LogicException('Cannot call authenticate on this request, as it is not behind a firewall.');
}
return $this->authenticatorManagers->get($firewallConfig->getName())->authenticate($token);
}
}

View File

@ -40,14 +40,16 @@ class AuthenticatorManager implements AuthenticationManagerInterface
private $authenticators; private $authenticators;
private $eventDispatcher; private $eventDispatcher;
private $eraseCredentials; private $eraseCredentials;
private $providerKey;
/** /**
* @param AuthenticatorInterface[] $authenticators The authenticators, with keys that match what's passed to AuthenticatorManagerListener * @param AuthenticatorInterface[] $authenticators The authenticators, with keys that match what's passed to AuthenticatorManagerListener
*/ */
public function __construct(iterable $authenticators, EventDispatcherInterface $eventDispatcher, bool $eraseCredentials = true) public function __construct(iterable $authenticators, EventDispatcherInterface $eventDispatcher, string $providerKey, bool $eraseCredentials = true)
{ {
$this->authenticators = $authenticators; $this->authenticators = $authenticators;
$this->eventDispatcher = $eventDispatcher; $this->eventDispatcher = $eventDispatcher;
$this->providerKey = $providerKey;
$this->eraseCredentials = $eraseCredentials; $this->eraseCredentials = $eraseCredentials;
} }

View File

@ -33,7 +33,7 @@ use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
*/ */
class AuthenticatorManagerListener extends AbstractListener class AuthenticatorManagerListener extends AbstractListener
{ {
private $authenticationManager; private $authenticatorManager;
private $authenticatorHandler; private $authenticatorHandler;
private $authenticators; private $authenticators;
protected $providerKey; protected $providerKey;
@ -45,7 +45,7 @@ class AuthenticatorManagerListener extends AbstractListener
*/ */
public function __construct(AuthenticationManagerInterface $authenticationManager, AuthenticatorHandler $authenticatorHandler, iterable $authenticators, string $providerKey, EventDispatcherInterface $eventDispatcher, ?LoggerInterface $logger = null) public function __construct(AuthenticationManagerInterface $authenticationManager, AuthenticatorHandler $authenticatorHandler, iterable $authenticators, string $providerKey, EventDispatcherInterface $eventDispatcher, ?LoggerInterface $logger = null)
{ {
$this->authenticationManager = $authenticationManager; $this->authenticatorManager = $authenticationManager;
$this->authenticatorHandler = $authenticatorHandler; $this->authenticatorHandler = $authenticatorHandler;
$this->authenticators = $authenticators; $this->authenticators = $authenticators;
$this->providerKey = $providerKey; $this->providerKey = $providerKey;
@ -157,7 +157,7 @@ class AuthenticatorManagerListener extends AbstractListener
} }
// pass the token into the AuthenticationManager system // pass the token into the AuthenticationManager system
// this indirectly calls AuthenticatorManager::authenticate() // this indirectly calls AuthenticatorManager::authenticate()
$token = $this->authenticationManager->authenticate($token); $token = $this->authenticatorManager->authenticate($token);
if (null !== $this->logger) { if (null !== $this->logger) {
$this->logger->info('Authenticator successful!', ['token' => $token, 'authenticator' => \get_class($authenticator)]); $this->logger->info('Authenticator successful!', ['token' => $token, 'authenticator' => \get_class($authenticator)]);