Skip to content

Commit 303edcd

Browse files
committed
[Security] Move TraceableAuthenticator to security-http
1 parent 344e7e7 commit 303edcd

File tree

3 files changed

+228
-0
lines changed

3 files changed

+228
-0
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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\Component\Security\Http\Authenticator\Debug;
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+
* Collects info about an authenticator for debugging purposes.
29+
*
30+
* @author Robin Chalas <robin.chalas@gmail.com>
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+
$class = \get_class($this->authenticator instanceof GuardBridgeAuthenticator ? $this->authenticator->getGuardAuthenticator() : $this->authenticator);
47+
48+
return [
49+
'supports' => true,
50+
'passport' => $this->passport,
51+
'duration' => $this->duration,
52+
'stub' => $this->stub ?? $this->stub = class_exists(ClassStub::class) ? new ClassStub($class) : $class,
53+
];
54+
}
55+
56+
public function supports(Request $request): ?bool
57+
{
58+
return $this->authenticator->supports($request);
59+
}
60+
61+
public function authenticate(Request $request): Passport
62+
{
63+
$startTime = microtime(true);
64+
$this->passport = $this->authenticator->authenticate($request);
65+
$this->duration = microtime(true) - $startTime;
66+
67+
return $this->passport;
68+
}
69+
70+
public function createToken(Passport $passport, string $firewallName): TokenInterface
71+
{
72+
return method_exists($this->authenticator, 'createToken') ? $this->authenticator->createToken($passport, $firewallName) : $this->authenticator->createAuthenticatedToken($passport, $firewallName);
73+
}
74+
75+
public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface
76+
{
77+
return $this->authenticator->createAuthenticatedToken($passport, $firewallName);
78+
}
79+
80+
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
81+
{
82+
return $this->authenticator->onAuthenticationSuccess($request, $token, $firewallName);
83+
}
84+
85+
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
86+
{
87+
return $this->authenticator->onAuthenticationFailure($request, $exception);
88+
}
89+
90+
public function start(Request $request, AuthenticationException $authException = null): Response
91+
{
92+
if (!$this->authenticator instanceof AuthenticationEntryPointInterface) {
93+
throw new NotAnEntryPointException();
94+
}
95+
96+
return $this->authenticator->start($request, $authException);
97+
}
98+
99+
public function isInteractive(): bool
100+
{
101+
return $this->authenticator instanceof InteractiveAuthenticatorInterface && $this->authenticator->isInteractive();
102+
}
103+
104+
public function __call($method, $args)
105+
{
106+
return $this->authenticator->{$method}(...$args);
107+
}
108+
}
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\Component\Security\Http\Authenticator\Debug;
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+
final class TraceableAuthenticatorManagerListener extends AbstractListener
26+
{
27+
private $authenticationManagerListener;
28+
private $authenticatorsInfo = [];
29+
private $hasVardumper;
30+
31+
public function __construct(AuthenticatorManagerListener $authenticationManagerListener)
32+
{
33+
$this->authenticationManagerListener = $authenticationManagerListener;
34+
$this->hasVardumper = class_exists(ClassStub::class);
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' => $this->hasVardumper ? new ClassStub(\get_class($skippedAuthenticator)) : \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+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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\Component\Security\Http\Tests\Authenticator\Debug;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
17+
use Symfony\Component\Security\Http\Authenticator\Debug\TraceableAuthenticator;
18+
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
19+
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
20+
21+
class TraceableAuthenticatorTest extends TestCase
22+
{
23+
public function testGetInfo()
24+
{
25+
$request = new Request();
26+
$passport = new SelfValidatingPassport(new UserBadge('robin', function () {}));
27+
28+
$authenticator = $this->createMock(AuthenticatorInterface::class);
29+
$authenticator
30+
->expects($this->once())
31+
->method('authenticate')
32+
->with($request)
33+
->willReturn($passport);
34+
35+
$traceable = new TraceableAuthenticator($authenticator);
36+
$this->assertSame($passport, $traceable->authenticate($request));
37+
$this->assertSame($passport, $traceable->getInfo()['passport']);
38+
}
39+
}

0 commit comments

Comments
 (0)