Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d0ca257

Browse files
committed
[Security][WIP] Add authenticators info to the profiler
1 parent 6b1d9b8 commit d0ca257

File tree

11 files changed

+310
-15
lines changed

11 files changed

+310
-15
lines changed

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

Lines 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
*/
Lines 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 <[email protected]>
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 <[email protected]>
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+
}
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 <[email protected]>
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 <[email protected]>
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

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 <[email protected]>
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

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
}

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

Lines 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

Lines 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)