feature #16835 [Security] Add Guard authenticator <supports> method (Amo, chalasr)
This PR was merged into the 3.4 branch. Discussion ---------- [Security] Add Guard authenticator <supports> method This method will be called before starting an authentication against a guard authenticator. The authentication will be tried only if the supports method returned `true` This improves understanding of code, increase consistency and removes responsability for `getCredentials` method to decide if the current request should be supported or not. | Q | A | | --- | --- | | Bug fix? | no | | New feature? | yes | | BC breaks? | yes | | Deprecations? | yes | | Tests pass? | yes | | Fixed tickets | none | | License | MIT | | Doc PR | | Commits -------78eecba780
Fix BC layera7a6f8a678
[Security] Add Guard authenticator <supports> method
This commit is contained in:
commit
02b77f54fb
|
@ -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
|
||||
--------------
|
||||
|
|
|
@ -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
|
||||
--------------
|
||||
|
||||
|
|
|
@ -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
|
||||
-----
|
||||
|
|
|
@ -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 <ryan@knpuniversity.com>
|
||||
*/
|
||||
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.
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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 <ryan@knpuniversity.com>
|
||||
* @author Amaury Leroux de Lens <amaury@lerouxdelens.com>
|
||||
*/
|
||||
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);
|
||||
}
|
|
@ -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 <ryan@knpuniversity.com>
|
||||
* @author Amaury Leroux de Lens <amaury@lerouxdelens.com>
|
||||
*/
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -29,6 +29,8 @@ use Symfony\Component\Security\Http\SecurityEvents;
|
|||
* can be called directly (e.g. for manual authentication) or overridden.
|
||||
*
|
||||
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
|
|
@ -28,6 +28,8 @@ use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface
|
|||
* one location.
|
||||
*
|
||||
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||
*
|
||||
* @deprecated since version 3.4, to be removed in 4.0. Use AuthenticatorInterface instead
|
||||
*/
|
||||
interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 <weaverryan@gmail.com>
|
||||
* @author Amaury Leroux de Lens <amaury@lerouxdelens.com>
|
||||
*/
|
||||
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')
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Reference in New Issue