From 180e2c78783428b463f59539e86ea2b1ffa26081 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 17 May 2015 17:29:55 -0400 Subject: [PATCH] Properly handles "post auth" tokens that have become not authenticated Here is the flow: A) You login using guard and are given a PostAuthGuardToken B) Your user changes between requests - AbstractToken::setUser() and hasUserChanged() - which results in the Token becoming "not authenticated" C) Something calls out to the security system, which then passes the no-longer-authed token back into the AuthenticationProviderManager D) Because the PostauthGuardToken implements GuardTokenInterface, the provider responds to it. But, seeing that this is a no-longer-authed PostAuthGuardToken, it returns an AnonymousToken, which triggers logout --- .../Provider/GuardAuthenticationProvider.php | 13 +++++++++++++ .../GuardAuthenticationProviderTest.php | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php index 150943dffa..aa9a78dde8 100644 --- a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php @@ -55,6 +55,19 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface throw new \InvalidArgumentException('GuardAuthenticationProvider only supports NonAuthenticatedGuardToken'); } + if (!$token instanceof PreAuthenticationGuardToken) { + /* + * The listener *only* passes PreAuthenticationGuardToken instances. + * This means that an authenticated token (e.g. PostAuthenticationGuardToken) + * is being passed here, which happens if that token becomes + * "not authenticated" (e.g. happens if the user changes between + * requests). In this case, the user should be logged out, so + * we will return an AnonymousToken to accomplish that. + */ + + return new AnonymousToken($this->providerKey, 'anon.'); + } + // find the *one* GuardAuthenticator that this token originated from foreach ($this->guardAuthenticators as $key => $guardAuthenticator) { // get a key that's unique to *this* guard authenticator diff --git a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php index 7df3ecb9d6..99e9b5d206 100644 --- a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php @@ -11,7 +11,9 @@ namespace Symfony\Component\Security\Guard\Tests\Provider; +use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Guard\Provider\GuardAuthenticationProvider; +use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken; /** * @author Ryan Weaver @@ -75,6 +77,22 @@ class GuardAuthenticationProviderTest extends \PHPUnit_Framework_TestCase $this->assertSame($authedToken, $actualAuthedToken); } + public function testGuardWithNoLongerAuthenticatedTriggersLogout() + { + $providerKey = 'my_firewall_abc'; + + // create a token and mark it as NOT authenticated anymore + // this mimics what would happen if a user "changed" between request + $mockedUser = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $token = new PostAuthenticationGuardToken($mockedUser, $providerKey, array('ROLE_USER')); + $token->setAuthenticated(false); + + $provider = new GuardAuthenticationProvider(array(), $this->userProvider, $providerKey, $this->userChecker); + $actualToken = $provider->authenticate($token); + // this should return the anonymous user + $this->assertEquals(new AnonymousToken($providerKey, 'anon.'), $actualToken); + } + protected function setUp() { $this->userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface');