From 4205f1bc686c75f0a925134642f797acd17962c1 Mon Sep 17 00:00:00 2001 From: VJ Date: Wed, 19 Apr 2017 12:13:04 -0400 Subject: [PATCH] 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 --- src/Symfony/Component/Security/CHANGELOG.md | 2 + .../Security/Http/Event/SwitchUserEvent.php | 18 ++++++++- .../Http/Firewall/SwitchUserListener.php | 7 +++- .../Tests/Firewall/SwitchUserListenerTest.php | 39 +++++++++++++++++++ 4 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Security/CHANGELOG.md b/src/Symfony/Component/Security/CHANGELOG.md index 292c304fc68c..7d30f22f777e 100644 --- a/src/Symfony/Component/Security/CHANGELOG.md +++ b/src/Symfony/Component/Security/CHANGELOG.md @@ -4,6 +4,8 @@ CHANGELOG 3.4.0 ----- + * added a `setToken()` method to the `SwitchUserEvent` class to allow to replace the created token while switching users + when custom token generation is required by application. * Using voters that do not implement the `VoterInterface`is now deprecated in the `AccessDecisionManager` and this functionality will be removed in 4.0. diff --git a/src/Symfony/Component/Security/Http/Event/SwitchUserEvent.php b/src/Symfony/Component/Security/Http/Event/SwitchUserEvent.php index 7cf94a70d313..2681fb6c602b 100644 --- a/src/Symfony/Component/Security/Http/Event/SwitchUserEvent.php +++ b/src/Symfony/Component/Security/Http/Event/SwitchUserEvent.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Security\Http\Event; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\EventDispatcher\Event; @@ -24,11 +25,13 @@ class SwitchUserEvent extends Event { private $request; private $targetUser; + private $token; - public function __construct(Request $request, UserInterface $targetUser) + public function __construct(Request $request, UserInterface $targetUser, TokenInterface $token = null) { $this->request = $request; $this->targetUser = $targetUser; + $this->token = $token; } /** @@ -46,4 +49,17 @@ public function getTargetUser() { return $this->targetUser; } + + /** + * @return TokenInterface|null + */ + public function getToken() + { + return $this->token; + } + + public function setToken(TokenInterface $token) + { + $this->token = $token; + } } diff --git a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php index 2caab2a35363..2fc06140cfd3 100644 --- a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php @@ -145,8 +145,10 @@ private function attemptSwitchUser(Request $request) $token = new UsernamePasswordToken($user, $user->getPassword(), $this->providerKey, $roles); if (null !== $this->dispatcher) { - $switchEvent = new SwitchUserEvent($request, $token->getUser()); + $switchEvent = new SwitchUserEvent($request, $token->getUser(), $token); $this->dispatcher->dispatch(SecurityEvents::SWITCH_USER, $switchEvent); + // use the token from the event in case any listeners have replaced it. + $token = $switchEvent->getToken(); } return $token; @@ -169,8 +171,9 @@ private function attemptExitUser(Request $request) if (null !== $this->dispatcher && $original->getUser() instanceof UserInterface) { $user = $this->provider->refreshUser($original->getUser()); - $switchEvent = new SwitchUserEvent($request, $user); + $switchEvent = new SwitchUserEvent($request, $user, $original); $this->dispatcher->dispatch(SecurityEvents::SWITCH_USER, $switchEvent); + $original = $switchEvent->getToken(); } return $original; diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php index 57cba1127a9b..c97eda759330 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/SwitchUserListenerTest.php @@ -227,4 +227,43 @@ public function testSwitchUserKeepsOtherQueryStringParameters() $this->assertSame('page=3§ion=2', $this->request->server->get('QUERY_STRING')); $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $this->tokenStorage->getToken()); } + + public function testSwitchUserWithReplacedToken() + { + $user = new User('username', 'password', array()); + $token = new UsernamePasswordToken($user, '', 'provider123', array('ROLE_FOO')); + + $user = new User('replaced', 'password', array()); + $replacedToken = new UsernamePasswordToken($user, '', 'provider123', array('ROLE_BAR')); + + $this->tokenStorage->setToken($token); + $this->request->query->set('_switch_user', 'kuba'); + + $this->accessDecisionManager->expects($this->any()) + ->method('decide')->with($token, array('ROLE_ALLOWED_TO_SWITCH')) + ->will($this->returnValue(true)); + + $this->userProvider->expects($this->any()) + ->method('loadUserByUsername')->with('kuba') + ->will($this->returnValue($user)); + + $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock(); + $dispatcher + ->expects($this->once()) + ->method('dispatch') + ->with(SecurityEvents::SWITCH_USER, + $this->callback(function (SwitchUserEvent $event) use ($replacedToken, $user) { + if ($user !== $event->getTargetUser()) { + return false; + } + $event->setToken($replacedToken); + + return true; + })); + + $listener = new SwitchUserListener($this->tokenStorage, $this->userProvider, $this->userChecker, 'provider123', $this->accessDecisionManager, null, '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', $dispatcher); + $listener->handle($this->event); + + $this->assertSame($replacedToken, $this->tokenStorage->getToken()); + } }