Skip to content

Commit

Permalink
Use one AuthenticatorManager per firewall
Browse files Browse the repository at this point in the history
  • Loading branch information
wouterj committed Apr 20, 2020
1 parent 09bed16 commit 44cc76f
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 21 deletions.
Expand Up @@ -23,6 +23,7 @@
use Symfony\Component\Console\Application;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
Expand Down Expand Up @@ -278,19 +279,16 @@ private function createFirewalls(array $config, ContainerBuilder $container)
$mapDef->replaceArgument(0, ServiceLocatorTagPass::register($container, $contextRefs));
$mapDef->replaceArgument(1, new IteratorArgument($map));

// add authentication providers to authentication manager
$authenticationProviders = array_map(function ($id) {
return new Reference($id);
}, array_unique($authenticationProviders));
$authenticationManagerId = 'security.authentication.manager.provider';
if ($this->authenticatorManagerEnabled) {
$authenticationManagerId = 'security.authentication.manager.authenticator';
$container->setAlias('security.authentication.manager', new Alias($authenticationManagerId));
if (!$this->authenticatorManagerEnabled) {
// add authentication providers to authentication manager
$authenticationProviders = array_map(function ($id) {
return new Reference($id);
}, array_unique($authenticationProviders));

$container
->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
if (!$customUserChecker) {
Expand Down Expand Up @@ -441,17 +439,28 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
$authenticationProviders = array_merge($authenticationProviders, $firewallAuthenticationProviders);

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
$container
->setDefinition('security.firewall.authenticator.'.$id.'.locator', new ChildDefinition('security.firewall.authenticator.locator'))
->setArguments([array_map(function ($id) {
return new Reference($id);
}, $firewallAuthenticationProviders)])
->setArguments([$authenticators])
->addTag('container.service_locator')
;

$container
->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(3, $id)
;
Expand Down
Expand Up @@ -6,14 +6,29 @@
<services>
<!-- 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="service" id="event_dispatcher" />
<argument>%security.authentication.manager.erase_credentials%</argument>
<call method="setEventDispatcher">
<argument type="service" id="event_dispatcher" />
</call>
</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="security.authenticator_handler"
Expand All @@ -35,7 +50,7 @@
class="Symfony\Bundle\SecurityBundle\EventListener\LazyAuthenticatorManagerListener"
abstract="true">
<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/> <!-- authenticator locator -->
<argument/> <!-- provider key -->
Expand Down
@@ -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);
}
}
Expand Up @@ -40,14 +40,16 @@ class AuthenticatorManager implements AuthenticationManagerInterface
private $authenticators;
private $eventDispatcher;
private $eraseCredentials;
private $providerKey;

/**
* @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->eventDispatcher = $eventDispatcher;
$this->providerKey = $providerKey;
$this->eraseCredentials = $eraseCredentials;
}

Expand Down
Expand Up @@ -33,7 +33,7 @@
*/
class AuthenticatorManagerListener extends AbstractListener
{
private $authenticationManager;
private $authenticatorManager;
private $authenticatorHandler;
private $authenticators;
protected $providerKey;
Expand All @@ -45,7 +45,7 @@ class AuthenticatorManagerListener extends AbstractListener
*/
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->authenticators = $authenticators;
$this->providerKey = $providerKey;
Expand Down Expand Up @@ -157,7 +157,7 @@ private function executeAuthenticator(string $uniqueAuthenticatorKey, Authentica
}
// pass the token into the AuthenticationManager system
// this indirectly calls AuthenticatorManager::authenticate()
$token = $this->authenticationManager->authenticate($token);
$token = $this->authenticatorManager->authenticate($token);

if (null !== $this->logger) {
$this->logger->info('Authenticator successful!', ['token' => $token, 'authenticator' => \get_class($authenticator)]);
Expand Down

0 comments on commit 44cc76f

Please sign in to comment.