From 9dff22ca9911d4bc478b04933a571cfe95eb9be3 Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 23 Apr 2018 14:06:09 +0200 Subject: [PATCH] [Security] guardAuthenticationProvider::authenticate cannot return null according to interface specification --- .../Provider/GuardAuthenticationProvider.php | 57 +++++++++++-------- .../GuardAuthenticationProviderTest.php | 35 ++++++++++++ 2 files changed, 67 insertions(+), 25 deletions(-) diff --git a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php index 4347e02002..b9e1f66abb 100644 --- a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php @@ -13,6 +13,7 @@ namespace Symfony\Component\Security\Guard\Provider; use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; +use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Guard\GuardAuthenticatorInterface; @@ -63,7 +64,7 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface */ public function authenticate(TokenInterface $token) { - if (!$this->supports($token)) { + if (!$token instanceof GuardTokenInterface) { throw new \InvalidArgumentException('GuardAuthenticationProvider only supports GuardTokenInterface.'); } @@ -87,19 +88,13 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface throw new AuthenticationExpiredException(); } - // 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 - // this MUST be the same as GuardAuthenticationListener - $uniqueGuardKey = $this->providerKey.'_'.$key; + $guardAuthenticator = $this->findOriginatingAuthenticator($token); - if ($uniqueGuardKey == $token->getGuardProviderKey()) { - return $this->authenticateViaGuard($guardAuthenticator, $token); - } + if (null === $guardAuthenticator) { + throw new AuthenticationException(sprintf('Token with provider key "%s" did not originate from any of the guard authenticators of provider "%s".', $token->getGuardProviderKey(), $this->providerKey)); } - // no matching authenticator found - but there will be multiple GuardAuthenticationProvider - // instances that will be checked if you have multiple firewalls. + return $this->authenticateViaGuard($guardAuthenticator, $token); } private function authenticateViaGuard(GuardAuthenticatorInterface $guardAuthenticator, PreAuthenticationGuardToken $token) @@ -108,18 +103,11 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface $user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider); if (null === $user) { - throw new UsernameNotFoundException(sprintf( - 'Null returned from %s::getUser()', - get_class($guardAuthenticator) - )); + throw new UsernameNotFoundException(sprintf('Null returned from %s::getUser()', get_class($guardAuthenticator))); } if (!$user instanceof UserInterface) { - throw new \UnexpectedValueException(sprintf( - 'The %s::getUser() method must return a UserInterface. You returned %s.', - get_class($guardAuthenticator), - is_object($user) ? get_class($user) : gettype($user) - )); + throw new \UnexpectedValueException(sprintf('The %s::getUser() method must return a UserInterface. You returned %s.', get_class($guardAuthenticator), is_object($user) ? get_class($user) : gettype($user))); } $this->userChecker->checkPreAuth($user); @@ -131,18 +119,37 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface // turn the UserInterface into a TokenInterface $authenticatedToken = $guardAuthenticator->createAuthenticatedToken($user, $this->providerKey); if (!$authenticatedToken instanceof TokenInterface) { - throw new \UnexpectedValueException(sprintf( - 'The %s::createAuthenticatedToken() method must return a TokenInterface. You returned %s.', - get_class($guardAuthenticator), - is_object($authenticatedToken) ? get_class($authenticatedToken) : gettype($authenticatedToken) - )); + throw new \UnexpectedValueException(sprintf('The %s::createAuthenticatedToken() method must return a TokenInterface. You returned %s.', get_class($guardAuthenticator), is_object($authenticatedToken) ? get_class($authenticatedToken) : gettype($authenticatedToken))); } return $authenticatedToken; } + private function findOriginatingAuthenticator(PreAuthenticationGuardToken $token) + { + // 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 + // this MUST be the same as GuardAuthenticationListener + $uniqueGuardKey = $this->providerKey.'_'.$key; + + if ($uniqueGuardKey === $token->getGuardProviderKey()) { + return $guardAuthenticator; + } + } + + // no matching authenticator found - but there will be multiple GuardAuthenticationProvider + // instances that will be checked if you have multiple firewalls. + + return null; + } + public function supports(TokenInterface $token) { + if ($token instanceof PreAuthenticationGuardToken) { + return null !== $this->findOriginatingAuthenticator($token); + } + return $token instanceof GuardTokenInterface; } } diff --git a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php index ed3920533f..847947110d 100644 --- a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php @@ -14,6 +14,7 @@ namespace Symfony\Component\Security\Guard\Tests\Provider; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Guard\Provider\GuardAuthenticationProvider; use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken; +use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; /** * @author Ryan Weaver @@ -133,6 +134,40 @@ class GuardAuthenticationProviderTest extends TestCase $actualToken = $provider->authenticate($token); } + public function testSupportsChecksGuardAuthenticatorsTokenOrigin() + { + $authenticatorA = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); + $authenticatorB = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); + $authenticators = array($authenticatorA, $authenticatorB); + + $mockedUser = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); + $provider = new GuardAuthenticationProvider($authenticators, $this->userProvider, 'first_firewall', $this->userChecker); + + $token = new PreAuthenticationGuardToken($mockedUser, 'first_firewall_1'); + $supports = $provider->supports($token); + $this->assertTrue($supports); + + $token = new PreAuthenticationGuardToken($mockedUser, 'second_firewall_0'); + $supports = $provider->supports($token); + $this->assertFalse($supports); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationException + * @expectedExceptionMessageRegExp /second_firewall_0/ + */ + public function testAuthenticateFailsOnNonOriginatingToken() + { + $authenticatorA = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); + $authenticators = array($authenticatorA); + + $mockedUser = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); + $provider = new GuardAuthenticationProvider($authenticators, $this->userProvider, 'first_firewall', $this->userChecker); + + $token = new PreAuthenticationGuardToken($mockedUser, 'second_firewall_0'); + $provider->authenticate($token); + } + protected function setUp() { $this->userProvider = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserProviderInterface')->getMock();