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'))
|
||||
->replaceArgument(2, new Reference('security.firewall.authenticator.'.$id.'.locator'))
|
||||
->replaceArgument(3, $id)
|
||||
->addTag('kernel.event_listener', ['event' => KernelEvents::REQUEST])
|
||||
;
|
||||
|
||||
$listeners[] = new Reference('security.firewall.authenticator.'.$id);
|
||||
|
@ -26,37 +26,39 @@ use Symfony\Component\Security\Http\Firewall\AuthenticatorManagerListener;
|
||||
*/
|
||||
class LazyAuthenticatorManagerListener extends AuthenticatorManagerListener
|
||||
{
|
||||
private $guardLocator;
|
||||
private $authenticatorLocator;
|
||||
|
||||
public function __construct(
|
||||
AuthenticationManagerInterface $authenticationManager,
|
||||
AuthenticatorHandler $authenticatorHandler,
|
||||
ServiceLocator $guardLocator,
|
||||
ServiceLocator $authenticatorLocator,
|
||||
string $providerKey,
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
?LoggerInterface $logger = null
|
||||
) {
|
||||
parent::__construct($authenticationManager, $authenticatorHandler, [], $providerKey, $eventDispatcher, $logger);
|
||||
|
||||
$this->guardLocator = $guardLocator;
|
||||
$this->authenticatorLocator = $authenticatorLocator;
|
||||
}
|
||||
|
||||
protected function getSupportingAuthenticators(Request $request): array
|
||||
{
|
||||
$guardAuthenticators = [];
|
||||
foreach ($this->guardLocator->getProvidedServices() as $key => $type) {
|
||||
$guardAuthenticator = $this->guardLocator->get($key);
|
||||
$authenticators = [];
|
||||
$lazy = true;
|
||||
foreach ($this->authenticatorLocator->getProvidedServices() as $key => $type) {
|
||||
$authenticator = $this->authenticatorLocator->get($key);
|
||||
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)) {
|
||||
$guardAuthenticators[$key] = $guardAuthenticator;
|
||||
if (false !== $supports = $authenticator->supports($request)) {
|
||||
$authenticators[$key] = $authenticator;
|
||||
$lazy = $lazy && null === $supports;
|
||||
} 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"
|
||||
abstract="true">
|
||||
<argument type="abstract">secret</argument>
|
||||
<argument type="service" id="security.token_storage" />
|
||||
<argument type="service" id="security.untracked_token_storage" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
@ -34,8 +34,6 @@ use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
|
||||
*/
|
||||
class GuardAuthenticationListener extends AbstractListener
|
||||
{
|
||||
use AuthenticatorManagerListenerTrait;
|
||||
|
||||
private $guardHandler;
|
||||
private $authenticationManager;
|
||||
private $providerKey;
|
||||
@ -75,7 +73,19 @@ class GuardAuthenticationListener extends AbstractListener
|
||||
$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) {
|
||||
return false;
|
||||
}
|
||||
|
@ -41,7 +41,8 @@ class AnonymousAuthenticator implements AuthenticatorInterface, CustomAuthentica
|
||||
public function supports(Request $request): ?bool
|
||||
{
|
||||
// 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)
|
||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Security\Http\Firewall;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
|
||||
@ -30,10 +31,8 @@ use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
|
||||
*
|
||||
* @experimental in 5.1
|
||||
*/
|
||||
class AuthenticatorManagerListener
|
||||
class AuthenticatorManagerListener extends AbstractListener
|
||||
{
|
||||
use AuthenticatorManagerListenerTrait;
|
||||
|
||||
private $authenticationManager;
|
||||
private $authenticatorHandler;
|
||||
private $authenticators;
|
||||
@ -54,15 +53,58 @@ class AuthenticatorManagerListener
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
public function __invoke(RequestEvent $requestEvent)
|
||||
public function supports(Request $request): ?bool
|
||||
{
|
||||
$request = $requestEvent->getRequest();
|
||||
$authenticators = $this->getSupportingAuthenticators($request);
|
||||
if (null !== $this->logger) {
|
||||
$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) {
|
||||
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
|
||||
{
|
||||
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);
|
||||
|
||||
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