Skip to content

Commit bf63a33

Browse files
committed
Merge branch '5.4' into 6.0
* 5.4: [Security][WIP] Add authenticators info to the profiler [Translation] Use symfony default locale when pulling translations from providers added missing translations for Bosnian (bs) Add the missing greek translations for security core and validator component [HttpKernel] Fix return types in `EventDataCollector` Do not call substr_count() if ip is null to avoid deprecation warning in PHP 8.1 [Security][Validator] Add missing translations for Slovenian (sl) [Messenger] Add worker metadata inside logs [Messenger] Log when worker should stop and when `SIGTERM` is received cs fix [Security][Validator] Add missing translations for Finnish (fi) [VarDumper] returns a 500 when dd() is executed chore(VarDumper): declare that dd() never returns [MonologBridge] Deprecate the Swiftmailer handler [Cache] Commit items implicitly only when deferred keys are requested [MonologBridge] Deprecates ResetLoggersWorkerSubscriber Fix "can not" spelling [HttpClient] fix missing kernel.reset tag on TraceableHttpClient services [Form] Fix ChoiceType Extension to effectively set and use the translator Added new CssColor constraint
2 parents ee97a0b + f6277c7 commit bf63a33

File tree

12 files changed

+648
-266
lines changed

12 files changed

+648
-266
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ CHANGELOG
1616
5.4
1717
---
1818

19+
* Deprecate `FirewallConfig::getListeners()`, use `FirewallConfig::getAuthenticators()` instead
1920
* Deprecate `security.authentication.basic_entry_point` and `security.authentication.retry_entry_point` services, the logic is moved into the
2021
`HttpBasicAuthenticator` and `ChannelListener` respectively
2122
* Deprecate `FirewallConfig::allowsAnonymous()` and the `allows_anonymous` from the data collector data, there will be no anonymous concept as of version 6.

DataCollector/SecurityDataCollector.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ public function collect(Request $request, Response $response, \Throwable $except
184184
'access_denied_handler' => $firewallConfig->getAccessDeniedHandler(),
185185
'access_denied_url' => $firewallConfig->getAccessDeniedUrl(),
186186
'user_checker' => $firewallConfig->getUserChecker(),
187-
'listeners' => $firewallConfig->getListeners(),
187+
'authenticators' => $firewallConfig->getAuthenticators(),
188188
];
189189

190190
// generate exit impersonation path from current request
@@ -203,6 +203,8 @@ public function collect(Request $request, Response $response, \Throwable $except
203203
if ($this->firewall) {
204204
$this->data['listeners'] = $this->firewall->getWrappedListeners();
205205
}
206+
207+
$this->data['authenticators'] = $this->firewall ? $this->firewall->getAuthenticatorsInfo() : [];
206208
}
207209

