Skip to content

Commit

Permalink
Merge AuthenticatorManager and AuthenticatorHandler
Browse files Browse the repository at this point in the history
The AuthenticatorManager now performs the whole authentication process. This
allows for manual authentication without duplicating or publicly exposing parts
of the process.
  • Loading branch information
wouterj committed Apr 20, 2020
1 parent 44cc76f commit bf1a452
Show file tree
Hide file tree
Showing 31 changed files with 590 additions and 635 deletions.
Expand Up @@ -156,8 +156,8 @@ public function load(array $configs, ContainerBuilder $container)
->replaceArgument(2, $this->statelessFirewallKeys);

if ($this->authenticatorManagerEnabled) {
$container->getDefinition('security.authenticator_handler')
->replaceArgument(2, $this->statelessFirewallKeys);
$container->getDefinition(SessionListener::class)
->replaceArgument(1, $this->statelessFirewallKeys);
}

if ($config['encoders']) {
Expand Down Expand Up @@ -444,25 +444,19 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
return new Reference($id);
}, $firewallAuthenticationProviders);
$container
->setDefinition($managerId = 'security.authenticator.manager.'.$id, new ChildDefinition('security.authentication.manager.authenticator'))
->setDefinition($managerId = 'security.authenticator.manager.'.$id, new ChildDefinition('security.authenticator.manager'))
->replaceArgument(0, $authenticators)
->replaceArgument(3, $id)
->addTag('monolog.logger', ['channel' => 'security'])
;

