Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7a41b05

Browse filesBrowse files
committed
feature #42582 [Security] Add authenticators info to the profiler (chalasr)
This PR was merged into the 5.4 branch. Discussion ---------- [Security] Add authenticators info to the profiler | Q | A | ------------- | --- | Branch? | 5.4 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | - | License | MIT | Doc PR | - The profiler's security panel needs an update regarding the not-so-new authenticator manager system. Here it is: <img width="1532" alt="Screenshot 2021-10-10 at 21 09 40" src="https://user-images.githubusercontent.com/7502063/136709926-8b97cca4-5f51-4495-9b93-4408fa11ece3.png"> Commits ------- 3ec5e96 [Security][WIP] Add authenticators info to the profiler
2 parents acbb8ed + 3ec5e96 commit 7a41b05
Copy full SHA for 7a41b05

File tree

16 files changed

+669
-274
lines changed
Filter options

16 files changed

+669
-274
lines changed

‎UPGRADE-5.4.md

Copy file name to clipboardExpand all lines: UPGRADE-5.4.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ Messenger
5151
SecurityBundle
5252
--------------
5353

54+
* Deprecate `FirewallConfig::getListeners()`, use `FirewallConfig::getAuthenticators()` instead
5455
* Deprecate `security.authentication.basic_entry_point` and `security.authentication.retry_entry_point` services, the logic is moved into the
5556
`HttpBasicAuthenticator` and `ChannelListener` respectively
5657
* Deprecate not setting `$authenticatorManagerEnabled` to `true` in `SecurityDataCollector` and `DebugFirewallCommand`

‎UPGRADE-6.0.md

Copy file name to clipboardExpand all lines: UPGRADE-6.0.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ Security
394394
SecurityBundle
395395
--------------
396396

397+
* Remove `FirewallConfig::getListeners()`, use `FirewallConfig::getAuthenticators()` instead
397398
* Remove `security.authentication.basic_entry_point` and `security.authentication.retry_entry_point` services,
398399
the logic is moved into the `HttpBasicAuthenticator` and `ChannelListener` respectively
399400
* Remove `SecurityFactoryInterface` and `SecurityExtension::addSecurityListenerFactory()` in favor of

‎src/Symfony/Bundle/SecurityBundle/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
5.4
55
---
66

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

‎src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php
+10-1Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ public function collect(Request $request, Response $response, \Throwable $except
194194
'access_denied_handler' => $firewallConfig->getAccessDeniedHandler(),
195195
'access_denied_url' => $firewallConfig->getAccessDeniedUrl(),
196196
'user_checker' => $firewallConfig->getUserChecker(),
197-
'listeners' => $firewallConfig->getListeners(),
197+
'authenticators' => $firewallConfig->getAuthenticators(),
198198
];
199199

200200
// generate exit impersonation path from current request
@@ -215,6 +215,7 @@ public function collect(Request $request, Response $response, \Throwable $except
215215
}
216216

217217
$this->data['authenticator_manager_enabled'] = $this->authenticatorManagerEnabled;
218+
$this->data['authenticators'] = $this->firewall ? $this->firewall->getAuthenticatorsInfo() : [];
218219
}
219220

