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 d0ca257

Browse filesBrowse files
committed
[Security][WIP] Add authenticators info to the profiler
1 parent 6b1d9b8 commit d0ca257
Copy full SHA for d0ca257

File tree

11 files changed

+310
-15
lines changed
Filter options

11 files changed

+310
-15
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php
+13-1Lines changed: 13 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,10 @@ public function collect(Request $request, Response $response, \Throwable $except
215215
}
216216

217217
$this->data['authenticator_manager_enabled'] = $this->authenticatorManagerEnabled;
218+
$this->data['authenticators'] = [];
219+
if ($this->firewall) {
220+
$this->data['authenticators'] = $this->firewall->getAuthenticatorsInfo();
221+
}
218222
}
219223

220224
/**
@@ -370,6 +374,14 @@ public function getListeners()
370374
return $this->data['listeners'];
371375
}
372376

377+
/**
378+
* @return array|Data
379+
*/
380+
public function getAuthenticators()
381+
{
382+
return $this->data['authenticators'];
383+
}
384+
373385
/**
374386
* {@inheritdoc}
375387
*/
+102Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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\Passport\Passport;
21+
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
22+
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
23+
use Symfony\Component\Security\Http\EntryPoint\Exception\NotAnEntryPointException;
24+
use Symfony\Component\VarDumper\Caster\ClassStub;
25+
26+
/**
27+
* @author Robin Chalas <robin.chalas@gmail.com>
28+
*
29+
* @internal
30+
*/
31+
final class TraceableAuthenticator implements AuthenticatorInterface, AuthenticationEntryPointInterface
32+
{
33+
private $authenticator;
34+
private $passport;
35+
private $duration;
36+
private $stub;
37+
38+
public function __construct(AuthenticatorInterface $authenticator)
39+
{
40+
$this->authenticator = $authenticator;
41+
}
42+
43+
public function getInfo(): array
44+
{
45+
$authenticator = $this->authenticator instanceof GuardBridgeAuthenticator ? $this->authenticator->getGuardAuthenticator() : $this->authenticator;
46+
47+
return [
48+
'supports' => true,
49+
'passport' => $this->passport,
50+
'duration' => $this->duration,
51+
'stub' => $this->stub ?? $this->stub = new ClassStub(\get_class($authenticator), $authenticator)
52+
];
53+
}
54+
55+
public function supports(Request $request): ?bool
56+
{
57+
return $this->authenticator->supports($request);
58+
}
59+
60+
public function authenticate(Request $request): PassportInterface
61+
{
62+
$startTime = microtime(true);
63+
$this->passport = $this->authenticator->authenticate($request);
64+
$this->duration = microtime(true) - $startTime;
65+
66+
return $this->passport;
67+
}
68+
69+
public function createToken(Passport $passport, string $firewallName): TokenInterface
70+
{
71+
return method_exists($this->authenticator, 'createToken') ? $this->authenticator->createToken($passport, $firewallName) : $this->authenticator->createAuthenticatedToken($passport, $firewallName);
72+
}
73+
74+
public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface
75+
{
76+
return $this->authenticator->createAuthenticatedToken($passport, $firewallName);
77+
}
78+
79+
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
80+
{
81+
return $this->authenticator->onAuthenticationSuccess($request, $token, $firewallName);
82+
}
83+
84+
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
85+
{
86+
return $this->authenticator->onAuthenticationFailure($request, $exception);
87+
}
88+
89+
public function start(Request $request, AuthenticationException $authException = null): Response
90+
{
91+
if (!$this->authenticator instanceof AuthenticationEntryPointInterface) {
92+
throw new NotAnEntryPointException();
93+
}
94+
95+
return $this->authenticator->start($request, $authException);
96+
}
97+
98+
public function __call($method, $args)
99+
{
100+
return $this->authenticator->{$method}(...$args);
101+
}
102+
}
+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)
43+
{
44+
$request = $event->getRequest();
45+
46+
if (!$authenticators = $request->attributes->get('_security_authenticators')) {
47+
return null;
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()
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
+17-1Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
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;
17+
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\CustomAuthenticatorFactory;
1618
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FirewallListenerFactoryInterface;
1719
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
1820
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface;
@@ -504,6 +506,14 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
504506
->replaceArgument(0, new Reference($managerId))
505507
;
506508

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

543553
foreach ($this->getSortedFactories() as $factory) {
544554
$key = str_replace('-', '_', $factory->getKey());
545-
if (\array_key_exists($key, $firewall)) {
555+
if ('custom_authenticators' !== $key && \array_key_exists($key, $firewall)) {
546556
$listenerKeys[] = $key;
547557
}
548558
}
549559

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

‎src/Symfony/Bundle/SecurityBundle/Resources/config/security_debug.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Resources/config/security_debug.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
1313

14+
use Symfony\Bundle\SecurityBundle\Debug\Authenticator\TraceableAuthenticatorManager;
1415
use Symfony\Bundle\SecurityBundle\Debug\TraceableFirewallListener;
1516
use Symfony\Bundle\SecurityBundle\EventListener\VoteListener;
1617
use Symfony\Component\Security\Core\Authorization\TraceableAccessDecisionManager;

0 commit comments

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