Added support for lazy firewalls
This commit is contained in:
parent
7859977324
commit
1c810d5d2a
@ -453,7 +453,6 @@ class SecurityExtension extends Extension implements PrependExtensionInterface
|
|||||||
->setDefinition('security.firewall.authenticator.'.$id, new ChildDefinition('security.firewall.authenticator'))
|
->setDefinition('security.firewall.authenticator.'.$id, new ChildDefinition('security.firewall.authenticator'))
|
||||||
->replaceArgument(2, new Reference('security.firewall.authenticator.'.$id.'.locator'))
|
->replaceArgument(2, new Reference('security.firewall.authenticator.'.$id.'.locator'))
|
||||||
->replaceArgument(3, $id)
|
->replaceArgument(3, $id)
|
||||||
->addTag('kernel.event_listener', ['event' => KernelEvents::REQUEST])
|
|
||||||
;
|
;
|
||||||
|
|
||||||
$listeners[] = new Reference('security.firewall.authenticator.'.$id);
|
$listeners[] = new Reference('security.firewall.authenticator.'.$id);
|
||||||
|
@ -26,37 +26,39 @@ use Symfony\Component\Security\Http\Firewall\AuthenticatorManagerListener;
|
|||||||
*/
|
*/
|
||||||
class LazyAuthenticatorManagerListener extends AuthenticatorManagerListener
|
class LazyAuthenticatorManagerListener extends AuthenticatorManagerListener
|
||||||
{
|
{
|
||||||
private $guardLocator;
|
private $authenticatorLocator;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
AuthenticationManagerInterface $authenticationManager,
|
AuthenticationManagerInterface $authenticationManager,
|
||||||
AuthenticatorHandler $authenticatorHandler,
|
AuthenticatorHandler $authenticatorHandler,
|
||||||
ServiceLocator $guardLocator,
|
ServiceLocator $authenticatorLocator,
|
||||||
string $providerKey,
|
string $providerKey,
|
||||||
EventDispatcherInterface $eventDispatcher,
|
EventDispatcherInterface $eventDispatcher,
|
||||||
?LoggerInterface $logger = null
|
?LoggerInterface $logger = null
|
||||||
) {
|
) {
|
||||||
parent::__construct($authenticationManager, $authenticatorHandler, [], $providerKey, $eventDispatcher, $logger);
|
parent::__construct($authenticationManager, $authenticatorHandler, [], $providerKey, $eventDispatcher, $logger);
|
||||||
|
|
||||||
$this->guardLocator = $guardLocator;
|
$this->authenticatorLocator = $authenticatorLocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getSupportingAuthenticators(Request $request): array
|
protected function getSupportingAuthenticators(Request $request): array
|
||||||
{
|
{
|
||||||
$guardAuthenticators = [];
|
$authenticators = [];
|
||||||
foreach ($this->guardLocator->getProvidedServices() as $key => $type) {
|
$lazy = true;
|
||||||
$guardAuthenticator = $this->guardLocator->get($key);
|
foreach ($this->authenticatorLocator->getProvidedServices() as $key => $type) {
|
||||||
|
$authenticator = $this->authenticatorLocator->get($key);
|
||||||
if (null !== $this->logger) {
|
if (null !== $this->logger) {
|
||||||
$this->logger->debug('Checking support on guard authenticator.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)]);
|
$this->logger->debug('Checking support on guard authenticator.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($authenticator)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($guardAuthenticator->supports($request)) {
|
if (false !== $supports = $authenticator->supports($request)) {
|
||||||
$guardAuthenticators[$key] = $guardAuthenticator;
|
$authenticators[$key] = $authenticator;
|
||||||
|
$lazy = $lazy && null === $supports;
|
||||||
} elseif (null !== $this->logger) {
|
} elseif (null !== $this->logger) {
|
||||||
$this->logger->debug('Guard authenticator does not support the request.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)]);
|
$this->logger->debug('Guard authenticator does not support the request.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($authenticator)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $guardAuthenticators;
|
return [$authenticators, $lazy];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@
|
|||||||
class="Symfony\Component\Security\Http\Authenticator\AnonymousAuthenticator"
|
class="Symfony\Component\Security\Http\Authenticator\AnonymousAuthenticator"
|
||||||
abstract="true">
|
abstract="true">
|
||||||
<argument type="abstract">secret</argument>
|
<argument type="abstract">secret</argument>
|
||||||
<argument type="service" id="security.token_storage" />
|
<argument type="service" id="security.untracked_token_storage" />
|
||||||
</service>
|
</service>
|
||||||
</services>
|
</services>
|
||||||
</container>
|
</container>
|
||||||
|
@ -34,8 +34,6 @@ use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
|
|||||||
*/
|
*/
|
||||||
class GuardAuthenticationListener extends AbstractListener
|
class GuardAuthenticationListener extends AbstractListener
|
||||||
{
|
{
|
||||||
use AuthenticatorManagerListenerTrait;
|
|
||||||
|
|
||||||
private $guardHandler;
|
private $guardHandler;
|
||||||
private $authenticationManager;
|
private $authenticationManager;
|
||||||
private $providerKey;
|
private $providerKey;
|
||||||
@ -75,7 +73,19 @@ class GuardAuthenticationListener extends AbstractListener
|
|||||||
$this->logger->debug('Checking for guard authentication credentials.', $context);
|
$this->logger->debug('Checking for guard authentication credentials.', $context);
|
||||||
}
|
}
|
||||||
|
|
||||||
$guardAuthenticators = $this->getSupportingAuthenticators($request);
|
$guardAuthenticators = [];
|
||||||
|
foreach ($this->authenticators as $key => $authenticator) {
|
||||||
|
if (null !== $this->logger) {
|
||||||
|
$this->logger->debug('Checking support on authenticator.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($authenticator)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($authenticator->supports($request)) {
|
||||||
|
$guardAuthenticators[$key] = $authenticator;
|
||||||
|
} elseif (null !== $this->logger) {
|
||||||
|
$this->logger->debug('Authenticator does not support the request.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($authenticator)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!$guardAuthenticators) {
|
if (!$guardAuthenticators) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,8 @@ class AnonymousAuthenticator implements AuthenticatorInterface, CustomAuthentica
|
|||||||
public function supports(Request $request): ?bool
|
public function supports(Request $request): ?bool
|
||||||
{
|
{
|
||||||
// do not overwrite already stored tokens (i.e. from the session)
|
// do not overwrite already stored tokens (i.e. from the session)
|
||||||
return null === $this->tokenStorage->getToken();
|
// the `null` return value indicates that this authenticator supports lazy firewalls
|
||||||
|
return null === $this->tokenStorage->getToken() ? null : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCredentials(Request $request)
|
public function getCredentials(Request $request)
|
||||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Security\Http\Firewall;
|
|||||||
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||||
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
|
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
|
||||||
@ -30,10 +31,8 @@ use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
|
|||||||
*
|
*
|
||||||
* @experimental in 5.1
|
* @experimental in 5.1
|
||||||
*/
|
*/
|
||||||
class AuthenticatorManagerListener
|
class AuthenticatorManagerListener extends AbstractListener
|
||||||
{
|
{
|
||||||
use AuthenticatorManagerListenerTrait;
|
|
||||||
|
|
||||||
private $authenticationManager;
|
private $authenticationManager;
|
||||||
private $authenticatorHandler;
|
private $authenticatorHandler;
|
||||||
private $authenticators;
|
private $authenticators;
|
||||||
@ -54,15 +53,58 @@ class AuthenticatorManagerListener
|
|||||||
$this->eventDispatcher = $eventDispatcher;
|
$this->eventDispatcher = $eventDispatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(RequestEvent $requestEvent)
|
public function supports(Request $request): ?bool
|
||||||
{
|
{
|
||||||
$request = $requestEvent->getRequest();
|
if (null !== $this->logger) {
|
||||||
$authenticators = $this->getSupportingAuthenticators($request);
|
$context = ['firewall_key' => $this->providerKey];
|
||||||
|
|
||||||
|
if ($this->authenticators instanceof \Countable || \is_array($this->authenticators)) {
|
||||||
|
$context['authenticators'] = \count($this->authenticators);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->logger->debug('Checking for guard authentication credentials.', $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
[$authenticators, $lazy] = $this->getSupportingAuthenticators($request);
|
||||||
|
if (!$authenticators) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$request->attributes->set('_guard_authenticators', $authenticators);
|
||||||
|
|
||||||
|
return $lazy ? null : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authenticate(RequestEvent $event)
|
||||||
|
{
|
||||||
|
$request = $event->getRequest();
|
||||||
|
$authenticators = $request->attributes->get('_guard_authenticators');
|
||||||
|
$request->attributes->remove('_guard_authenticators');
|
||||||
if (!$authenticators) {
|
if (!$authenticators) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->executeAuthenticators($authenticators, $requestEvent);
|
$this->executeAuthenticators($authenticators, $event);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getSupportingAuthenticators(Request $request): array
|
||||||
|
{
|
||||||
|
$authenticators = [];
|
||||||
|
$lazy = true;
|
||||||
|
foreach ($this->authenticators as $key => $authenticator) {
|
||||||
|
if (null !== $this->logger) {
|
||||||
|
$this->logger->debug('Checking support on authenticator.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($authenticator)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false !== $supports = $authenticator->supports($request)) {
|
||||||
|
$authenticators[$key] = $authenticator;
|
||||||
|
$lazy = $lazy && null === $supports;
|
||||||
|
} elseif (null !== $this->logger) {
|
||||||
|
$this->logger->debug('Authenticator does not support the request.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($authenticator)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$authenticators, $lazy];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -71,6 +113,15 @@ class AuthenticatorManagerListener
|
|||||||
protected function executeAuthenticators(array $authenticators, RequestEvent $event): void
|
protected function executeAuthenticators(array $authenticators, RequestEvent $event): void
|
||||||
{
|
{
|
||||||
foreach ($authenticators as $key => $authenticator) {
|
foreach ($authenticators as $key => $authenticator) {
|
||||||
|
// recheck if the authenticator still supports the listener. support() is called
|
||||||
|
// eagerly (before token storage is initialized), whereas authenticate() is called
|
||||||
|
// lazily (after initialization). This is important for e.g. the AnonymousAuthenticator
|
||||||
|
// as its support is relying on the (initialized) token in the TokenStorage.
|
||||||
|
if (false === $authenticator->supports($event->getRequest())) {
|
||||||
|
$this->logger->debug('Skipping the "{authenticator}" authenticator as it did not support the request.', ['authenticator' => \get_class($authenticator)]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$this->executeAuthenticator($key, $authenticator, $event);
|
$this->executeAuthenticator($key, $authenticator, $event);
|
||||||
|
|
||||||
if ($event->hasResponse()) {
|
if ($event->hasResponse()) {
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
<?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\Component\Security\Http\Firewall;
|
|
||||||
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Ryan Weaver <ryan@knpuniversity.com>
|
|
||||||
* @author Amaury Leroux de Lens <amaury@lerouxdelens.com>
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
trait AuthenticatorManagerListenerTrait
|
|
||||||
{
|
|
||||||
protected function getSupportingAuthenticators(Request $request): array
|
|
||||||
{
|
|
||||||
$authenticators = [];
|
|
||||||
foreach ($this->authenticators as $key => $authenticator) {
|
|
||||||
if (null !== $this->logger) {
|
|
||||||
$this->logger->debug('Checking support on authenticator.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($authenticator)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($authenticator->supports($request)) {
|
|
||||||
$authenticators[$key] = $authenticator;
|
|
||||||
} elseif (null !== $this->logger) {
|
|
||||||
$this->logger->debug('Authenticator does not support the request.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($authenticator)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $authenticators;
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user