Skip to content

Commit b21b514

Browse files
committed
Added GuardAuthenticationListener
This replaces all individual authentication listeners when guard authentication manager is enabled.
1 parent 001d573 commit b21b514

File tree

6 files changed

+297
-122
lines changed

6 files changed

+297
-122
lines changed

src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
2929
use Symfony\Component\DependencyInjection\Reference;
3030
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
31+
use Symfony\Component\HttpKernel\KernelEvents;
3132
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
3233
use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder;
3334
use Symfony\Component\Security\Core\Encoder\SodiumPasswordEncoder;
@@ -262,11 +263,24 @@ private function createFirewalls(array $config, ContainerBuilder $container)
262263
// add authentication providers to authentication manager
263264
$authenticationProviders = array_map(function ($id) {
264265
return new Reference($id);
265-
}, array_values(array_unique($authenticationProviders)));
266+
}, array_unique($authenticationProviders));
266267
$authenticationManagerId = 'security.authentication.manager.provider';
267268
if ($this->guardAuthenticationManagerEnabled) {
268269
$authenticationManagerId = 'security.authentication.manager.guard';
269270
$container->setAlias('security.authentication.manager', new Alias($authenticationManagerId));
271+
272+
// guard authentication manager listener
273+
$container
274+
->setDefinition('security.firewall.guard.'.$name.'locator', new ChildDefinition('security.firewall.guard.locator'))
275+
->setArguments([$authenticationProviders])
276+
->addTag('container.service_locator')
277+
;
278+
$container
279+
->setDefinition('security.firewall.guard.'.$name, new ChildDefinition('security.firewall.guard'))
280+
->replaceArgument(2, new Reference('security.firewall.guard.'.$name.'locator'))
281+
->replaceArgument(3, $name)
282+
->addTag('kernel.event_listener', ['event' => KernelEvents::REQUEST])
283+
;
270284
}
271285
$container
272286
->getDefinition($authenticationManagerId)
@@ -477,7 +491,7 @@ private function createAuthenticationListeners(ContainerBuilder $container, stri
477491
list($provider, $listenerId, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint);
478492

479493
$listeners[] = new Reference($listenerId);
480-
$authenticationProviders[] = $provider;
494+
$authenticationProviders[$id.'_'.$key] = $provider;
481495
}
482496
$hasListeners = true;
483497
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
namespace Symfony\Bundle\SecurityBundle\EventListener;
4+
5+
use Psr\Log\LoggerInterface;
6+
use Symfony\Component\DependencyInjection\ServiceLocator;
7+
use Symfony\Component\HttpFoundation\Request;
8+
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
9+
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
10+
use Symfony\Component\Security\Http\Firewall\GuardManagerListener;
11+
12+
/**
13+
* @author Wouter de Jong <wouter@wouterj.nl>
14+
*/
15+
class LazyGuardManagerListener extends GuardManagerListener
16+
{
17+
private $guardLocator;
18+
19+
public function __construct(
20+
AuthenticationManagerInterface $authenticationManager,
21+
GuardAuthenticatorHandler $guardHandler,
22+
ServiceLocator $guardLocator,
23+
string $providerKey,
24+
?LoggerInterface $logger = null
25+
) {
26+
parent::__construct($authenticationManager, $guardHandler, [], $providerKey, $logger);
27+
28+
$this->guardLocator = $guardLocator;
29+
}
30+
31+
protected function getSupportingGuardAuthenticators(Request $request): array
32+
{
33+
$guardAuthenticators = [];
34+
foreach ($this->guardLocator->getProvidedServices() as $key => $type) {
35+
$guardAuthenticator = $this->guardLocator->get($key);
36+
if (null !== $this->logger) {
37+
$this->logger->debug('Checking support on guard authenticator.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)]);
38+
}
39+
40+
if ($guardAuthenticator->supports($request)) {
41+
$guardAuthenticators[$key] = $guardAuthenticator;
42+
} elseif (null !== $this->logger) {
43+
$this->logger->debug('Guard authenticator does not support the request.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)]);
44+
}
45+
}
46+
47+
return $guardAuthenticators;
48+
}
49+
}

src/Symfony/Bundle/SecurityBundle/Resources/config/authenticators.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@
44
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
55

66
<services>
7+
<service id="security.firewall.guard.locator"
8+
class="Symfony\Component\DependencyInjection\ServiceLocator"
9+
abstract="true" />
10+
11+
<service id="security.firewall.guard"
12+
class="Symfony\Bundle\SecurityBundle\EventListener\LazyGuardManagerListener"
13+
abstract="true">
14+
<tag name="monolog.logger" channel="security" />
15+
<argument type="service" id="security.authentication.manager"/>
16+
<argument type="service" id="security.authentication.guard_handler"/>
17+
<argument/> <!-- guard authenticator locator -->
18+
<argument/> <!-- provider key -->
19+
<argument type="service" id="logger" on-invalid="null" />
20+
</service>
21+
722
<service id="security.authenticator.basic"
823
class="Symfony\Component\Security\Core\Authentication\Authenticator\HttpBasicAuthenticator"
924
abstract="true">

src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php

Lines changed: 8 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
*/
3535
class GuardAuthenticationListener extends AbstractListener
3636
{
37+
use GuardAuthenticatorListenerTrait;
38+
3739
private $guardHandler;
3840
private $authenticationManager;
3941
private $providerKey;
@@ -73,20 +75,7 @@ public function supports(Request $request): ?bool
7375
$this->logger->debug('Checking for guard authentication credentials.', $context);
7476
}
7577

76-
$guardAuthenticators = [];
77-
78-
foreach ($this->guardAuthenticators as $key => $guardAuthenticator) {
79-
if (null !== $this->logger) {
80-
$this->logger->debug('Checking support on guard authenticator.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)]);
81-
}
82-
83-
if ($guardAuthenticator->supports($request)) {
84-
$guardAuthenticators[$key] = $guardAuthenticator;
85-
} elseif (null !== $this->logger) {
86-
$this->logger->debug('Guard authenticator does not support the request.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)]);
87-
}
88-
}
89-
78+
$guardAuthenticators = $this->getSupportingGuardAuthenticators($request);
9079
if (!$guardAuthenticators) {
9180
return false;
9281
}
@@ -105,86 +94,7 @@ public function authenticate(RequestEvent $event)
10594
$guardAuthenticators = $request->attributes->get('_guard_authenticators');
10695
$request->attributes->remove('_guard_authenticators');
10796

108-
foreach ($guardAuthenticators as $key => $guardAuthenticator) {
109-
// get a key that's unique to *this* guard authenticator
110-
// this MUST be the same as GuardAuthenticationProvider
111-
$uniqueGuardKey = $this->providerKey.'_'.$key;
112-
113-
$this->executeGuardAuthenticator($uniqueGuardKey, $guardAuthenticator, $event);
114-
115-
if ($event->hasResponse()) {
116-
if (null !== $this->logger) {
117-
$this->logger->debug('The "{authenticator}" authenticator set the response. Any later authenticator will not be called', ['authenticator' => \get_class($guardAuthenticator)]);
118-
}
119-
120-
break;
121-
}
122-
}
123-
}
124-
125-
private function executeGuardAuthenticator(string $uniqueGuardKey, AuthenticatorInterface $guardAuthenticator, RequestEvent $event)
126-
{
127-
$request = $event->getRequest();
128-
try {
129-
if (null !== $this->logger) {
130-
$this->logger->debug('Calling getCredentials() on guard authenticator.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)]);
131-
}
132-
133-
// allow the authenticator to fetch authentication info from the request
134-
$credentials = $guardAuthenticator->getCredentials($request);
135-
136-
if (null === $credentials) {
137-
throw new \UnexpectedValueException(sprintf('The return value of "%1$s::getCredentials()" must not be null. Return false from "%1$s::supports()" instead.', \get_class($guardAuthenticator)));
138-
}
139-
140-
// create a token with the unique key, so that the provider knows which authenticator to use
141-
$token = new PreAuthenticationGuardToken($credentials, $uniqueGuardKey);
142-
143-
if (null !== $this->logger) {
144-
$this->logger->debug('Passing guard token information to the GuardAuthenticationProvider', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)]);
145-
}
146-
// pass the token into the AuthenticationManager system
147-
// this indirectly calls GuardAuthenticationProvider::authenticate()
148-
$token = $this->authenticationManager->authenticate($token);
149-
150-
if (null !== $this->logger) {
151-
$this->logger->info('Guard authentication successful!', ['token' => $token, 'authenticator' => \get_class($guardAuthenticator)]);
152-
}
153-
154-
// sets the token on the token storage, etc
155-
$this->guardHandler->authenticateWithToken($token, $request, $this->providerKey);
156-
} catch (AuthenticationException $e) {
157-
// oh no! Authentication failed!
158-
159-
if (null !== $this->logger) {
160-
$this->logger->info('Guard authentication failed.', ['exception' => $e, 'authenticator' => \get_class($guardAuthenticator)]);
161-
}
162-
163-
$response = $this->guardHandler->handleAuthenticationFailure($e, $request, $guardAuthenticator, $this->providerKey);
164-
165-
if ($response instanceof Response) {
166-
$event->setResponse($response);
167-
}
168-
169-
return;
170-
}
171-
172-
// success!
173-
$response = $this->guardHandler->handleAuthenticationSuccess($token, $request, $guardAuthenticator, $this->providerKey);
174-
if ($response instanceof Response) {
175-
if (null !== $this->logger) {
176-
$this->logger->debug('Guard authenticator set success response.', ['response' => $response, 'authenticator' => \get_class($guardAuthenticator)]);
177-
}
178-
179-
$event->setResponse($response);
180-
} else {
181-
if (null !== $this->logger) {
182-
$this->logger->debug('Guard authenticator set no success response: request continues.', ['authenticator' => \get_class($guardAuthenticator)]);
183-
}
184-
}
185-
186-
// attempt to trigger the remember me functionality
187-
$this->triggerRememberMe($guardAuthenticator, $request, $token, $response);
97+
$this->executeGuardAuthenticators($guardAuthenticators, $event);
18898
}
18999

190100
/**
@@ -195,32 +105,10 @@ public function setRememberMeServices(RememberMeServicesInterface $rememberMeSer
195105
$this->rememberMeServices = $rememberMeServices;
196106
}
197107

198-
/**
199-
* Checks to see if remember me is supported in the authenticator and
200-
* on the firewall. If it is, the RememberMeServicesInterface is notified.
201-
*/
202-
private function triggerRememberMe(AuthenticatorInterface $guardAuthenticator, Request $request, TokenInterface $token, Response $response = null)
108+
protected function getGuardKey(string $key): string
203109
{
204-
if (null === $this->rememberMeServices) {
205-
if (null !== $this->logger) {
206-
$this->logger->debug('Remember me skipped: it is not configured for the firewall.', ['authenticator' => \get_class($guardAuthenticator)]);
207-
}
208-
209-
return;
210-
}
211-
212-
if (!$guardAuthenticator->supportsRememberMe()) {
213-
if (null !== $this->logger) {
214-
$this->logger->debug('Remember me skipped: your authenticator does not support it.', ['authenticator' => \get_class($guardAuthenticator)]);
215-
}
216-
217-
return;
218-
}
219-
220-
if (!$response instanceof Response) {
221-
throw new \LogicException(sprintf('%s::onAuthenticationSuccess *must* return a Response if you want to use the remember me functionality. Return a Response, or set remember_me to false under the guard configuration.', \get_class($guardAuthenticator)));
222-
}
223-
224-
$this->rememberMeServices->loginSuccess($request, $response, $token);
110+
// get a key that's unique to *this* guard authenticator
111+
// this MUST be the same as GuardAuthenticationProvider
112+
return $this->providerKey.'_'.$key;
225113
}
226114
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy