Upgrade 1-11.38

This commit is contained in:
xesmyd
2026-03-30 14:10:30 +02:00
parent f2a7e6d1fc
commit ac648ef29d
24665 changed files with 69682 additions and 2205004 deletions
@@ -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,14 +20,23 @@ use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
*
* @author Ryan Weaver <ryan@knpuniversity.com>
*/
abstract class AbstractGuardAuthenticator implements GuardAuthenticatorInterface
abstract class AbstractGuardAuthenticator implements AuthenticatorInterface
{
/**
* {@inheritdoc}
*/
public function supports(Request $request)
{
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.4 and will be removed in 4.0. Implement the "%s::supports()" method in class "%s" instead.', __METHOD__, AuthenticatorInterface::class, static::class), \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.
*
* @param UserInterface $user
* @param string $providerKey
* @param string $providerKey
*
* @return PostAuthenticationGuardToken
*/
@@ -11,13 +11,13 @@
namespace Symfony\Component\Security\Guard\Authenticator;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
/**
@@ -39,9 +39,6 @@ abstract class AbstractFormLoginAuthenticator extends AbstractGuardAuthenticator
/**
* Override to change what happens after a bad username/password is submitted.
*
* @param Request $request
* @param AuthenticationException $exception
*
* @return RedirectResponse
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
@@ -58,18 +55,16 @@ abstract class AbstractFormLoginAuthenticator extends AbstractGuardAuthenticator
/**
* Override to change what happens after successful authentication.
*
* @param Request $request
* @param TokenInterface $token
* @param string $providerKey
* @param string $providerKey
*
* @return RedirectResponse
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
@trigger_error(sprintf('The AbstractFormLoginAuthenticator::onAuthenticationSuccess() implementation was deprecated in Symfony 3.1 and will be removed in Symfony 4.0. You should implement this method yourself in %s and remove getDefaultSuccessRedirectUrl().', get_class($this)), E_USER_DEPRECATED);
@trigger_error(sprintf('The AbstractFormLoginAuthenticator::onAuthenticationSuccess() implementation was deprecated in Symfony 3.1 and will be removed in Symfony 4.0. You should implement this method yourself in %s and remove getDefaultSuccessRedirectUrl().', static::class), \E_USER_DEPRECATED);
if (!method_exists($this, 'getDefaultSuccessRedirectUrl')) {
throw new \Exception(sprintf('You must implement onAuthenticationSuccess() or getDefaultSuccessRedirectUrl() in %s.', get_class($this)));
throw new \Exception(sprintf('You must implement onAuthenticationSuccess() or getDefaultSuccessRedirectUrl() in "%s".', static::class));
}
$targetPath = null;
@@ -96,9 +91,6 @@ abstract class AbstractFormLoginAuthenticator extends AbstractGuardAuthenticator
* Override to control what happens when the user hits a secure page
* but isn't logged in yet.
*
* @param Request $request
* @param AuthenticationException|null $authException
*
* @return RedirectResponse
*/
public function start(Request $request, AuthenticationException $authException = null)
@@ -11,16 +11,21 @@
namespace Symfony\Component\Security\Guard\Firewall;
use Psr\Log\LoggerInterface;
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\Token\PreAuthenticationGuardToken;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Guard\GuardAuthenticatorInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AccountStatusException;
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\AbstractGuardAuthenticator;
use Symfony\Component\Security\Guard\AuthenticatorInterface;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
use Symfony\Component\Security\Guard\GuardAuthenticatorInterface;
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
@@ -28,6 +33,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
{
@@ -37,15 +43,16 @@ class GuardAuthenticationListener implements ListenerInterface
private $guardAuthenticators;
private $logger;
private $rememberMeServices;
private $hideUserNotFoundExceptions;
/**
* @param GuardAuthenticatorHandler $guardHandler The Guard handler
* @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance
* @param string $providerKey The provider (i.e. firewall) key
* @param 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, array $guardAuthenticators, LoggerInterface $logger = null)
public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, $providerKey, $guardAuthenticators, LoggerInterface $logger = null, $hideUserNotFoundExceptions = true)
{
if (empty($providerKey)) {
throw new \InvalidArgumentException('$providerKey must not be empty.');
@@ -56,17 +63,22 @@ class GuardAuthenticationListener implements ListenerInterface
$this->providerKey = $providerKey;
$this->guardAuthenticators = $guardAuthenticators;
$this->logger = $logger;
$this->hideUserNotFoundExceptions = $hideUserNotFoundExceptions;
}
/**
* Iterates over each authenticator to see if each wants to authenticate the request.
*
* @param GetResponseEvent $event
*/
public function handle(GetResponseEvent $event)
{
if (null !== $this->logger) {
$this->logger->debug('Checking for guard authentication credentials.', array('firewall_key' => $this->providerKey, 'authenticators' => count($this->guardAuthenticators)));
$context = ['firewall_key' => $this->providerKey];
if ($this->guardAuthenticators instanceof \Countable || \is_array($this->guardAuthenticators)) {
$context['authenticators'] = \count($this->guardAuthenticators);
}
$this->logger->debug('Checking for guard authentication credentials.', $context);
}
foreach ($this->guardAuthenticators as $key => $guardAuthenticator) {
@@ -78,7 +90,7 @@ class GuardAuthenticationListener implements ListenerInterface
if ($event->hasResponse()) {
if (null !== $this->logger) {
$this->logger->debug('The "{authenticator}" authenticator set the response. Any later authenticator will not be called', array('authenticator' => get_class($guardAuthenticator)));
$this->logger->debug('The "{authenticator}" authenticator set the response. Any later authenticator will not be called', ['authenticator' => \get_class($guardAuthenticator)]);
}
break;
@@ -90,39 +102,76 @@ class GuardAuthenticationListener implements ListenerInterface
{
$request = $event->getRequest();
try {
// abort the execution of the authenticator if it doesn't support the request
if ($guardAuthenticator instanceof AuthenticatorInterface) {
if (null !== $this->logger) {
$this->logger->debug('Checking support on guard authenticator.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)]);
}
if (!$guardAuthenticator->supports($request)) {
if (null !== $this->logger) {
$this->logger->debug('Guard authenticator does not support the request.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)]);
}
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;
}
if (null !== $this->logger) {
$this->logger->debug('Calling getCredentials() on guard configurator.', array('firewall_key' => $this->providerKey, 'authenticator' => get_class($guardAuthenticator)));
$this->logger->debug('Calling getCredentials() on guard authenticator.', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)]);
}
// 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;
}
if ($guardAuthenticator instanceof AbstractGuardAuthenticator) {
@trigger_error(sprintf('Returning null from "%1$s::getCredentials()" is deprecated since Symfony 3.4 and will throw an \UnexpectedValueException in 4.0. Return false from "%1$s::supports()" instead.', \get_class($guardAuthenticator)), \E_USER_DEPRECATED);
return;
}
throw new \UnexpectedValueException(sprintf('The return value of "%1$s::getCredentials()" must not be null. Return false from "%1$s::supports()" instead.', \get_class($guardAuthenticator)));
}
// create a token with the unique key, so that the provider knows which authenticator to use
$token = new PreAuthenticationGuardToken($credentials, $uniqueGuardKey);
if (null !== $this->logger) {
$this->logger->debug('Passing guard token information to the GuardAuthenticationProvider', array('firewall_key' => $this->providerKey, 'authenticator' => get_class($guardAuthenticator)));
$this->logger->debug('Passing guard token information to the GuardAuthenticationProvider', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)]);
}
// pass the token into the AuthenticationManager system
// this indirectly calls GuardAuthenticationProvider::authenticate()
$token = $this->authenticationManager->authenticate($token);
if (null !== $this->logger) {
$this->logger->info('Guard authentication successful!', array('token' => $token, 'authenticator' => get_class($guardAuthenticator)));
$this->logger->info('Guard authentication successful!', ['token' => $token, 'authenticator' => \get_class($guardAuthenticator)]);
}
// sets the token on the token storage, etc
$this->guardHandler->authenticateWithToken($token, $request);
$this->guardHandler->authenticateWithToken($token, $request, $this->providerKey);
} catch (AuthenticationException $e) {
// oh no! Authentication failed!
if (null !== $this->logger) {
$this->logger->info('Guard authentication failed.', array('exception' => $e, 'authenticator' => get_class($guardAuthenticator)));
$this->logger->info('Guard authentication failed.', ['exception' => $e, 'authenticator' => \get_class($guardAuthenticator)]);
}
// Avoid leaking error details in case of invalid user (e.g. user not found or invalid account status)
// to prevent user enumeration via response content
if ($this->hideUserNotFoundExceptions && ($e instanceof UsernameNotFoundException || $e instanceof AccountStatusException)) {
$e = new BadCredentialsException('Bad credentials.', 0, $e);
}
$response = $this->guardHandler->handleAuthenticationFailure($e, $request, $guardAuthenticator, $this->providerKey);
@@ -138,13 +187,13 @@ class GuardAuthenticationListener implements ListenerInterface
$response = $this->guardHandler->handleAuthenticationSuccess($token, $request, $guardAuthenticator, $this->providerKey);
if ($response instanceof Response) {
if (null !== $this->logger) {
$this->logger->debug('Guard authenticator set success response.', array('response' => $response, 'authenticator' => get_class($guardAuthenticator)));
$this->logger->debug('Guard authenticator set success response.', ['response' => $response, 'authenticator' => \get_class($guardAuthenticator)]);
}
$event->setResponse($response);
} else {
if (null !== $this->logger) {
$this->logger->debug('Guard authenticator set no success response: request continues.', array('authenticator' => get_class($guardAuthenticator)));
$this->logger->debug('Guard authenticator set no success response: request continues.', ['authenticator' => \get_class($guardAuthenticator)]);
}
}
@@ -154,8 +203,6 @@ class GuardAuthenticationListener implements ListenerInterface
/**
* Should be called if this listener will support remember me.
*
* @param RememberMeServicesInterface $rememberMeServices
*/
public function setRememberMeServices(RememberMeServicesInterface $rememberMeServices)
{
@@ -165,17 +212,12 @@ 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
*/
private function triggerRememberMe(GuardAuthenticatorInterface $guardAuthenticator, Request $request, TokenInterface $token, Response $response = null)
{
if (null === $this->rememberMeServices) {
if (null !== $this->logger) {
$this->logger->debug('Remember me skipped: it is not configured for the firewall.', array('authenticator' => get_class($guardAuthenticator)));
$this->logger->debug('Remember me skipped: it is not configured for the firewall.', ['authenticator' => \get_class($guardAuthenticator)]);
}
return;
@@ -183,17 +225,14 @@ class GuardAuthenticationListener implements ListenerInterface
if (!$guardAuthenticator->supportsRememberMe()) {
if (null !== $this->logger) {
$this->logger->debug('Remember me skipped: your authenticator does not support it.', array('authenticator' => get_class($guardAuthenticator)));
$this->logger->debug('Remember me skipped: your authenticator does not support it.', ['authenticator' => \get_class($guardAuthenticator)]);
}
return;
}
if (!$response instanceof Response) {
throw new \LogicException(sprintf(
'%s::onAuthenticationSuccess *must* return a Response if you want to use the remember me functionality. Return a Response, or set remember_me to false under the guard configuration.',
get_class($guardAuthenticator)
));
throw new \LogicException(sprintf('"%s::onAuthenticationSuccess()" *must* return a Response if you want to use the remember me functionality. Return a Response, or set remember_me to false under the guard configuration.', \get_class($guardAuthenticator)));
}
$this->rememberMeServices->loginSuccess($request, $response, $token);
+41 -37
View File
@@ -18,9 +18,9 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInt
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
/**
* A utility class that does much of the *work* during the guard authentication process.
@@ -29,27 +29,34 @@ 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
{
private $tokenStorage;
private $dispatcher;
private $sessionStrategy;
private $statelessProviderKeys;
public function __construct(TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher = null)
/**
* @param array $statelessProviderKeys An array of provider/firewall keys that are "stateless" and so do not need the session migrated on success
*/
public function __construct(TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher = null, array $statelessProviderKeys = [])
{
$this->tokenStorage = $tokenStorage;
$this->dispatcher = $eventDispatcher;
$this->statelessProviderKeys = $statelessProviderKeys;
}
/**
* Authenticates the given token in the system.
*
* @param TokenInterface $token
* @param Request $request
*/
public function authenticateWithToken(TokenInterface $token, Request $request)
public function authenticateWithToken(TokenInterface $token, Request $request/*, string $providerKey */)
{
$providerKey = \func_num_args() > 2 ? func_get_arg(2) : null;
$this->migrateSession($request, $token, $providerKey);
$this->tokenStorage->setToken($token);
if (null !== $this->dispatcher) {
@@ -61,12 +68,9 @@ 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 string $providerKey The provider (i.e. firewall) key
*
* @return null|Response
* @return Response|null
*/
public function handleAuthenticationSuccess(TokenInterface $token, Request $request, GuardAuthenticatorInterface $guardAuthenticator, $providerKey)
{
@@ -77,21 +81,14 @@ class GuardAuthenticatorHandler
return $response;
}
throw new \UnexpectedValueException(sprintf(
'The %s::onAuthenticationSuccess method must return null or a Response object. You returned %s.',
get_class($guardAuthenticator),
is_object($response) ? get_class($response) : gettype($response)
));
throw new \UnexpectedValueException(sprintf('The "%s::onAuthenticationSuccess()" method must return null or a Response object. You returned "%s".', \get_class($guardAuthenticator), \is_object($response) ? \get_class($response) : \gettype($response)));
}
/**
* 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 string $providerKey The provider (i.e. firewall) key
*
* @return Response|null
*/
@@ -100,7 +97,7 @@ class GuardAuthenticatorHandler
// create an authenticated token for the User
$token = $authenticator->createAuthenticatedToken($user, $providerKey);
// authenticate this in the system
$this->authenticateWithToken($token, $request);
$this->authenticateWithToken($token, $request, $providerKey);
// return the success metric
return $this->handleAuthenticationSuccess($token, $request, $authenticator, $providerKey);
@@ -110,30 +107,37 @@ 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 string $providerKey The provider (i.e. firewall) key
*
* @return null|Response
* @return Response|null
*/
public function handleAuthenticationFailure(AuthenticationException $authenticationException, Request $request, GuardAuthenticatorInterface $guardAuthenticator, $providerKey)
{
$token = $this->tokenStorage->getToken();
if ($token instanceof PostAuthenticationGuardToken && $providerKey === $token->getProviderKey()) {
$this->tokenStorage->setToken(null);
}
$response = $guardAuthenticator->onAuthenticationFailure($request, $authenticationException);
if ($response instanceof Response || null === $response) {
// returning null is ok, it means they want the request to continue
return $response;
}
throw new \UnexpectedValueException(sprintf(
'The %s::onAuthenticationFailure method must return null or a Response object. You returned %s.',
get_class($guardAuthenticator),
is_object($response) ? get_class($response) : gettype($response)
));
throw new \UnexpectedValueException(sprintf('The "%s::onAuthenticationFailure()" method must return null or a Response object. You returned "%s".', \get_class($guardAuthenticator), \is_object($response) ? \get_class($response) : \gettype($response)));
}
/**
* Call this method if your authentication token is stored to a session.
*
* @final
*/
public function setSessionAuthenticationStrategy(SessionAuthenticationStrategyInterface $sessionStrategy)
{
$this->sessionStrategy = $sessionStrategy;
}
private function migrateSession(Request $request, TokenInterface $token, $providerKey)
{
if (\in_array($providerKey, $this->statelessProviderKeys, true) || !$this->sessionStrategy || !$request->hasSession() || !$request->hasPreviousSession()) {
return;
}
$this->sessionStrategy->onAuthentication($request, $token);
}
}
+15 -26
View File
@@ -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
{
@@ -40,20 +42,18 @@ interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface
*
* For example, for a form login, you might:
*
* if ($request->request->has('_username')) {
* return array(
* 'username' => $request->request->get('_username'),
* 'password' => $request->request->get('_password'),
* );
* } else {
* return;
* }
* if ($request->request->has('_username')) {
* return [
* 'username' => $request->request->get('_username'),
* 'password' => $request->request->get('_password'),
* ];
* } else {
* return;
* }
*
* 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 ['api_key' => $request->headers->get('X-API-TOKEN')];
*
* @return mixed|null
*/
@@ -67,9 +67,6 @@ interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface
* You may throw an AuthenticationException if you wish. If you return
* null, then a UsernameNotFoundException is thrown for you.
*
* @param mixed $credentials
* @param UserProviderInterface $userProvider
*
* @throws AuthenticationException
*
* @return UserInterface|null
@@ -85,9 +82,6 @@ interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface
*
* The *credentials* are the return value from getCredentials()
*
* @param mixed $credentials
* @param UserInterface $user
*
* @return bool
*
* @throws AuthenticationException
@@ -103,8 +97,7 @@ interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface
*
* @see AbstractGuardAuthenticator
*
* @param UserInterface $user
* @param string $providerKey The provider (i.e. firewall) key
* @param string $providerKey The provider (i.e. firewall) key
*
* @return GuardTokenInterface
*/
@@ -114,14 +107,11 @@ interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface
* Called when authentication executed, but failed (e.g. wrong username password).
*
* This should return the Response sent back to the user, like a
* RedirectResponse to the login page or a 403 response.
* RedirectResponse to the login page or a 401 response.
*
* If you return null, the request will continue, but the user will
* not be authenticated. This is probably not what you want to do.
*
* @param Request $request
* @param AuthenticationException $exception
*
* @return Response|null
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception);
@@ -135,9 +125,7 @@ interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface
* If you return null, the current request will continue, and the user
* will be authenticated. This makes sense, for example, with an API.
*
* @param Request $request
* @param TokenInterface $token
* @param string $providerKey The provider (i.e. firewall) key
* @param string $providerKey The provider (i.e. firewall) key
*
* @return Response|null
*/
@@ -153,6 +141,7 @@ interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface
* done by having a _remember_me checkbox in your form, but
* can be configured by the "always_remember_me" and "remember_me_parameter"
* parameters under the "remember_me" firewall key
* D) The onAuthenticationSuccess method returns a Response object
*
* @return bool
*/
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2004-2017 Fabien Potencier
Copyright (c) 2004-2020 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -12,16 +12,17 @@
namespace Symfony\Component\Security\Guard\Provider;
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\AuthenticationExpiredException;
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\Token\GuardTokenInterface;
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationExpiredException;
use Symfony\Component\Security\Guard\AuthenticatorInterface;
use Symfony\Component\Security\Guard\Token\GuardTokenInterface;
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
/**
* Responsible for accepting the PreAuthenticationGuardToken and calling
@@ -32,7 +33,7 @@ use Symfony\Component\Security\Core\Exception\AuthenticationExpiredException;
class GuardAuthenticationProvider implements AuthenticationProviderInterface
{
/**
* @var GuardAuthenticatorInterface[]
* @var AuthenticatorInterface[]
*/
private $guardAuthenticators;
private $userProvider;
@@ -40,12 +41,11 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface
private $userChecker;
/**
* @param 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
*/
public function __construct(array $guardAuthenticators, UserProviderInterface $userProvider, $providerKey, UserCheckerInterface $userChecker)
public function __construct($guardAuthenticators, UserProviderInterface $userProvider, $providerKey, UserCheckerInterface $userChecker)
{
$this->guardAuthenticators = $guardAuthenticators;
$this->userProvider = $userProvider;
@@ -62,7 +62,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.');
}
@@ -82,66 +82,72 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface
return $token;
}
// this AccountStatusException causes the user to be logged out
// this causes the user to be logged out
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)
private function authenticateViaGuard($guardAuthenticator, PreAuthenticationGuardToken $token)
{
// get the user from the GuardAuthenticator
$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);
if (true !== $guardAuthenticator->checkCredentials($token->getCredentials(), $user)) {
throw new BadCredentialsException(sprintf('Authentication failed because %s::checkCredentials() did not return true.', get_class($guardAuthenticator)));
throw new BadCredentialsException(sprintf('Authentication failed because "%s"::checkCredentials() did not return true.', \get_class($guardAuthenticator)));
}
$this->userChecker->checkPostAuth($user);
// 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;
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ total control.
Resources
---------
* [Documentation](https://symfony.com/doc/current/components/security/index.html)
* [Documentation](https://symfony.com/doc/current/components/security.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
@@ -77,7 +77,7 @@ class FormLoginAuthenticatorTest extends TestCase
$this->requestWithSession->getSession()
->expects($this->once())
->method('get')
->will($this->returnValue(null));
->willReturn(null);
$redirectResponse = $this->authenticator->onAuthenticationSuccess($this->requestWithSession, $token, 'providerkey');
@@ -96,7 +96,7 @@ class FormLoginAuthenticatorTest extends TestCase
$this->requestWithSession->getSession()
->expects($this->once())
->method('get')
->will($this->returnValue(self::CUSTOM_SUCCESS_URL));
->willReturn(self::CUSTOM_SUCCESS_URL);
$redirectResponse = $this->authenticator->onAuthenticationSuccess($this->requestWithSession, $token, 'providerkey');
@@ -129,8 +129,8 @@ class FormLoginAuthenticatorTest extends TestCase
protected function setUp()
{
$this->requestWithoutSession = new Request(array(), array(), array(), array(), array(), array());
$this->requestWithSession = new Request(array(), array(), array(), array(), array(), array());
$this->requestWithoutSession = new Request([], [], [], [], [], []);
$this->requestWithSession = new Request([], [], [], [], [], []);
$session = $this->getMockBuilder('Symfony\\Component\\HttpFoundation\\Session\\SessionInterface')
->disableOriginalConstructor()
@@ -143,12 +143,6 @@ class FormLoginAuthenticatorTest extends TestCase
->setDefaultSuccessRedirectUrl(self::DEFAULT_SUCCESS_URL)
;
}
protected function tearDown()
{
$this->request = null;
$this->requestWithSession = null;
}
}
class TestFormLoginAuthenticator extends AbstractFormLoginAuthenticator
@@ -14,12 +14,20 @@ 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\Guard\Firewall\GuardAuthenticationListener;
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Exception\LockedException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
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;
/**
* @author Ryan Weaver <weaverryan@gmail.com>
* @author Amaury Leroux de Lens <amaury@lerouxdelens.com>
*/
class GuardAuthenticationListenerTest extends TestCase
{
@@ -32,16 +40,21 @@ 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');
$credentials = ['username' => 'weaverryan', 'password' => 'all_your_base'];
$authenticator
->expects($this->once())
->method('supports')
->willReturn(true);
$authenticator
->expects($this->once())
->method('getCredentials')
->with($this->equalTo($this->request))
->will($this->returnValue($credentials));
->willReturn($credentials);
// a clone of the token that should be created internally
$uniqueGuardKey = 'my_firewall_0';
@@ -51,7 +64,7 @@ class GuardAuthenticationListenerTest extends TestCase
->expects($this->once())
->method('authenticate')
->with($this->equalTo($nonAuthedToken))
->will($this->returnValue($authenticateToken));
->willReturn($authenticateToken);
$this->guardAuthenticatorHandler
->expects($this->once())
@@ -67,7 +80,7 @@ class GuardAuthenticationListenerTest extends TestCase
$this->guardAuthenticatorHandler,
$this->authenticationManager,
$providerKey,
array($authenticator),
[$authenticator],
$this->logger
);
@@ -82,10 +95,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')
@@ -103,7 +120,7 @@ class GuardAuthenticationListenerTest extends TestCase
$this->guardAuthenticatorHandler,
$this->authenticationManager,
'my_firewall',
array($authenticator1, $authenticator2),
[$authenticator1, $authenticator2],
$this->logger
);
@@ -112,39 +129,44 @@ 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')
->with($this->equalTo($this->request))
->will($this->returnValue(array('username' => 'anything_not_empty')));
->willReturn(['username' => 'anything_not_empty']);
$this->authenticationManager
->expects($this->once())
->method('authenticate')
->will($this->returnValue($authenticateToken));
->willReturn($authenticateToken);
$successResponse = new Response('Success!');
$this->guardAuthenticatorHandler
->expects($this->once())
->method('handleAuthenticationSuccess')
->will($this->returnValue($successResponse));
->willReturn($successResponse);
$listener = new GuardAuthenticationListener(
$this->guardAuthenticatorHandler,
$this->authenticationManager,
$providerKey,
array($authenticator),
[$authenticator],
$this->logger
);
$listener->setRememberMeServices($this->rememberMeServices);
$authenticator->expects($this->once())
->method('supportsRememberMe')
->will($this->returnValue(true));
->willReturn(true);
// should be called - we do have a success Response
$this->rememberMeServices
->expects($this->once())
@@ -155,14 +177,18 @@ 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')
->will($this->throwException($authException));
->willThrowException($authException);
// this is not called
$this->authenticationManager
@@ -178,27 +204,73 @@ class GuardAuthenticationListenerTest extends TestCase
$this->guardAuthenticatorHandler,
$this->authenticationManager,
$providerKey,
array($authenticator),
[$authenticator],
$this->logger
);
$listener->handle($this->event);
}
public function testReturnNullToSkipAuth()
/**
* @dataProvider exceptionsToHide
*/
public function testHandleHidesInvalidUserExceptions(AuthenticationException $exceptionToHide)
{
$authenticatorA = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock();
$authenticatorB = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock();
$authenticator = $this->createMock(AuthenticatorInterface::class);
$providerKey = 'my_firewall2';
$authenticator
->expects($this->once())
->method('supports')
->willReturn(true);
$authenticator
->expects($this->once())
->method('getCredentials')
->willReturn(['username' => 'robin', 'password' => 'hood']);
$this->authenticationManager
->expects($this->once())
->method('authenticate')
->willThrowException($exceptionToHide);
$this->guardAuthenticatorHandler
->expects($this->once())
->method('handleAuthenticationFailure')
->with($this->callback(function ($e) use ($exceptionToHide) {
return $e instanceof BadCredentialsException && $exceptionToHide === $e->getPrevious();
}), $this->request, $authenticator, $providerKey);
$listener = new GuardAuthenticationListener(
$this->guardAuthenticatorHandler,
$this->authenticationManager,
$providerKey,
[$authenticator],
$this->logger
);
$listener->handle($this->event);
}
public function exceptionsToHide()
{
return [
[new UsernameNotFoundException()],
[new LockedException()],
];
}
/**
* @group legacy
*/
public function testLegacyInterfaceNullCredentials()
{
$authenticatorA = $this->getMockBuilder(GuardAuthenticatorInterface::class)->getMock();
$providerKey = 'my_firewall3';
$authenticatorA
->expects($this->once())
->method('getCredentials')
->will($this->returnValue(null));
$authenticatorB
->expects($this->once())
->method('getCredentials')
->will($this->returnValue(null));
->willReturn(null);
// this is not called
$this->authenticationManager
@@ -213,7 +285,184 @@ class GuardAuthenticationListenerTest extends TestCase
$this->guardAuthenticatorHandler,
$this->authenticationManager,
$providerKey,
array($authenticatorA, $authenticatorB),
[$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 = ['username' => 'weaverryan', 'password' => 'all_your_base'];
$authenticator
->expects($this->once())
->method('getCredentials')
->with($this->equalTo($this->request))
->willReturn($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))
->willReturn($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,
[$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();
$authenticatorB = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock();
$providerKey = 'my_firewall3';
$authenticatorA
->expects($this->once())
->method('getCredentials')
->willReturn(null);
$authenticatorB
->expects($this->once())
->method('getCredentials')
->willReturn(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,
[$authenticatorA, $authenticatorB],
$this->logger
);
$listener->handle($this->event);
}
public function testSupportsReturnFalseSkipAuth()
{
$authenticator = $this->getMockBuilder(AuthenticatorInterface::class)->getMock();
$providerKey = 'my_firewall4';
$authenticator
->expects($this->once())
->method('supports')
->willReturn(false);
// this is not called
$authenticator
->expects($this->never())
->method('getCredentials');
$listener = new GuardAuthenticationListener(
$this->guardAuthenticatorHandler,
$this->authenticationManager,
$providerKey,
[$authenticator],
$this->logger
);
$listener->handle($this->event);
}
public function testReturnNullFromGetCredentials()
{
$this->expectException('UnexpectedValueException');
$authenticator = $this->getMockBuilder(AuthenticatorInterface::class)->getMock();
$providerKey = 'my_firewall4';
$authenticator
->expects($this->once())
->method('supports')
->willReturn(true);
// this will raise exception
$authenticator
->expects($this->once())
->method('getCredentials')
->willReturn(null);
$listener = new GuardAuthenticationListener(
$this->guardAuthenticatorHandler,
$this->authenticationManager,
$providerKey,
[$authenticator],
$this->logger
);
$listener->handle($this->event);
}
/**
* @group legacy
* @expectedDeprecation Returning null from "%s::getCredentials()" is deprecated since Symfony 3.4 and will throw an \UnexpectedValueException in 4.0. Return false from "%s::supports()" instead.
*/
public function testReturnNullFromGetCredentialsTriggersForAbstractGuardAuthenticatorInstances()
{
$authenticator = $this->getMockBuilder(AbstractGuardAuthenticator::class)->getMock();
$providerKey = 'my_firewall4';
$authenticator
->expects($this->once())
->method('supports')
->willReturn(true);
// this will raise exception
$authenticator
->expects($this->once())
->method('getCredentials')
->willReturn(null);
$listener = new GuardAuthenticationListener(
$this->guardAuthenticatorHandler,
$this->authenticationManager,
$providerKey,
[$authenticator],
$this->logger
);
@@ -230,16 +479,16 @@ class GuardAuthenticationListenerTest extends TestCase
->disableOriginalConstructor()
->getMock();
$this->request = new Request(array(), array(), array(), array(), array(), array());
$this->request = new Request([], [], [], [], [], []);
$this->event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent')
->disableOriginalConstructor()
->setMethods(array('getRequest'))
->setMethods(['getRequest'])
->getMock();
$this->event
->expects($this->any())
->method('getRequest')
->will($this->returnValue($this->request));
->willReturn($this->request);
$this->logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$this->rememberMeServices = $this->getMockBuilder('Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface')->getMock();
@@ -14,8 +14,9 @@ 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\GuardAuthenticatorHandler;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Guard\AuthenticatorInterface;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
@@ -25,6 +26,7 @@ class GuardAuthenticatorHandlerTest extends TestCase
private $dispatcher;
private $token;
private $request;
private $sessionStrategy;
private $guardAuthenticator;
public function testAuthenticateWithToken()
@@ -52,7 +54,7 @@ class GuardAuthenticatorHandlerTest extends TestCase
$this->guardAuthenticator->expects($this->once())
->method('onAuthenticationSuccess')
->with($this->request, $this->token, $providerKey)
->will($this->returnValue($response));
->willReturn($response);
$handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher);
$actualResponse = $handler->handleAuthenticationSuccess($this->token, $this->request, $this->guardAuthenticator, $providerKey);
@@ -71,7 +73,7 @@ class GuardAuthenticatorHandlerTest extends TestCase
$this->guardAuthenticator->expects($this->once())
->method('onAuthenticationFailure')
->with($this->request, $authException)
->will($this->returnValue($response));
->willReturn($response);
$handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher);
$actualResponse = $handler->handleAuthenticationFailure($authException, $this->request, $this->guardAuthenticator, 'firewall_provider_key');
@@ -81,21 +83,9 @@ class GuardAuthenticatorHandlerTest extends TestCase
/**
* @dataProvider getTokenClearingTests
*/
public function testHandleAuthenticationClearsToken($tokenClass, $tokenProviderKey, $actualProviderKey, $shouldTokenBeCleared)
public function testHandleAuthenticationClearsToken($tokenProviderKey, $actualProviderKey)
{
$token = $this->getMockBuilder($tokenClass)
->disableOriginalConstructor()
->getMock();
$token->expects($this->any())
->method('getProviderKey')
->will($this->returnValue($tokenProviderKey));
// make the $token be the current token
$this->tokenStorage->expects($this->once())
->method('getToken')
->will($this->returnValue($token));
$this->tokenStorage->expects($shouldTokenBeCleared ? $this->once() : $this->never())
$this->tokenStorage->expects($this->never())
->method('setToken')
->with(null);
$authException = new AuthenticationException('Bad password!');
@@ -104,7 +94,7 @@ class GuardAuthenticatorHandlerTest extends TestCase
$this->guardAuthenticator->expects($this->once())
->method('onAuthenticationFailure')
->with($this->request, $authException)
->will($this->returnValue($response));
->willReturn($response);
$handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher);
$actualResponse = $handler->handleAuthenticationFailure($authException, $this->request, $this->guardAuthenticator, $actualProviderKey);
@@ -113,22 +103,79 @@ class GuardAuthenticatorHandlerTest extends TestCase
public function getTokenClearingTests()
{
$tests = array();
// correct token class and matching firewall => clear the token
$tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'the_firewall_key', true);
$tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'different_key', false);
$tests[] = array('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', 'the_firewall_key', 'the_firewall_key', false);
$tests = [];
// matching firewall => clear the token
$tests[] = ['the_firewall_key', 'the_firewall_key'];
$tests[] = ['the_firewall_key', 'different_key'];
$tests[] = ['the_firewall_key', 'the_firewall_key'];
return $tests;
}
public function testNoFailureIfSessionStrategyNotPassed()
{
$this->configurePreviousSession();
$this->tokenStorage->expects($this->once())
->method('setToken')
->with($this->token);
$handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher);
$handler->authenticateWithToken($this->token, $this->request);
}
public function testSessionStrategyIsCalled()
{
$this->configurePreviousSession();
$this->sessionStrategy->expects($this->once())
->method('onAuthentication')
->with($this->request, $this->token);
$handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher);
$handler->setSessionAuthenticationStrategy($this->sessionStrategy);
$handler->authenticateWithToken($this->token, $this->request);
}
public function testSessionStrategyIsNotCalledWhenStateless()
{
$this->configurePreviousSession();
$this->sessionStrategy->expects($this->never())
->method('onAuthentication');
$handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher, ['some_provider_key']);
$handler->setSessionAuthenticationStrategy($this->sessionStrategy);
$handler->authenticateWithToken($this->token, $this->request, 'some_provider_key');
}
/**
* @requires function \Symfony\Component\HttpFoundation\Request::setSessionFactory
*/
public function testSessionIsNotInstantiatedOnStatelessFirewall()
{
$sessionFactory = $this->getMockBuilder(\stdClass::class)
->setMethods(['__invoke'])
->getMock();
$sessionFactory->expects($this->never())
->method('__invoke');
$this->request->setSessionFactory($sessionFactory);
$handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher, ['stateless_provider_key']);
$handler->setSessionAuthenticationStrategy($this->sessionStrategy);
$handler->authenticateWithToken($this->token, $this->request, 'stateless_provider_key');
}
protected function setUp()
{
$this->tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock();
$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->request = new Request([], [], [], [], [], []);
$this->sessionStrategy = $this->getMockBuilder('Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface')->getMock();
$this->guardAuthenticator = $this->getMockBuilder(AuthenticatorInterface::class)->getMock();
}
protected function tearDown()
@@ -139,4 +186,14 @@ class GuardAuthenticatorHandlerTest extends TestCase
$this->request = null;
$this->guardAuthenticator = null;
}
private function configurePreviousSession()
{
$session = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\SessionInterface')->getMock();
$session->expects($this->any())
->method('getName')
->willReturn('test_session_name');
$this->request->setSession($session);
$this->request->cookies->set('test_session_name', 'session_cookie_val');
}
}
@@ -12,8 +12,12 @@
namespace Symfony\Component\Security\Guard\Tests\Provider;
use PHPUnit\Framework\TestCase;
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\GuardTokenInterface;
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
/**
* @author Ryan Weaver <weaverryan@gmail.com>
@@ -28,24 +32,24 @@ class GuardAuthenticationProviderTest extends TestCase
{
$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();
$authenticators = array($authenticatorA, $authenticatorB, $authenticatorC);
$authenticatorA = $this->getMockBuilder(AuthenticatorInterface::class)->getMock();
$authenticatorB = $this->getMockBuilder(AuthenticatorInterface::class)->getMock();
$authenticatorC = $this->getMockBuilder(AuthenticatorInterface::class)->getMock();
$authenticators = [$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'));
->willReturn('my_cool_firewall_1');
$enteredCredentials = array(
$enteredCredentials = [
'username' => '_weaverryan_test_user',
'password' => 'guard_auth_ftw',
);
];
$this->preAuthenticationToken->expects($this->atLeastOnce())
->method('getCredentials')
->will($this->returnValue($enteredCredentials));
->willReturn($enteredCredentials);
// authenticators A and C are never called
$authenticatorA->expects($this->never())
@@ -53,22 +57,22 @@ class GuardAuthenticationProviderTest extends TestCase
$authenticatorC->expects($this->never())
->method('getUser');
$mockedUser = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock();
$mockedUser = $this->getMockBuilder(UserInterface::class)->getMock();
$authenticatorB->expects($this->once())
->method('getUser')
->with($enteredCredentials, $this->userProvider)
->will($this->returnValue($mockedUser));
->willReturn($mockedUser);
// checkCredentials is called
$authenticatorB->expects($this->once())
->method('checkCredentials')
->with($enteredCredentials, $mockedUser)
// authentication works!
->will($this->returnValue(true));
$authedToken = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock();
->willReturn(true);
$authedToken = $this->getMockBuilder(GuardTokenInterface::class)->getMock();
$authenticatorB->expects($this->once())
->method('createAuthenticatedToken')
->with($mockedUser, $providerKey)
->will($this->returnValue($authedToken));
->willReturn($authedToken);
// user checker should be called
$this->userChecker->expects($this->once())
@@ -84,53 +88,143 @@ class GuardAuthenticationProviderTest extends TestCase
}
/**
* @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException
* @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();
$authenticators = [$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
->willReturn('my_cool_firewall_1');
$enteredCredentials = [
'username' => '_weaverryan_test_user',
'password' => 'guard_auth_ftw',
];
$this->preAuthenticationToken->expects($this->atLeastOnce())
->method('getCredentials')
->willReturn($enteredCredentials);
// authenticators A and C are never called
$authenticatorA->expects($this->never())
->method('getUser');
$authenticatorC->expects($this->never())
->method('getUser');
$mockedUser = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock();
$authenticatorB->expects($this->once())
->method('getUser')
->with($enteredCredentials, $this->userProvider)
->willReturn($mockedUser);
// checkCredentials is called
$authenticatorB->expects($this->once())
->method('checkCredentials')
->with($enteredCredentials, $mockedUser)
// authentication works!
->willReturn(true);
$authedToken = $this->getMockBuilder(GuardTokenInterface::class)->getMock();
$authenticatorB->expects($this->once())
->method('createAuthenticatedToken')
->with($mockedUser, $providerKey)
->willReturn($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);
}
public function testCheckCredentialsReturningNonTrueFailsAuthentication()
{
$this->expectException('Symfony\Component\Security\Core\Exception\BadCredentialsException');
$providerKey = 'my_uncool_firewall';
$authenticator = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorInterface')->getMock();
$authenticator = $this->getMockBuilder(AuthenticatorInterface::class)->getMock();
// make sure the authenticator is used
$this->preAuthenticationToken->expects($this->any())
->method('getGuardProviderKey')
// the 0 index, to match the only authenticator
->will($this->returnValue('my_uncool_firewall_0'));
->willReturn('my_uncool_firewall_0');
$this->preAuthenticationToken->expects($this->atLeastOnce())
->method('getCredentials')
->will($this->returnValue('non-null-value'));
->willReturn('non-null-value');
$mockedUser = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock();
$authenticator->expects($this->once())
->method('getUser')
->will($this->returnValue($mockedUser));
->willReturn($mockedUser);
// checkCredentials is called
$authenticator->expects($this->once())
->method('checkCredentials')
// authentication fails :(
->will($this->returnValue(null));
->willReturn(null);
$provider = new GuardAuthenticationProvider(array($authenticator), $this->userProvider, $providerKey, $this->userChecker);
$provider = new GuardAuthenticationProvider([$authenticator], $this->userProvider, $providerKey, $this->userChecker);
$provider->authenticate($this->preAuthenticationToken);
}
/**
* @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationExpiredException
*/
public function testGuardWithNoLongerAuthenticatedTriggersLogout()
{
$this->expectException('Symfony\Component\Security\Core\Exception\AuthenticationExpiredException');
$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->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock();
$token = new PostAuthenticationGuardToken($mockedUser, $providerKey, array('ROLE_USER'));
$token = new PostAuthenticationGuardToken($mockedUser, $providerKey, ['ROLE_USER']);
$token->setAuthenticated(false);
$provider = new GuardAuthenticationProvider(array(), $this->userProvider, $providerKey, $this->userChecker);
$actualToken = $provider->authenticate($token);
$provider = new GuardAuthenticationProvider([], $this->userProvider, $providerKey, $this->userChecker);
$provider->authenticate($token);
}
public function testSupportsChecksGuardAuthenticatorsTokenOrigin()
{
$authenticatorA = $this->getMockBuilder(AuthenticatorInterface::class)->getMock();
$authenticatorB = $this->getMockBuilder(AuthenticatorInterface::class)->getMock();
$authenticators = [$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);
}
public function testAuthenticateFailsOnNonOriginatingToken()
{
$this->expectException('Symfony\Component\Security\Core\Exception\AuthenticationException');
$this->expectExceptionMessageMatches('/second_firewall_0/');
$authenticatorA = $this->getMockBuilder(AuthenticatorInterface::class)->getMock();
$authenticators = [$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()
@@ -12,7 +12,7 @@
namespace Symfony\Component\Security\Guard\Token;
use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
use Symfony\Component\Security\Core\Role\RoleInterface;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Core\User\UserInterface;
/**
@@ -28,9 +28,9 @@ class PostAuthenticationGuardToken extends AbstractToken implements GuardTokenIn
private $providerKey;
/**
* @param UserInterface $user The user!
* @param string $providerKey The provider (firewall) key
* @param RoleInterface[]|string[] $roles An array of roles
* @param UserInterface $user The user!
* @param string $providerKey The provider (firewall) key
* @param (Role|string)[] $roles An array of roles
*
* @throws \InvalidArgumentException
*/
@@ -47,7 +47,7 @@ class PostAuthenticationGuardToken extends AbstractToken implements GuardTokenIn
// this token is meant to be used after authentication success, so it is always authenticated
// you could set it as non authenticated later if you need to
parent::setAuthenticated(true);
$this->setAuthenticated(true);
}
/**
@@ -58,7 +58,7 @@ class PostAuthenticationGuardToken extends AbstractToken implements GuardTokenIn
*/
public function getCredentials()
{
return array();
return [];
}
/**
@@ -76,7 +76,9 @@ class PostAuthenticationGuardToken extends AbstractToken implements GuardTokenIn
*/
public function serialize()
{
return serialize(array($this->providerKey, parent::serialize()));
$serialized = [$this->providerKey, parent::serialize(true)];
return $this->doSerialize($serialized, \func_num_args() ? func_get_arg(0) : null);
}
/**
@@ -84,7 +86,7 @@ class PostAuthenticationGuardToken extends AbstractToken implements GuardTokenIn
*/
public function unserialize($serialized)
{
list($this->providerKey, $parentStr) = unserialize($serialized);
list($this->providerKey, $parentStr) = \is_array($serialized) ? $serialized : unserialize($serialized);
parent::unserialize($parentStr);
}
}
@@ -29,14 +29,14 @@ 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)
{
$this->credentials = $credentials;
$this->guardProviderKey = $guardProviderKey;
parent::__construct(array());
parent::__construct([]);
// never authenticated
parent::setAuthenticated(false);
+4 -9
View File
@@ -16,9 +16,9 @@
}
],
"require": {
"php": ">=5.5.9",
"symfony/security-core": "~2.8|~3.0",
"symfony/security-http": "~3.1"
"php": "^5.5.9|>=7.0.8",
"symfony/security-core": "~3.4.22|^4.2.3",
"symfony/security-http": "^3.3.13|~4.0"
},
"require-dev": {
"psr/log": "~1.0"
@@ -29,10 +29,5 @@
"/Tests/"
]
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "3.2-dev"
}
}
"minimum-stability": "dev"
}
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/5.2/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"