220221
/**
@@ -370,6 +371,14 @@ public function getListeners()
370371
return $this->data['listeners'];
371372
}
372373

374+
/**
375+
* @return array|Data
376+
*/
377+
public function getAuthenticators()
378+
{
379+
return $this->data['authenticators'];
380+
}
381+
373382
/**
374383
* {@inheritdoc}
375384
*/
+106Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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\Guard\Authenticator\GuardBridgeAuthenticator;
19+
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
20+
use Symfony\Component\Security\Http\Authenticator\InteractiveAuthenticatorInterface;
21+
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
22+
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
23+
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
24+
use Symfony\Component\Security\Http\EntryPoint\Exception\NotAnEntryPointException;
25+
use Symfony\Component\VarDumper\Caster\ClassStub;
26+
27+
/**
28+
* @author Robin Chalas <robin.chalas@gmail.com>
29+
*
30+
* @internal
31+
*/
32+
final class TraceableAuthenticator implements AuthenticatorInterface, InteractiveAuthenticatorInterface, AuthenticationEntryPointInterface
33+
{
34+
private $authenticator;
35+
private $passport;
36+
private $duration;
37+
private $stub;
38+
39+
public function __construct(AuthenticatorInterface $authenticator)
40+
{
41+
$this->authenticator = $authenticator;
42+
}
43+
44+
public function getInfo(): array
45+
{
46+
return [
47+
'supports' => true,
48+
'passport' => $this->passport,
49+
'duration' => $this->duration,
50+
'stub' => $this->stub ?? $this->stub = new ClassStub(\get_class($this->authenticator instanceof GuardBridgeAuthenticator ? $this->authenticator->getGuardAuthenticator() : $this->authenticator)),
51+
];
52+
}
53+
54+
public function supports(Request $request): ?bool
55+
{
56+
return $this->authenticator->supports($request);
57+
}
58+
59+
public function authenticate(Request $request): PassportInterface
60+
{
61+
$startTime = microtime(true);
62+
$this->passport = $this->authenticator->authenticate($request);
63+
$this->duration = microtime(true) - $startTime;
64+
65+
return $this->passport;
66+
}
67+
68+
public function createToken(Passport $passport, string $firewallName): TokenInterface
69+
{
70+
return method_exists($this->authenticator, 'createToken') ? $this->authenticator->createToken($passport, $firewallName) : $this->authenticator->createAuthenticatedToken($passport, $firewallName);
71+
}
72+
73+
public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface
74+
{
75+
return $this->authenticator->createAuthenticatedToken($passport, $firewallName);
76+
}
77+
78+
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
79+
{
80+
return $this->authenticator->onAuthenticationSuccess($request, $token, $firewallName);
81+
}
82+
83+
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
84+
{
85+
return $this->authenticator->onAuthenticationFailure($request, $exception);
86+
}
87+
88+
public function start(Request $request, AuthenticationException $authException = null): Response
89+
{
90+
if (!$this->authenticator instanceof AuthenticationEntryPointInterface) {
91+
throw new NotAnEntryPointException();
92+
}
93+
94+
return $this->authenticator->start($request, $authException);
95+
}
96+
97+
public function isInteractive(): bool
98+
{
99+
return $this->authenticator instanceof InteractiveAuthenticatorInterface && $this->authenticator->isInteractive();
100+
}
101+
102+
public function __call($method, $args)
103+
{
104+
return $this->authenticator->{$method}(...$args);
105+
}
106+
}
+81Lines 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+
}

‎src/Symfony/Bundle/SecurityBundle/Debug/TraceableFirewallListener.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Debug/TraceableFirewallListener.php
+20-2Lines 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
}

‎src/Symfony/Bundle/SecurityBundle/Debug/TraceableListenerTrait.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Debug/TraceableListenerTrait.php
+2-1Lines 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
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+16-1Lines 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\Factory\SecurityFactoryInterface;
@@ -504,6 +505,14 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
504505
->replaceArgument(0, new Reference($managerId))
505506
;
506507

508+
if ($container->hasDefinition('debug.security.firewall') && $this->authenticatorManagerEnabled) {
509+
$container
510+
->register('debug.security.firewall.authenticator.'.$id, TraceableAuthenticatorManagerListener::class)
511+
->setDecoratedService('security.firewall.authenticator.'.$id)
512+
->setArguments([new Reference('debug.security.firewall.authenticator.'.$id.'.inner')])
513+
;
514+
}
515+
507516
// user checker listener
508517
$container
509518
->setDefinition('security.listener.user_checker.'.$id, new ChildDefinition('security.listener.user_checker'))
@@ -542,11 +551,17 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
542551

543552
foreach ($this->getSortedFactories() as $factory) {
544553
$key = str_replace('-', '_', $factory->getKey());
545-
if (\array_key_exists($key, $firewall)) {
554+
if ('custom_authenticators' !== $key && \array_key_exists($key, $firewall)) {
546555
$listenerKeys[] = $key;
547556
}
548557
}
549558

559+
if ($firewall['custom_authenticators'] ?? false) {
560+
foreach ($firewall['custom_authenticators'] as $customAuthenticatorId) {
561+
$listenerKeys[] = $customAuthenticatorId;
562+
}
563+
}
564+
550565
$config->replaceArgument(10, $listenerKeys);
551566
$config->replaceArgument(11, $firewall['switch_user'] ?? null);
552567

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.