Skip to content

Commit ecdeab8

Browse files
SimperfitRobin Chalas
authored andcommitted
[Security] Dispatch an event when "logout user on change" steps in
1 parent 98929dc commit ecdeab8

File tree

4 files changed

+82
-1
lines changed

4 files changed

+82
-1
lines changed

src/Symfony/Component/Security/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ CHANGELOG
2323
* Dispatch `SwitchUserEvent` on `security.switch_user`
2424
* Deprecated `Argon2iPasswordEncoder`, use `SodiumPasswordEncoder` instead
2525
* Deprecated `BCryptPasswordEncoder`, use `NativePasswordEncoder` instead
26+
* Added `DeauthenticatedEvent` dispatched in case the user has changed when trying to refresh it
2627

2728
4.2.0
2829
-----
@@ -32,7 +33,7 @@ CHANGELOG
3233
* Passing custom class names to the
3334
`Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver` to define
3435
custom anonymous and remember me token classes is deprecated. To
35-
use custom tokens, extend the existing `Symfony\Component\Security\Core\Authentication\Token\AnonymousToken`
36+
use custom tokens, extend the ex~~~~isting `Symfony\Component\Security\Core\Authentication\Token\AnonymousToken`
3637
or `Symfony\Component\Security\Core\Authentication\Token\RememberMeToken`.
3738
* allow passing null as $filter in LdapUserProvider to get the default filter
3839
* accessing the user object that is not an instance of `UserInterface` from `Security::getUser()` is deprecated
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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\Event;
13+
14+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
15+
use Symfony\Component\Security\Core\User\UserInterface;
16+
use Symfony\Contracts\EventDispatcher\Event;
17+
18+
/**
19+
* Deauthentication happens in case the user has changed when trying to refresh it.
20+
*
21+
* @author Hamza Amrouche <hamza.simperfit@gmail.com>
22+
*/
23+
class DeauthenticatedEvent extends Event
24+
{
25+
private $token;
26+
private $user;
27+
28+
public function __construct(TokenInterface $token, UserInterface $user)
29+
{
30+
$this->token = $token;
31+
$this->user = $user;
32+
}
33+
34+
/**
35+
* @return TokenInterface The token containing the refreshed (changed) user
36+
*/
37+
public function getToken(): TokenInterface
38+
{
39+
return $this->token;
40+
}
41+
42+
/**
43+
* @return UserInterface The original user
44+
*/
45+
public function getUser(): UserInterface
46+
{
47+
return $this->user;
48+
}
49+
}

src/Symfony/Component/Security/Http/Firewall/ContextListener.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
use Symfony\Component\Security\Core\Role\SwitchUserRole;
2929
use Symfony\Component\Security\Core\User\UserInterface;
3030
use Symfony\Component\Security\Core\User\UserProviderInterface;
31+
use Symfony\Component\Security\Http\Event\DeauthenticatedEvent;
3132

3233
/**
3334
* ContextListener manages the SecurityContext persistence through a session.
@@ -225,6 +226,11 @@ protected function refreshUser(TokenInterface $token)
225226
$this->logger->debug('Token was deauthenticated after trying to refresh it.');
226227
}
227228

229+
if (null !== $this->dispatcher) {
230+
$token->setUser($refreshedUser);
231+
$this->dispatcher->dispatch(new DeauthenticatedEvent($token, $user), DeauthenticatedEvent::class);
232+
}
233+
228234
return null;
229235
}
230236

src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
use Symfony\Component\Security\Core\User\User;
3232
use Symfony\Component\Security\Core\User\UserInterface;
3333
use Symfony\Component\Security\Core\User\UserProviderInterface;
34+
use Symfony\Component\Security\Http\Event\DeauthenticatedEvent;
3435
use Symfony\Component\Security\Http\Firewall\ContextListener;
3536

3637
class ContextListenerTest extends TestCase
@@ -313,6 +314,30 @@ public function testAcceptsProvidersAsTraversable()
313314
$this->assertSame($refreshedUser, $tokenStorage->getToken()->getUser());
314315
}
315316

317+
public function testDeauthenticatedEvent()
318+
{
319+
$tokenStorage = new TokenStorage();
320+
$refreshedUser = new User('foobar', 'baz');
321+
322+
$user = new User('foo', 'bar');
323+
$session = new Session(new MockArraySessionStorage());
324+
$session->set('_security_context_key', serialize(new UsernamePasswordToken($user, '', 'context_key', ['ROLE_USER'])));
325+
326+
$request = new Request();
327+
$request->setSession($session);
328+
$request->cookies->set('MOCKSESSID', true);
329+
330+
$eventDispatcher = new EventDispatcher();
331+
$eventDispatcher->addListener(DeauthenticatedEvent::class, function ($event) use ($user) {
332+
$this->assertEquals($event->getWrappedEvent()->getUser(), $user);
333+
});
334+
335+
$listener = new ContextListener($tokenStorage, [new NotSupportingUserProvider(), new SupportingUserProvider($refreshedUser)], 'context_key', null, $eventDispatcher);
336+
$listener(new RequestEvent($this->getMockBuilder(HttpKernelInterface::class)->getMock(), $request, HttpKernelInterface::MASTER_REQUEST));
337+
338+
$this->assertNull($tokenStorage->getToken());
339+
}
340+
316341
protected function runSessionOnKernelResponse($newToken, $original = null)
317342
{
318343
$session = new Session(new MockArraySessionStorage());

0 commit comments

Comments
 (0)