Skip to content

Commit 4205f1b

Browse files
committed
Passing the newly generated security token to the event during user switching.
Event allows listeners to easily switch out the token if custom token updates are required
1 parent 701d41c commit 4205f1b

File tree

4 files changed

+63
-3
lines changed

4 files changed

+63
-3
lines changed

src/Symfony/Component/Security/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ CHANGELOG
44
3.4.0
55
-----
66

7+
* added a `setToken()` method to the `SwitchUserEvent` class to allow to replace the created token while switching users
8+
when custom token generation is required by application.
79
* Using voters that do not implement the `VoterInterface`is now deprecated in
810
the `AccessDecisionManager` and this functionality will be removed in 4.0.
911

src/Symfony/Component/Security/Http/Event/SwitchUserEvent.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Security\Http\Event;
1313

1414
use Symfony\Component\HttpFoundation\Request;
15+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
1516
use Symfony\Component\Security\Core\User\UserInterface;
1617
use Symfony\Component\EventDispatcher\Event;
1718

@@ -24,11 +25,13 @@ class SwitchUserEvent extends Event
2425
{
2526
private $request;
2627
private $targetUser;
28+
private $token;
2729

28-
public function __construct(Request $request, UserInterface $targetUser)
30+
public function __construct(Request $request, UserInterface $targetUser, TokenInterface $token = null)
2931
{
3032
$this->request = $request;
3133
$this->targetUser = $targetUser;
34+
$this->token = $token;
3235
}
3336

3437
/**
@@ -46,4 +49,17 @@ public function getTargetUser()
4649
{
4750
return $this->targetUser;
4851
}
52+
53+
/**
54+
* @return TokenInterface|null
55+
*/
56+
public function getToken()
57+
{
58+
return $this->token;
59+
}
60+
61+
public function setToken(TokenInterface $token)
62+
{
63+
$this->token = $token;
64+
}
4965
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,10 @@ private function attemptSwitchUser(Request $request)
145145
$token = new UsernamePasswordToken($user, $user->getPassword(), $this->providerKey, $roles);
146146

147147
if (null !== $this->dispatcher) {
148-
$switchEvent = new SwitchUserEvent($request, $token->getUser());
148+
$switchEvent = new SwitchUserEvent($request, $token->getUser(), $token);
149149
$this->dispatcher->dispatch(SecurityEvents::SWITCH_USER, $switchEvent);
150+
// use the token from the event in case any listeners have replaced it.
151+
$token = $switchEvent->getToken();
150152
}
151153

152154
return $token;
@@ -169,8 +171,9 @@ private function attemptExitUser(Request $request)
169171

170172
if (null !== $this->dispatcher && $original->getUser() instanceof UserInterface) {
171173
$user = $this->provider->refreshUser($original->getUser());
172-
$switchEvent = new SwitchUserEvent($request, $user);
174+
$switchEvent = new SwitchUserEvent($request, $user, $original);
173175
$this->dispatcher->dispatch(SecurityEvents::SWITCH_USER, $switchEvent);
176+
$original = $switchEvent->getToken();
174177
}
175178

176179
return $original;

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,4 +227,43 @@ public function testSwitchUserKeepsOtherQueryStringParameters()
227227
$this->assertSame('page=3&section=2', $this->request->server->get('QUERY_STRING'));
228228
$this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $this->tokenStorage->getToken());
229229
}
230+
231+
public function testSwitchUserWithReplacedToken()
232+
{
233+
$user = new User('username', 'password', array());
234+
$token = new UsernamePasswordToken($user, '', 'provider123', array('ROLE_FOO'));
235+
236+
$user = new User('replaced', 'password', array());
237+
$replacedToken = new UsernamePasswordToken($user, '', 'provider123', array('ROLE_BAR'));
238+
239+
$this->tokenStorage->setToken($token);
240+
$this->request->query->set('_switch_user', 'kuba');
241+
242+
$this->accessDecisionManager->expects($this->any())
243+
->method('decide')->with($token, array('ROLE_ALLOWED_TO_SWITCH'))
244+
->will($this->returnValue(true));
245+
246+
$this->userProvider->expects($this->any())
247+
->method('loadUserByUsername')->with('kuba')
248+
->will($this->returnValue($user));
249+
250+
$dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
251+
$dispatcher
252+
->expects($this->once())
253+
->method('dispatch')
254+
->with(SecurityEvents::SWITCH_USER,
255+
$this->callback(function (SwitchUserEvent $event) use ($replacedToken, $user) {
256+
if ($user !== $event->getTargetUser()) {
257+
return false;
258+
}
259+
$event->setToken($replacedToken);
260+
261+
return true;
262+
}));
263+
264+
$listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager, null, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', $dispatcher);
265+
$listener->handle($this->event);
266+
267+
$this->assertSame($replacedToken, $this->tokenStorage->getToken());
268+
}
230269
}

0 commit comments

Comments
 (0)