Skip to content

Navigation Menu

Sign in
Appearance settings

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 fc17f10

Browse filesBrowse files
committed
[Security][WIP] Add authenticators info to the profiler
1 parent 626d9aa commit fc17f10
Copy full SHA for fc17f10

File tree

12 files changed

+651
-261
lines changed
Filter options

12 files changed

+651
-261
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
*/
+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)
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
+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.