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 b3d37d2

Browse filesBrowse files
committed
initial work on more advanced authentication success sensitive event
Signed-off-by: Rob Frawley 2nd <rmf@src.run>
1 parent 21cf149 commit b3d37d2
Copy full SHA for b3d37d2

File tree

4 files changed

+191
-14
lines changed
Filter options

4 files changed

+191
-14
lines changed

‎src/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.php
+4-1Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Security\Core\Authentication;
1313

14+
use Symfony\Component\EventDispatcher\Event;
1415
use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
1516
use Symfony\Component\Security\Core\Event\AuthenticationEvent;
1617
use Symfony\Component\Security\Core\AuthenticationEvents;
@@ -67,6 +68,7 @@ public function authenticate(TokenInterface $token)
6768
{
6869
$lastException = null;
6970
$result = null;
71+
$providerClassName = null;
7072

7173
foreach ($this->providers as $provider) {
7274
if (!$provider instanceof AuthenticationProviderInterface) {
@@ -81,6 +83,7 @@ public function authenticate(TokenInterface $token)
8183
$result = $provider->authenticate($token);
8284

8385
if (null !== $result) {
86+
$providerClassName = get_class($provider);
8487
break;
8588
}
8689
} catch (AccountStatusException $e) {
@@ -94,7 +97,7 @@ public function authenticate(TokenInterface $token)
9497

9598
if (null !== $result) {
9699
if (null !== $this->eventDispatcher) {
97-
$this->eventDispatcher->dispatch(AuthenticationEvents::AUTHENTICATION_SUCCESS_SENSITIVE, new AuthenticationSensitiveEvent($result));
100+
$this->eventDispatcher->dispatch(AuthenticationEvents::AUTHENTICATION_SUCCESS_SENSITIVE, new AuthenticationSensitiveEvent($result, $token, $providerClassName));
98101
}
99102

100103
if (true === $this->eraseCredentials) {

‎src/Symfony/Component/Security/Core/Event/AuthenticationSensitiveEvent.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Core/Event/AuthenticationSensitiveEvent.php
+57-1Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,67 @@
1111

1212
namespace Symfony\Component\Security\Core\Event;
1313

14+
use Symfony\Component\EventDispatcher\Event;
15+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
16+
1417
/**
1518
* This is an authentication event that includes sensitive data.
1619
*
1720
* @author Rob Frawley 2nd <rmf@src.run>
1821
*/
19-
class AuthenticationSensitiveEvent extends AuthenticationEvent
22+
class AuthenticationSensitiveEvent extends Event
2023
{
24+
private $authenticationToken;
25+
private $preAuthenticationToken;
26+
private $authenticationProviderClassName;
27+
28+
public function __construct(TokenInterface $authenticationToken, TokenInterface $preAuthenticationToken, string $authenticationProviderClassName = null)
29+
{
30+
$this->authenticationToken = $authenticationToken;
31+
$this->preAuthenticationToken = $preAuthenticationToken;
32+
$this->authenticationProviderClassName = $authenticationProviderClassName;
33+
}
34+
35+
public function getAuthenticationToken(): TokenInterface
36+
{
37+
return $this->authenticationToken;
38+
}
39+
40+
public function getPreAuthenticationToken(): TokenInterface
41+
{
42+
return $this->preAuthenticationToken;
43+
}
44+
45+
public function getAuthenticationProviderClassName(): ?string
46+
{
47+
return $this->authenticationProviderClassName;
48+
}
49+
50+
public function getAuthenticationTokenPassword(\Closure $extractor = null): ?string
51+
{
52+
return $this->extractAuthenticationTokenPassword($this->preAuthenticationToken, $extractor)
53+
?: $this->extractAuthenticationTokenPassword($this->authenticationToken, $extractor);
54+
}
55+
56+
private function extractAuthenticationTokenPassword(TokenInterface $token, \Closure $extractor = null): ?string
57+
{
58+
return ($extractor ?? function (TokenInterface $token): ?string {
59+
$c = $token->getCredentials();
60+
61+
return is_array($c) && (null !== $p = $this->resolveArrayAuthenticationTokenCredentialsPassword($c))
62+
? $p ?: null
63+
: $c ?: null;
64+
})($token, $this);
65+
}
66+
67+
private function resolveArrayAuthenticationTokenCredentialsPassword(array $credentials): ?string
68+
{
69+
foreach (array('password', 'secret', 'api_key') as $index) {
70+
if (isset($credentials[$index]) && !empty($credentials[$index])) {
71+
return $credentials[$index];
72+
}
73+
}
74+
75+
return null;
76+
}
2177
}

‎src/Symfony/Component/Security/Core/Tests/Authentication/AuthenticationProviderManagerTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Core/Tests/Authentication/AuthenticationProviderManagerTest.php
+21-12Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -157,41 +157,50 @@ public function testAuthenticateDispatchesAuthenticationFailureEvent()
157157

158158
public function testAuthenticateDispatchesAuthenticationSuccessEvents()
159159
{
160-
$token = new UsernamePasswordToken('foo', 'bar', 'key');
160+
$finalToken = new UsernamePasswordToken('foo', 'bar', 'baz', array('role-01', 'role-02'));
161+
$priorToken = new UsernamePasswordToken('foo', 'bar', 'baz');
162+
163+
$provider = $this->getAuthenticationProvider(true, $finalToken);
164+
$providerCN = get_class($provider);
161165

162166
$dispatcher = $this->getMockBuilder(EventDispatcherInterface::class)->getMock();
163167
$dispatcher
164168
->expects($this->exactly(2))
165169
->method('dispatch')
166170
->withConsecutive(array(
167-
AuthenticationEvents::AUTHENTICATION_SUCCESS_SENSITIVE, $this->equalTo(new AuthenticationSensitiveEvent($token)),
171+
AuthenticationEvents::AUTHENTICATION_SUCCESS_SENSITIVE, $this->equalTo(new AuthenticationSensitiveEvent($finalToken, $priorToken, $providerCN)),
168172
), array(
169-
AuthenticationEvents::AUTHENTICATION_SUCCESS, $this->equalTo(new AuthenticationEvent($token)),
173+
AuthenticationEvents::AUTHENTICATION_SUCCESS, $this->equalTo(new AuthenticationEvent($finalToken)),
170174
));
171175

172-
$manager = new AuthenticationProviderManager(array(
173-
$this->getAuthenticationProvider(true, $token),
174-
));
176+
$manager = new AuthenticationProviderManager(array($provider));
175177
$manager->setEventDispatcher($dispatcher);
176178

177-
$this->assertSame($token, $manager->authenticate($token));
179+
$this->assertSame($finalToken, $manager->authenticate($priorToken));
178180
}
179181

180182
public function testAuthenticateDispatchesAuthenticationSuccessEventsWithCredentialsAvailableAndRemovedForSuccessiveDispatches()
181183
{
184+
$finalToken = new UsernamePasswordToken('foo', 'bar', 'baz', array('role-01', 'role-02'));
185+
$priorToken = new UsernamePasswordToken('foo', 'bar', 'baz');
186+
187+
$provider = $this->getAuthenticationProvider(true, $finalToken);
188+
$providerCN = get_class($provider);
189+
182190
$dispatcher = new EventDispatcher();
183-
$dispatcher->addListener(AuthenticationEvents::AUTHENTICATION_SUCCESS_SENSITIVE, function (AuthenticationSensitiveEvent $event) {
191+
$dispatcher->addListener(AuthenticationEvents::AUTHENTICATION_SUCCESS_SENSITIVE, function (AuthenticationSensitiveEvent $event) use ($providerCN) {
192+
$this->assertSame($providerCN, $event->getAuthenticationProviderClassName());
193+
$this->assertSame('bar', $event->getAuthenticationTokenPassword());
194+
$this->assertEquals('bar', $event->getPreAuthenticationToken()->getCredentials());
184195
$this->assertEquals('bar', $event->getAuthenticationToken()->getCredentials());
185196
});
186197
$dispatcher->addListener(AuthenticationEvents::AUTHENTICATION_SUCCESS, function (AuthenticationEvent $event) {
187198
$this->assertEquals('', $event->getAuthenticationToken()->getCredentials());
188199
});
189200

190-
$manager = new AuthenticationProviderManager(array(
191-
$this->getAuthenticationProvider(true, $token = new UsernamePasswordToken('foo', 'bar', 'key')),
192-
));
201+
$manager = new AuthenticationProviderManager(array($provider));
193202
$manager->setEventDispatcher($dispatcher);
194-
$manager->authenticate($token);
203+
$manager->authenticate($priorToken);
195204
}
196205

197206
protected function getAuthenticationProvider($supports, $token = null, $exception = null)
+109Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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\Core\Tests\Authentication;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\EventDispatcher\EventDispatcher;
16+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
17+
use Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager;
18+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
19+
use Symfony\Component\Security\Core\AuthenticationEvents;
20+
use Symfony\Component\Security\Core\Event\AuthenticationEvent;
21+
use Symfony\Component\Security\Core\Event\AuthenticationSensitiveEvent;
22+
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
23+
24+
class AuthenticationSensitiveEventTest extends TestCase
25+
{
26+
public function testAuthenticateDispatchesAuthenticationSuccessEvents()
27+
{
28+
$finalToken = new UsernamePasswordToken('foo', 'bar', 'baz', array('role-01', 'role-02'));
29+
$priorToken = new UsernamePasswordToken('foo', 'bar', 'baz');
30+
31+
$provider = $this->getAuthenticationProvider(true, $finalToken);
32+
$providerCN = get_class($provider);
33+
34+
$dispatcher = $this->getMockBuilder(EventDispatcherInterface::class)->getMock();
35+
$dispatcher
36+
->expects($this->exactly(2))
37+
->method('dispatch')
38+
->withConsecutive(array(
39+
AuthenticationEvents::AUTHENTICATION_SUCCESS_SENSITIVE, $this->equalTo(new AuthenticationSensitiveEvent($finalToken, $priorToken, $providerCN)),
40+
), array(
41+
AuthenticationEvents::AUTHENTICATION_SUCCESS, $this->equalTo(new AuthenticationEvent($finalToken)),
42+
));
43+
44+
$manager = new AuthenticationProviderManager(array($provider));
45+
$manager->setEventDispatcher($dispatcher);
46+
47+
$this->assertSame($finalToken, $manager->authenticate($priorToken));
48+
}
49+
50+
public function testAuthenticateDispatchesAuthenticationSuccessEventsWithCredentialsAvailableAndRemovedForSuccessiveDispatches()
51+
{
52+
$finalToken = $this->getTokenInterfaceMock();
53+
$priorToken = $this->getTokenInterfaceMock($credentials = array('password' => 'foobar'));
54+
55+
$provider = $this->getAuthenticationProvider(true, $finalToken);
56+
$providerCN = get_class($provider);
57+
58+
$dispatcher = new EventDispatcher();
59+
$dispatcher->addListener(AuthenticationEvents::AUTHENTICATION_SUCCESS_SENSITIVE, function (AuthenticationSensitiveEvent $event) use ($credentials, $providerCN) {
60+
$this->assertSame($providerCN, $event->getAuthenticationProviderClassName());
61+
$this->assertSame('foobar', $event->getAuthenticationTokenPassword());
62+
$this->assertEquals($credentials, $event->getPreAuthenticationToken()->getCredentials());
63+
});
64+
$dispatcher->addListener(AuthenticationEvents::AUTHENTICATION_SUCCESS, function (AuthenticationEvent $event) {
65+
$this->assertEquals('', $event->getAuthenticationToken()->getCredentials());
66+
});
67+
68+
$manager = new AuthenticationProviderManager(array($provider));
69+
$manager->setEventDispatcher($dispatcher);
70+
$manager->authenticate($priorToken);
71+
}
72+
73+
private function getTokenInterfaceMock($credentials = null): TokenInterface
74+
{
75+
$token = $this->getMockBuilder(TokenInterface::class)->getMock();
76+
77+
if (null !== $credentials) {
78+
$token
79+
->expects($this->atLeastOnce())
80+
->method('getCredentials')
81+
->will($this->returnValue($credentials));
82+
}
83+
84+
return $token;
85+
}
86+
87+
private function getAuthenticationProvider($supports, $token = null, $exception = null)
88+
{
89+
$provider = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface')->getMock();
90+
$provider->expects($this->once())
91+
->method('supports')
92+
->will($this->returnValue($supports))
93+
;
94+
95+
if (null !== $token) {
96+
$provider->expects($this->once())
97+
->method('authenticate')
98+
->will($this->returnValue($token))
99+
;
100+
} elseif (null !== $exception) {
101+
$provider->expects($this->once())
102+
->method('authenticate')
103+
->will($this->throwException($this->getMockBuilder($exception)->setMethods(null)->getMock()))
104+
;
105+
}
106+
107+
return $provider;
108+
}
109+
}

0 commit comments

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