$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([$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)
;

$listeners[] = new Reference('security.firewall.authenticator.'.$id);
Expand Down

This file was deleted.

4 changes: 2 additions & 2 deletions src/Symfony/Bundle/SecurityBundle/Resources/config/guard.xml
Expand Up @@ -8,7 +8,7 @@
<defaults public="false" />

<service id="security.authentication.guard_handler"
class="Symfony\Component\Security\Guard\GuardHandler"
class="Symfony\Component\Security\Guard\GuardAuthenticatorHandler"
>
<argument type="service" id="security.token_storage" />
<argument type="service" id="event_dispatcher" on-invalid="null" />
Expand All @@ -18,7 +18,7 @@
</call>
</service>

<service id="AuthenticatorHandler" alias="security.authentication.guard_handler" />
<service id="Symfony\Component\Security\Guard\GuardAuthenticatorHandler" alias="security.authentication.guard_handler" />

<!-- See GuardAuthenticationFactory -->
<service id="security.authentication.provider.guard"
Expand Down
Expand Up @@ -6,56 +6,39 @@
<services>
<!-- Manager -->

<service id="security.authentication.manager.authenticator"
<service id="security.authenticator.manager"
class="Symfony\Component\Security\Http\Authentication\AuthenticatorManager"
abstract="true"
>
<argument type="abstract">authenticators</argument>
<argument type="service" id="security.token_storage" />
<argument type="service" id="event_dispatcher" />
<argument type="abstract">provider key</argument>
<argument type="service" id="logger" on-invalid="null" />
<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">
<service id="security.user_authenticator"
class="Symfony\Bundle\SecurityBundle\Security\UserAuthenticator">
<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\Http\Authentication\UserAuthenticatorInterface" alias="security.user_authenticator" />

<service id="security.authenticator_handler"
class="Symfony\Component\Security\Http\Authentication\AuthenticatorHandler"
>
<argument type="service" id="security.token_storage" />
<argument type="service" id="event_dispatcher" on-invalid="null" />
<argument /> <!-- stateless firewall keys -->
<call method="setSessionAuthenticationStrategy">
<argument type="service" id="security.authentication.session_strategy" />
</call>
</service>

<service id="security.firewall.authenticator.locator"
class="Symfony\Component\DependencyInjection\ServiceLocator"
abstract="true" />
<service id="security.authentication.manager"
class="Symfony\Component\Security\Http\Authentication\NoopAuthenticationManager"/>
<service id="Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface" alias="security.authentication.manager" />

<service id="security.firewall.authenticator"
class="Symfony\Bundle\SecurityBundle\EventListener\LazyAuthenticatorManagerListener"
class="Symfony\Component\Security\Http\Firewall\AuthenticatorManagerListener"
abstract="true">
<tag name="monolog.logger" channel="security" />
<argument type="abstract">authenticator manager</argument>
<argument type="service" id="security.authenticator_handler" />
<argument/> <!-- authenticator locator -->
<argument/> <!-- provider key -->
<argument type="service" id="event_dispatcher" />
<argument type="service" id="logger" on-invalid="null" />
</service>

<!-- Listeners -->
Expand All @@ -75,6 +58,12 @@
<argument type="service" id="Symfony\Component\Security\Core\User\UserCheckerInterface" />
</service>

<service id="security.listener.session" class="Symfony\Component\Security\Http\EventListener\SessionStrategyListener">
<tag name="kernel.event_subscriber" />
<argument type="service" id="security.authentication.session_strategy" />
<argument type="abstract">stateless firewall keys</argument>
</service>

<service id="security.listener.remember_me"
class="Symfony\Component\Security\Http\EventListener\RememberMeListener"
abstract="true">
Expand Down

This file was deleted.

59 changes: 59 additions & 0 deletions src/Symfony/Bundle/SecurityBundle/Security/UserAuthenticator.php
@@ -0,0 +1,59 @@
<?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 Psr\Container\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\LogicException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Http\Authentication\UserAuthenticatorInterface;
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;

/**
* A decorator that delegates all method calls to the authenticator
* manager of the current firewall.
*
* @author Wouter de Jong <wouter@wouterj.nl>
*
* @final
* @experimental in Symfony 5.1
*/
class UserAuthenticator implements UserAuthenticatorInterface
{
private $firewallMap;
private $userAuthenticators;
private $requestStack;

public function __construct(FirewallMap $firewallMap, ContainerInterface $userAuthenticators, RequestStack $requestStack)
{
$this->firewallMap = $firewallMap;
$this->userAuthenticators = $userAuthenticators;
$this->requestStack = $requestStack;
}

public function authenticateUser(UserInterface $user, AuthenticatorInterface $authenticator, Request $request): ?Response
{
return $this->getUserAuthenticator()->authenticateUser($user, $authenticator, $request);
}

private function getUserAuthenticator(): UserAuthenticatorInterface
{
$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->userAuthenticators->get($firewallConfig->getName());
}
}
Expand Up @@ -19,8 +19,8 @@
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Guard\AuthenticatorInterface;
use Symfony\Component\Security\Guard\GuardHandler;
use Symfony\Component\Security\Guard\Token\PreAuthenticationToken as GuardPreAuthenticationGuardToken;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken as GuardPreAuthenticationGuardToken;
use Symfony\Component\Security\Http\Firewall\AbstractListener;
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;

Expand All @@ -45,7 +45,7 @@ class GuardAuthenticationListener extends AbstractListener
* @param string $providerKey The provider (i.e. firewall) key
* @param iterable|AuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationProvider
*/
public function __construct(GuardHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, string $providerKey, iterable $guardAuthenticators, LoggerInterface $logger = null)
public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, string $providerKey, iterable $guardAuthenticators, LoggerInterface $logger = null)
{
if (empty($providerKey)) {
throw new \InvalidArgumentException('$providerKey must not be empty.');
Expand Down Expand Up @@ -121,7 +121,7 @@ public function setRememberMeServices(RememberMeServicesInterface $rememberMeSer
protected function executeGuardAuthenticators(array $guardAuthenticators, RequestEvent $event): void
{
foreach ($guardAuthenticators as $key => $guardAuthenticator) {
$uniqueGuardKey = $this->providerKey.'_'.$key;;
$uniqueGuardKey = $this->providerKey.'_'.$key;

$this->executeGuardAuthenticator($uniqueGuardKey, $guardAuthenticator, $event);

Expand Down

0 comments on commit bf1a452

Please sign in to comment.