diff --git a/UPGRADE-3.4.md b/UPGRADE-3.4.md index c0573eb219..3586268f6c 100644 --- a/UPGRADE-3.4.md +++ b/UPGRADE-3.4.md @@ -305,6 +305,9 @@ Security * Deprecated the HTTP digest authentication: `NonceExpiredException`, `DigestAuthenticationListener` and `DigestAuthenticationEntryPoint` will be removed in 4.0. Use another authentication system like `http_basic` instead. + + * The `GuardAuthenticatorInterface` has been deprecated and will be removed in 4.0. + Use `AuthenticatorInterface` instead. SecurityBundle -------------- diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index 5bc486f44f..31881d6325 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -679,6 +679,9 @@ Security `DigestAuthenticationListener` and `DigestAuthenticationEntryPoint` classes have been removed. Use another authentication system like `http_basic` instead. + * The `GuardAuthenticatorInterface` interface has been removed. + Use `AuthenticatorInterface` instead. + SecurityBundle -------------- diff --git a/src/Symfony/Component/Security/CHANGELOG.md b/src/Symfony/Component/Security/CHANGELOG.md index 25e89572a5..bd1b7b59eb 100644 --- a/src/Symfony/Component/Security/CHANGELOG.md +++ b/src/Symfony/Component/Security/CHANGELOG.md @@ -15,6 +15,7 @@ CHANGELOG requests. * deprecated HTTP digest authentication * Added a new password encoder for the Argon2i hashing algorithm + * deprecated `GuardAuthenticatorInterface` in favor of `AuthenticatorInterface` 3.3.0 ----- diff --git a/src/Symfony/Component/Security/Guard/AbstractGuardAuthenticator.php b/src/Symfony/Component/Security/Guard/AbstractGuardAuthenticator.php index 609d772e19..5eceb8470a 100644 --- a/src/Symfony/Component/Security/Guard/AbstractGuardAuthenticator.php +++ b/src/Symfony/Component/Security/Guard/AbstractGuardAuthenticator.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Guard; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken; @@ -19,8 +20,20 @@ use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken; * * @author Ryan Weaver */ -abstract class AbstractGuardAuthenticator implements GuardAuthenticatorInterface +abstract class AbstractGuardAuthenticator implements AuthenticatorInterface { + /** + * {@inheritdoc} + * + * @deprecated since version 3.4, to be removed in 4.0 + */ + public function supports(Request $request) + { + @trigger_error(sprintf('The "%s()" method is deprecated since version 3.4 and will be removed in 4.0. Implement the "%s::supports()" method in class "%s" instead.', __METHOD__, AuthenticatorInterface::class, get_class($this)), E_USER_DEPRECATED); + + return true; + } + /** * Shortcut to create a PostAuthenticationGuardToken for you, if you don't really * care about which authenticated token you're using. diff --git a/src/Symfony/Component/Security/Guard/AuthenticatorInterface.php b/src/Symfony/Component/Security/Guard/AuthenticatorInterface.php new file mode 100644 index 0000000000..f68b8d6f43 --- /dev/null +++ b/src/Symfony/Component/Security/Guard/AuthenticatorInterface.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Guard; + +use Symfony\Component\HttpFoundation\Request; + +/** + * The interface for all "guard" authenticators. + * + * The methods on this interface are called throughout the guard authentication + * process to give you the power to control most parts of the process from + * one location. + * + * @author Ryan Weaver + * @author Amaury Leroux de Lens + */ +interface AuthenticatorInterface extends GuardAuthenticatorInterface +{ + /** + * Does the authenticator support the given Request? + * + * If this returns false, the authenticator will be skipped. + * + * @param Request $request + * + * @return bool + */ + public function supports(Request $request); + + /** + * Get the authentication credentials from the request and return them + * as any type (e.g. an associate array). + * + * Whatever value you return here will be passed to getUser() and checkCredentials() + * + * For example, for a form login, you might: + * + * return array( + * 'username' => $request->request->get('_username'), + * 'password' => $request->request->get('_password'), + * ); + * + * Or for an API token that's on a header, you might use: + * + * return array('api_key' => $request->headers->get('X-API-TOKEN')); + * + * @param Request $request + * + * @return mixed Any non-null value + * + * @throws \UnexpectedValueException If null is returned + */ + public function getCredentials(Request $request); +} diff --git a/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php b/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php index 41a37e6fef..4ec0b8f326 100644 --- a/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php +++ b/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php @@ -15,9 +15,10 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\Security\Guard\GuardAuthenticatorHandler; +use Symfony\Component\Security\Guard\GuardAuthenticatorInterface; use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; -use Symfony\Component\Security\Guard\GuardAuthenticatorInterface; +use Symfony\Component\Security\Guard\AuthenticatorInterface; use Psr\Log\LoggerInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; @@ -28,6 +29,7 @@ use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; * Authentication listener for the "guard" system. * * @author Ryan Weaver + * @author Amaury Leroux de Lens */ class GuardAuthenticationListener implements ListenerInterface { @@ -39,11 +41,11 @@ class GuardAuthenticationListener implements ListenerInterface private $rememberMeServices; /** - * @param GuardAuthenticatorHandler $guardHandler The Guard handler - * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance - * @param string $providerKey The provider (i.e. firewall) key - * @param iterable|GuardAuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationProvider - * @param LoggerInterface $logger A LoggerInterface instance + * @param GuardAuthenticatorHandler $guardHandler The Guard handler + * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance + * @param string $providerKey The provider (i.e. firewall) key + * @param iterable|AuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationProvider + * @param LoggerInterface $logger A LoggerInterface instance */ public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, $providerKey, $guardAuthenticators, LoggerInterface $logger = null) { @@ -100,12 +102,29 @@ class GuardAuthenticationListener implements ListenerInterface $this->logger->debug('Calling getCredentials() on guard configurator.', array('firewall_key' => $this->providerKey, 'authenticator' => get_class($guardAuthenticator))); } + // abort the execution of the authenticator if it doesn't support the request + if ($guardAuthenticator instanceof AuthenticatorInterface) { + if (!$guardAuthenticator->supports($request)) { + return; + } + // as there was a support for given request, + // authenticator is expected to give not-null credentials. + $credentialsCanBeNull = false; + } else { + // deprecated since version 3.4, to be removed in 4.0 + $credentialsCanBeNull = true; + } + // allow the authenticator to fetch authentication info from the request $credentials = $guardAuthenticator->getCredentials($request); - // allow null to be returned to skip authentication if (null === $credentials) { - return; + // deprecated since version 3.4, to be removed in 4.0 + if ($credentialsCanBeNull) { + return; + } + + throw new \UnexpectedValueException(sprintf('The return value of "%s::getCredentials()" must not be null. Return false from "%s::supports()" instead.', get_class($guardAuthenticator), get_class($guardAuthenticator))); } // create a token with the unique key, so that the provider knows which authenticator to use @@ -172,10 +191,10 @@ class GuardAuthenticationListener implements ListenerInterface * Checks to see if remember me is supported in the authenticator and * on the firewall. If it is, the RememberMeServicesInterface is notified. * - * @param GuardAuthenticatorInterface $guardAuthenticator - * @param Request $request - * @param TokenInterface $token - * @param Response $response + * @param AuthenticatorInterface $guardAuthenticator + * @param Request $request + * @param TokenInterface $token + * @param Response $response */ private function triggerRememberMe(GuardAuthenticatorInterface $guardAuthenticator, Request $request, TokenInterface $token, Response $response = null) { diff --git a/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php b/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php index 5e1351dcc2..abe263d888 100644 --- a/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php +++ b/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php @@ -29,6 +29,8 @@ use Symfony\Component\Security\Http\SecurityEvents; * can be called directly (e.g. for manual authentication) or overridden. * * @author Ryan Weaver + * + * @final since version 3.4 */ class GuardAuthenticatorHandler { @@ -61,10 +63,10 @@ class GuardAuthenticatorHandler /** * Returns the "on success" response for the given GuardAuthenticator. * - * @param TokenInterface $token - * @param Request $request - * @param GuardAuthenticatorInterface $guardAuthenticator - * @param string $providerKey The provider (i.e. firewall) key + * @param TokenInterface $token + * @param Request $request + * @param AuthenticatorInterface $guardAuthenticator + * @param string $providerKey The provider (i.e. firewall) key * * @return null|Response */ @@ -88,10 +90,10 @@ class GuardAuthenticatorHandler * Convenience method for authenticating the user and returning the * Response *if any* for success. * - * @param UserInterface $user - * @param Request $request - * @param GuardAuthenticatorInterface $authenticator - * @param string $providerKey The provider (i.e. firewall) key + * @param UserInterface $user + * @param Request $request + * @param AuthenticatorInterface $authenticator + * @param string $providerKey The provider (i.e. firewall) key * * @return Response|null */ @@ -110,10 +112,10 @@ class GuardAuthenticatorHandler * Handles an authentication failure and returns the Response for the * GuardAuthenticator. * - * @param AuthenticationException $authenticationException - * @param Request $request - * @param GuardAuthenticatorInterface $guardAuthenticator - * @param string $providerKey The key of the firewall + * @param AuthenticationException $authenticationException + * @param Request $request + * @param AuthenticatorInterface $guardAuthenticator + * @param string $providerKey The key of the firewall * * @return null|Response */ diff --git a/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php b/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php index 307d70f9e9..0d11573c03 100644 --- a/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php +++ b/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php @@ -28,6 +28,8 @@ use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface * one location. * * @author Ryan Weaver + * + * @deprecated since version 3.4, to be removed in 4.0. Use AuthenticatorInterface instead */ interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface { diff --git a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php index 90b9580a3a..2f2678035f 100644 --- a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php @@ -14,7 +14,7 @@ namespace Symfony\Component\Security\Guard\Provider; use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; -use Symfony\Component\Security\Guard\GuardAuthenticatorInterface; +use Symfony\Component\Security\Guard\AuthenticatorInterface; use Symfony\Component\Security\Guard\Token\GuardTokenInterface; use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; use Symfony\Component\Security\Core\User\UserCheckerInterface; @@ -32,7 +32,7 @@ use Symfony\Component\Security\Core\Exception\AuthenticationExpiredException; class GuardAuthenticationProvider implements AuthenticationProviderInterface { /** - * @var GuardAuthenticatorInterface[] + * @var AuthenticatorInterface[] */ private $guardAuthenticators; private $userProvider; @@ -40,10 +40,10 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface private $userChecker; /** - * @param iterable|GuardAuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationListener - * @param UserProviderInterface $userProvider The user provider - * @param string $providerKey The provider (i.e. firewall) key - * @param UserCheckerInterface $userChecker + * @param iterable|AuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationListener + * @param UserProviderInterface $userProvider The user provider + * @param string $providerKey The provider (i.e. firewall) key + * @param UserCheckerInterface $userChecker */ public function __construct($guardAuthenticators, UserProviderInterface $userProvider, $providerKey, UserCheckerInterface $userChecker) { @@ -101,7 +101,7 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface // instances that will be checked if you have multiple firewalls. } - private function authenticateViaGuard(GuardAuthenticatorInterface $guardAuthenticator, PreAuthenticationGuardToken $token) + private function authenticateViaGuard($guardAuthenticator, PreAuthenticationGuardToken $token) { // get the user from the GuardAuthenticator $user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider); diff --git a/src/Symfony/Component/Security/Guard/Tests/Firewall/GuardAuthenticationListenerTest.php b/src/Symfony/Component/Security/Guard/Tests/Firewall/GuardAuthenticationListenerTest.php index 943ca49247..5af9f130f8 100644 --- a/src/Symfony/Component/Security/Guard/Tests/Firewall/GuardAuthenticationListenerTest.php +++ b/src/Symfony/Component/Security/Guard/Tests/Firewall/GuardAuthenticationListenerTest.php @@ -14,12 +14,16 @@ namespace Symfony\Component\Security\Guard\Tests\Firewall; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Guard\AuthenticatorInterface; use Symfony\Component\Security\Guard\Firewall\GuardAuthenticationListener; +use Symfony\Component\Security\Guard\GuardAuthenticatorInterface; use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; use Symfony\Component\Security\Core\Exception\AuthenticationException; /** * @author Ryan Weaver + * @author Amaury Leroux de Lens */ class GuardAuthenticationListenerTest extends TestCase { @@ -32,11 +36,16 @@ class GuardAuthenticationListenerTest extends TestCase public function testHandleSuccess() { - $authenticator = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); - $authenticateToken = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); + $authenticator = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); + $authenticateToken = $this->getMockBuilder(TokenInterface::class)->getMock(); $providerKey = 'my_firewall'; $credentials = array('username' => 'weaverryan', 'password' => 'all_your_base'); + + $authenticator + ->expects($this->once()) + ->method('supports') + ->willReturn(true); $authenticator ->expects($this->once()) ->method('getCredentials') @@ -82,10 +91,14 @@ class GuardAuthenticationListenerTest extends TestCase public function testHandleSuccessStopsAfterResponseIsSet() { - $authenticator1 = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); - $authenticator2 = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); + $authenticator1 = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); + $authenticator2 = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); // mock the first authenticator to fail, and set a Response + $authenticator1 + ->expects($this->once()) + ->method('supports') + ->willReturn(true); $authenticator1 ->expects($this->once()) ->method('getCredentials') @@ -112,10 +125,15 @@ class GuardAuthenticationListenerTest extends TestCase public function testHandleSuccessWithRememberMe() { - $authenticator = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); - $authenticateToken = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); + $authenticator = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); + $authenticateToken = $this->getMockBuilder(TokenInterface::class)->getMock(); $providerKey = 'my_firewall_with_rememberme'; + $authenticator + ->expects($this->once()) + ->method('supports') + ->with($this->equalTo($this->request)) + ->willReturn(true); $authenticator ->expects($this->once()) ->method('getCredentials') @@ -155,10 +173,14 @@ class GuardAuthenticationListenerTest extends TestCase public function testHandleCatchesAuthenticationException() { - $authenticator = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); + $authenticator = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); $providerKey = 'my_firewall2'; $authException = new AuthenticationException('Get outta here crazy user with a bad password!'); + $authenticator + ->expects($this->once()) + ->method('supports') + ->willReturn(true); $authenticator ->expects($this->once()) ->method('getCredentials') @@ -185,6 +207,96 @@ class GuardAuthenticationListenerTest extends TestCase $listener->handle($this->event); } + /** + * @group legacy + */ + public function testLegacyInterfaceNullCredentials() + { + $authenticatorA = $this->getMockBuilder(GuardAuthenticatorInterface::class)->getMock(); + $providerKey = 'my_firewall3'; + + $authenticatorA + ->expects($this->once()) + ->method('getCredentials') + ->will($this->returnValue(null)); + + // this is not called + $this->authenticationManager + ->expects($this->never()) + ->method('authenticate'); + + $this->guardAuthenticatorHandler + ->expects($this->never()) + ->method('handleAuthenticationSuccess'); + + $listener = new GuardAuthenticationListener( + $this->guardAuthenticatorHandler, + $this->authenticationManager, + $providerKey, + array($authenticatorA), + $this->logger + ); + + $listener->handle($this->event); + } + + /** + * @group legacy + */ + public function testLegacyInterfaceKeepsWorking() + { + $authenticator = $this->getMockBuilder(GuardAuthenticatorInterface::class)->getMock(); + $authenticateToken = $this->getMockBuilder(TokenInterface::class)->getMock(); + $providerKey = 'my_firewall'; + + $credentials = array('username' => 'weaverryan', 'password' => 'all_your_base'); + + $authenticator + ->expects($this->once()) + ->method('getCredentials') + ->with($this->equalTo($this->request)) + ->will($this->returnValue($credentials)); + + // a clone of the token that should be created internally + $uniqueGuardKey = 'my_firewall_0'; + $nonAuthedToken = new PreAuthenticationGuardToken($credentials, $uniqueGuardKey); + + $this->authenticationManager + ->expects($this->once()) + ->method('authenticate') + ->with($this->equalTo($nonAuthedToken)) + ->will($this->returnValue($authenticateToken)); + + $this->guardAuthenticatorHandler + ->expects($this->once()) + ->method('authenticateWithToken') + ->with($authenticateToken, $this->request); + + $this->guardAuthenticatorHandler + ->expects($this->once()) + ->method('handleAuthenticationSuccess') + ->with($authenticateToken, $this->request, $authenticator, $providerKey); + + $listener = new GuardAuthenticationListener( + $this->guardAuthenticatorHandler, + $this->authenticationManager, + $providerKey, + array($authenticator), + $this->logger + ); + + $listener->setRememberMeServices($this->rememberMeServices); + // should never be called - our handleAuthenticationSuccess() does not return a Response + $this->rememberMeServices + ->expects($this->never()) + ->method('loginSuccess'); + + $listener->handle($this->event); + } + + /** + * @group legacy + */ public function testReturnNullToSkipAuth() { $authenticatorA = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); @@ -220,6 +332,62 @@ class GuardAuthenticationListenerTest extends TestCase $listener->handle($this->event); } + public function testSupportsReturnFalseSkipAuth() + { + $authenticator = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); + $providerKey = 'my_firewall4'; + + $authenticator + ->expects($this->once()) + ->method('supports') + ->will($this->returnValue(false)); + + // this is not called + $authenticator + ->expects($this->never()) + ->method('getCredentials'); + + $listener = new GuardAuthenticationListener( + $this->guardAuthenticatorHandler, + $this->authenticationManager, + $providerKey, + array($authenticator), + $this->logger + ); + + $listener->handle($this->event); + } + + /** + * @expectedException \UnexpectedValueException + */ + public function testReturnNullFromGetCredentials() + { + $authenticator = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); + $providerKey = 'my_firewall4'; + + $authenticator + ->expects($this->once()) + ->method('supports') + ->will($this->returnValue(true)); + + // this will raise exception + $authenticator + ->expects($this->once()) + ->method('getCredentials') + ->will($this->returnValue(null)); + + $listener = new GuardAuthenticationListener( + $this->guardAuthenticatorHandler, + $this->authenticationManager, + $providerKey, + array($authenticator), + $this->logger + ); + + $listener->handle($this->event); + } + protected function setUp() { $this->authenticationManager = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager') diff --git a/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php b/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php index b46bc4a78d..c67f38e9ef 100644 --- a/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php +++ b/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php @@ -14,6 +14,7 @@ namespace Symfony\Component\Security\Guard\Tests; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Guard\AuthenticatorInterface; use Symfony\Component\Security\Guard\GuardAuthenticatorHandler; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; @@ -128,7 +129,7 @@ class GuardAuthenticatorHandlerTest extends TestCase $this->dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock(); $this->token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); $this->request = new Request(array(), array(), array(), array(), array(), array()); - $this->guardAuthenticator = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); + $this->guardAuthenticator = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); } protected function tearDown() diff --git a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php index ed3920533f..9d8301fce8 100644 --- a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php @@ -12,6 +12,9 @@ namespace Symfony\Component\Security\Guard\Tests\Provider; use PHPUnit\Framework\TestCase; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Guard\AuthenticatorInterface; use Symfony\Component\Security\Guard\Provider\GuardAuthenticationProvider; use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken; @@ -28,6 +31,68 @@ class GuardAuthenticationProviderTest extends TestCase { $providerKey = 'my_cool_firewall'; + $authenticatorA = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); + $authenticatorB = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); + $authenticatorC = $this->getMockBuilder(AuthenticatorInterface::class)->getMock(); + $authenticators = array($authenticatorA, $authenticatorB, $authenticatorC); + + // called 2 times - for authenticator A and B (stops on B because of match) + $this->preAuthenticationToken->expects($this->exactly(2)) + ->method('getGuardProviderKey') + // it will return the "1" index, which will match authenticatorB + ->will($this->returnValue('my_cool_firewall_1')); + + $enteredCredentials = array( + 'username' => '_weaverryan_test_user', + 'password' => 'guard_auth_ftw', + ); + $this->preAuthenticationToken->expects($this->atLeastOnce()) + ->method('getCredentials') + ->will($this->returnValue($enteredCredentials)); + + // authenticators A and C are never called + $authenticatorA->expects($this->never()) + ->method('getUser'); + $authenticatorC->expects($this->never()) + ->method('getUser'); + + $mockedUser = $this->getMockBuilder(UserInterface::class)->getMock(); + $authenticatorB->expects($this->once()) + ->method('getUser') + ->with($enteredCredentials, $this->userProvider) + ->will($this->returnValue($mockedUser)); + // checkCredentials is called + $authenticatorB->expects($this->once()) + ->method('checkCredentials') + ->with($enteredCredentials, $mockedUser) + // authentication works! + ->will($this->returnValue(true)); + $authedToken = $this->getMockBuilder(TokenInterface::class)->getMock(); + $authenticatorB->expects($this->once()) + ->method('createAuthenticatedToken') + ->with($mockedUser, $providerKey) + ->will($this->returnValue($authedToken)); + + // user checker should be called + $this->userChecker->expects($this->once()) + ->method('checkPreAuth') + ->with($mockedUser); + $this->userChecker->expects($this->once()) + ->method('checkPostAuth') + ->with($mockedUser); + + $provider = new GuardAuthenticationProvider($authenticators, $this->userProvider, $providerKey, $this->userChecker); + $actualAuthedToken = $provider->authenticate($this->preAuthenticationToken); + $this->assertSame($authedToken, $actualAuthedToken); + } + + /** + * @group legacy + */ + public function testLegacyAuthenticate() + { + $providerKey = 'my_cool_firewall'; + $authenticatorA = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); $authenticatorB = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); $authenticatorC = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock(); diff --git a/src/Symfony/Component/Security/Guard/Token/PreAuthenticationGuardToken.php b/src/Symfony/Component/Security/Guard/Token/PreAuthenticationGuardToken.php index abbe985c9e..e1b39417d7 100644 --- a/src/Symfony/Component/Security/Guard/Token/PreAuthenticationGuardToken.php +++ b/src/Symfony/Component/Security/Guard/Token/PreAuthenticationGuardToken.php @@ -29,7 +29,7 @@ class PreAuthenticationGuardToken extends AbstractToken implements GuardTokenInt /** * @param mixed $credentials - * @param string $guardProviderKey Unique key that bind this token to a specific GuardAuthenticatorInterface + * @param string $guardProviderKey Unique key that bind this token to a specific AuthenticatorInterface */ public function __construct($credentials, $guardProviderKey) {