208210
/**
@@ -345,6 +347,11 @@ public function getListeners(): array|Data
345347
return $this->data['listeners'];
346348
}
347349

350+
public function getAuthenticators(): array|Data
351+
{
352+
return $this->data['authenticators'];
353+
}
354+
348355
/**
349356
* {@inheritdoc}
350357
*/
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\Debug\Authenticator;
13+
14+
use Symfony\Component\HttpFoundation\Request;
15+
use Symfony\Component\HttpFoundation\Response;
16+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
17+
use Symfony\Component\Security\Core\Exception\AuthenticationException;
18+
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
19+
use Symfony\Component\Security\Http\Authenticator\InteractiveAuthenticatorInterface;
20+
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
21+
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
22+
use Symfony\Component\Security\Http\EntryPoint\Exception\NotAnEntryPointException;
23+
use Symfony\Component\VarDumper\Caster\ClassStub;
24+
25+
/**
26+
* @author Robin Chalas <robin.chalas@gmail.com>
27+
*
28+
* @internal
29+
*/
30+
final class TraceableAuthenticator implements AuthenticatorInterface, InteractiveAuthenticatorInterface, AuthenticationEntryPointInterface
31+
{
32+
private $authenticator;
33+
private $passport;
34+
private $duration;
35+
private $stub;
36+
37+
public function __construct(AuthenticatorInterface $authenticator)
38+
{
39+
$this->authenticator = $authenticator;
40+
}
41+
42+
public function getInfo(): array
43+
{
44+
return [
45+
'supports' => true,
46+
'passport' => $this->passport,
47+
'duration' => $this->duration,
48+
'stub' => $this->stub ?? $this->stub = new ClassStub(\get_class($this->authenticator)),
49+
];
50+
}
51+
52+
public function supports(Request $request): ?bool
53+
{
54+
return $this->authenticator->supports($request);
55+
}
56+
57+
public function authenticate(Request $request): Passport
58+
{
59+
$startTime = microtime(true);
60+
$this->passport = $this->authenticator->authenticate($request);
61+
$this->duration = microtime(true) - $startTime;
62+
63+
return $this->passport;
64+
}
65+
66+
public function createToken(Passport $passport, string $firewallName): TokenInterface
67+
{
68+
return method_exists($this->authenticator, 'createToken') ? $this->authenticator->createToken($passport, $firewallName) : $this->authenticator->createAuthenticatedToken($passport, $firewallName);
69+
}
70+
71+
public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface
72+
{
73+
return $this->authenticator->createAuthenticatedToken($passport, $firewallName);
74+
}
75+
76+
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
77+
{
78+
return $this->authenticator->onAuthenticationSuccess($request, $token, $firewallName);
79+
}
80+
81+
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
82+
{
83+
return $this->authenticator->onAuthenticationFailure($request, $exception);
84+
}
85+
86+
public function start(Request $request, AuthenticationException $authException = null): Response
87+
{
88+
if (!$this->authenticator instanceof AuthenticationEntryPointInterface) {
89+
throw new NotAnEntryPointException();
90+
}
91+
92+
return $this->authenticator->start($request, $authException);
93+
}
94+
95+
public function isInteractive(): bool
96+
{
97+
return $this->authenticator instanceof InteractiveAuthenticatorInterface && $this->authenticator->isInteractive();
98+
}
99+
100+
public function __call($method, $args)
101+
{
102+
return $this->authenticator->{$method}(...$args);
103+
}
104+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\Debug\Authenticator;
13+
14+
use Symfony\Component\HttpFoundation\Request;
15+
use Symfony\Component\HttpKernel\Event\RequestEvent;
16+
use Symfony\Component\Security\Http\Firewall\AbstractListener;
17+
use Symfony\Component\Security\Http\Firewall\AuthenticatorManagerListener;
18+
use Symfony\Component\VarDumper\Caster\ClassStub;
19+
20+
/**
21+
* Decorates the AuthenticatorManagerListener to collect information about security authenticators.
22+
*
23+
* @author Robin Chalas <robin.chalas@gmail.com>
24+
*
25+
* @internal
26+
*/
27+
class TraceableAuthenticatorManagerListener extends AbstractListener
28+
{
29+
private $authenticationManagerListener;
30+
private $authenticatorsInfo = [];
31+
32+
public function __construct(AuthenticatorManagerListener $authenticationManagerListener)
33+
{
34+
$this->authenticationManagerListener = $authenticationManagerListener;
35+
}
36+
37+
public function supports(Request $request): ?bool
38+
{
39+
return $this->authenticationManagerListener->supports($request);
40+
}
41+
42+
public function authenticate(RequestEvent $event): void
43+
{
44+
$request = $event->getRequest();
45+
46+
if (!$authenticators = $request->attributes->get('_security_authenticators')) {
47+
return;
48+
}
49+
50+
foreach ($request->attributes->get('_security_skipped_authenticators') as $skippedAuthenticator) {
51+
$this->authenticatorsInfo[] = [
52+
'supports' => false,
53+
'stub' => new ClassStub(\get_class($skippedAuthenticator)),
54+
'passport' => null,
55+
'duration' => 0,
56+
];
57+
}
58+
59+
foreach ($authenticators as $key => $authenticator) {
60+
$authenticators[$key] = new TraceableAuthenticator($authenticator);
61+
}
62+
63+
$request->attributes->set('_security_authenticators', $authenticators);
64+
65+
$this->authenticationManagerListener->authenticate($event);
66+
67+
foreach ($authenticators as $authenticator) {
68+
$this->authenticatorsInfo[] = $authenticator->getInfo();
69+
}
70+
}
71+
72+
public function getAuthenticatorManagerListener(): AuthenticatorManagerListener
73+
{
74+
return $this->authenticationManagerListener;
75+
}
76+
77+
public function getAuthenticatorsInfo(): array
78+
{
79+
return $this->authenticatorsInfo;
80+
}
81+
}

Debug/TraceableFirewallListener.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,36 +11,47 @@
1111

1212
namespace Symfony\Bundle\SecurityBundle\Debug;
1313

14+
use Symfony\Bundle\SecurityBundle\Debug\Authenticator\TraceableAuthenticatorManagerListener;
1415
use Symfony\Bundle\SecurityBundle\EventListener\FirewallListener;
1516
use Symfony\Bundle\SecurityBundle\Security\FirewallContext;
1617
use Symfony\Bundle\SecurityBundle\Security\LazyFirewallContext;
1718
use Symfony\Component\HttpKernel\Event\RequestEvent;
1819
use Symfony\Component\Security\Http\Firewall\FirewallListenerInterface;
1920

2021
/**
21-
* Firewall collecting called listeners.
22+
* Firewall collecting called security listeners and authenticators.
2223
*
2324
* @author Robin Chalas <robin.chalas@gmail.com>
2425
*/
2526
final class TraceableFirewallListener extends FirewallListener
2627
{
2728
private $wrappedListeners = [];
29+
private $authenticatorsInfo = [];
2830

2931
public function getWrappedListeners()
3032
{
3133
return $this->wrappedListeners;
3234
}
3335

36+
public function getAuthenticatorsInfo(): array
37+
{
38+
return $this->authenticatorsInfo;
39+
}
40+
3441
protected function callListeners(RequestEvent $event, iterable $listeners)
3542
{
3643
$wrappedListeners = [];
3744
$wrappedLazyListeners = [];
45+
$authenticatorManagerListener = null;
3846

3947
foreach ($listeners as $listener) {
4048
if ($listener instanceof LazyFirewallContext) {
41-
\Closure::bind(function () use (&$wrappedLazyListeners, &$wrappedListeners) {
49+
\Closure::bind(function () use (&$wrappedLazyListeners, &$wrappedListeners, &$authenticatorManagerListener) {
4250
$listeners = [];
4351
foreach ($this->listeners as $listener) {
52+
if (!$authenticatorManagerListener && $listener instanceof TraceableAuthenticatorManagerListener) {
53+
$authenticatorManagerListener = $listener;
54+
}
4455
if ($listener instanceof FirewallListenerInterface) {
4556
$listener = new WrappedLazyListener($listener);
4657
$listeners[] = $listener;
@@ -61,6 +72,9 @@ protected function callListeners(RequestEvent $event, iterable $listeners)
6172
$wrappedListener = $listener instanceof FirewallListenerInterface ? new WrappedLazyListener($listener) : new WrappedListener($listener);
6273
$wrappedListener($event);
6374
$wrappedListeners[] = $wrappedListener->getInfo();
75+
if (!$authenticatorManagerListener && $listener instanceof TraceableAuthenticatorManagerListener) {
76+
$authenticatorManagerListener = $listener;
77+
}
6478
}
6579

6680
if ($event->hasResponse()) {
@@ -75,5 +89,9 @@ protected function callListeners(RequestEvent $event, iterable $listeners)
7589
}
7690

7791
$this->wrappedListeners = array_merge($this->wrappedListeners, $wrappedListeners);
92+
93+
if ($authenticatorManagerListener) {
94+
$this->authenticatorsInfo = $authenticatorManagerListener->getAuthenticatorsInfo();
95+
}
7896
}
7997
}

Debug/TraceableListenerTrait.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Bundle\SecurityBundle\Debug;
1313

14+
use Symfony\Bundle\SecurityBundle\Debug\Authenticator\TraceableAuthenticatorManagerListener;
1415
use Symfony\Component\VarDumper\Caster\ClassStub;
1516

1617
/**
@@ -43,7 +44,7 @@ public function getInfo(): array
4344
return [
4445
'response' => $this->response,
4546
'time' => $this->time,
46-
'stub' => $this->stub ?? $this->stub = ClassStub::wrapCallable($this->listener),
47+
'stub' => $this->stub ?? $this->stub = ClassStub::wrapCallable($this->listener instanceof TraceableAuthenticatorManagerListener ? $this->listener->getAuthenticatorManagerListener() : $this->listener),
4748
];
4849
}
4950
}

DependencyInjection/SecurityExtension.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Bundle\SecurityBundle\DependencyInjection;
1313

1414
use Symfony\Bridge\Twig\Extension\LogoutUrlExtension;
15+
use Symfony\Bundle\SecurityBundle\Debug\Authenticator\TraceableAuthenticatorManagerListener;
1516
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AuthenticatorFactoryInterface;
1617
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FirewallListenerFactoryInterface;
1718
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface;
@@ -429,6 +430,14 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
429430
->replaceArgument(0, new Reference($managerId))
430431
;
431432

433+
if ($container->hasDefinition('debug.security.firewall')) {
434+
$container
435+
->register('debug.security.firewall.authenticator.'.$id, TraceableAuthenticatorManagerListener::class)
436+
->setDecoratedService('security.firewall.authenticator.'.$id)
437+
->setArguments([new Reference('debug.security.firewall.authenticator.'.$id.'.inner')])
438+
;
439+
}
440+
432441
// user checker listener
433442
$container
434443
->setDefinition('security.listener.user_checker.'.$id, new ChildDefinition('security.listener.user_checker'))
@@ -466,11 +475,17 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
466475

467476
foreach ($this->getSortedFactories() as $factory) {
468477
$key = str_replace('-', '_', $factory->getKey());
469-
if (\array_key_exists($key, $firewall)) {
478+
if ('custom_authenticators' !== $key && \array_key_exists($key, $firewall)) {
470479
$listenerKeys[] = $key;
471480
}
472481
}
473482

483+
if ($firewall['custom_authenticators'] ?? false) {
484+
foreach ($firewall['custom_authenticators'] as $customAuthenticatorId) {
485+
$listenerKeys[] = $customAuthenticatorId;
486+
}
487+
}
488+
474489
$config->replaceArgument(10, $listenerKeys);
475490
$config->replaceArgument(11, $firewall['switch_user'] ?? null);
476491

0 commit comments

Comments
 (0)