Added tests
This commit is contained in:
parent
59f49b20ca
commit
6b9d78d5e0
@ -104,9 +104,8 @@ class FormLoginFactory extends AbstractFactory implements AuthenticatorFactoryIn
|
|||||||
$options = array_merge($defaultOptions, array_intersect_key($config, $defaultOptions));
|
$options = array_merge($defaultOptions, array_intersect_key($config, $defaultOptions));
|
||||||
$container
|
$container
|
||||||
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.form_login'))
|
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.form_login'))
|
||||||
->replaceArgument(1, isset($config['csrf_token_generator']) ? new Reference($config['csrf_token_generator']) : null)
|
->replaceArgument(1, new Reference($userProviderId))
|
||||||
->replaceArgument(2, new Reference($userProviderId))
|
->replaceArgument(2, $options);
|
||||||
->replaceArgument(3, $options);
|
|
||||||
|
|
||||||
return $authenticatorId;
|
return $authenticatorId;
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,6 @@
|
|||||||
abstract="true">
|
abstract="true">
|
||||||
<argument type="abstract">realm name</argument>
|
<argument type="abstract">realm name</argument>
|
||||||
<argument type="abstract">user provider</argument>
|
<argument type="abstract">user provider</argument>
|
||||||
<argument type="service" id="security.encoder_factory" />
|
|
||||||
<argument type="service" id="logger" on-invalid="null" />
|
<argument type="service" id="logger" on-invalid="null" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
@ -92,7 +91,6 @@
|
|||||||
class="Symfony\Component\Security\Http\Authenticator\FormLoginAuthenticator"
|
class="Symfony\Component\Security\Http\Authenticator\FormLoginAuthenticator"
|
||||||
abstract="true">
|
abstract="true">
|
||||||
<argument type="service" id="security.http_utils" />
|
<argument type="service" id="security.http_utils" />
|
||||||
<argument /> <!-- csrf token generator -->
|
|
||||||
<argument type="abstract">user provider</argument>
|
<argument type="abstract">user provider</argument>
|
||||||
<argument type="abstract">options</argument>
|
<argument type="abstract">options</argument>
|
||||||
</service>
|
</service>
|
||||||
|
@ -23,7 +23,6 @@ use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
|||||||
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\Token\PreAuthenticationToken;
|
|
||||||
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
|
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
|
||||||
use Symfony\Component\Security\Http\Event\LoginFailureEvent;
|
use Symfony\Component\Security\Http\Event\LoginFailureEvent;
|
||||||
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
|
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
|
||||||
@ -40,8 +39,6 @@ use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
|||||||
*/
|
*/
|
||||||
class AuthenticatorManager implements AuthenticatorManagerInterface, UserAuthenticatorInterface
|
class AuthenticatorManager implements AuthenticatorManagerInterface, UserAuthenticatorInterface
|
||||||
{
|
{
|
||||||
use AuthenticatorManagerTrait;
|
|
||||||
|
|
||||||
private $authenticators;
|
private $authenticators;
|
||||||
private $tokenStorage;
|
private $tokenStorage;
|
||||||
private $eventDispatcher;
|
private $eventDispatcher;
|
||||||
@ -131,7 +128,9 @@ class AuthenticatorManager implements AuthenticatorManagerInterface, UserAuthent
|
|||||||
// lazily (after initialization). This is important for e.g. the AnonymousAuthenticator
|
// lazily (after initialization). This is important for e.g. the AnonymousAuthenticator
|
||||||
// as its support is relying on the (initialized) token in the TokenStorage.
|
// as its support is relying on the (initialized) token in the TokenStorage.
|
||||||
if (false === $authenticator->supports($request)) {
|
if (false === $authenticator->supports($request)) {
|
||||||
$this->logger->debug('Skipping the "{authenticator}" authenticator as it did not support the request.', ['authenticator' => \get_class($authenticator)]);
|
if (null !== $this->logger) {
|
||||||
|
$this->logger->debug('Skipping the "{authenticator}" authenticator as it did not support the request.', ['authenticator' => \get_class($authenticator)]);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,21 +214,14 @@ class AuthenticatorManager implements AuthenticatorManagerInterface, UserAuthent
|
|||||||
throw new UsernameNotFoundException(sprintf('Null returned from "%s::getUser()".', \get_class($authenticator)));
|
throw new UsernameNotFoundException(sprintf('Null returned from "%s::getUser()".', \get_class($authenticator)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$user instanceof UserInterface) {
|
|
||||||
throw new \UnexpectedValueException(sprintf('The %s::getUser() method must return a UserInterface. You returned %s.', \get_class($authenticator), \is_object($user) ? \get_class($user) : \gettype($user)));
|
|
||||||
}
|
|
||||||
|
|
||||||
$event = new VerifyAuthenticatorCredentialsEvent($authenticator, $credentials, $user);
|
$event = new VerifyAuthenticatorCredentialsEvent($authenticator, $credentials, $user);
|
||||||
$this->eventDispatcher->dispatch($event);
|
$this->eventDispatcher->dispatch($event);
|
||||||
if (true !== $event->areCredentialsValid()) {
|
if (true !== $event->areCredentialsValid()) {
|
||||||
throw new BadCredentialsException(sprintf('Authentication failed because "%s" did not approve the credentials.', \get_class($authenticator)));
|
throw new BadCredentialsException(sprintf('Authentication failed because "%s" did not approve the credentials.', \get_class($authenticator)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// turn the UserInterface into a TokenInterface
|
// turn the UserInterface into a TokenInterface
|
||||||
$authenticatedToken = $authenticator->createAuthenticatedToken($user, $this->providerKey);
|
$authenticatedToken = $authenticator->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($authenticator), \is_object($authenticatedToken) ? \get_class($authenticatedToken) : \gettype($authenticatedToken)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (true === $this->eraseCredentials) {
|
if (true === $this->eraseCredentials) {
|
||||||
$authenticatedToken->eraseCredentials();
|
$authenticatedToken->eraseCredentials();
|
||||||
@ -259,21 +251,10 @@ class AuthenticatorManager implements AuthenticatorManagerInterface, UserAuthent
|
|||||||
return $loginSuccessEvent->getResponse();
|
return $loginSuccessEvent->getResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function handleAuthenticationFailure(AuthenticationException $exception, TokenInterface $token)
|
|
||||||
{
|
|
||||||
if (null !== $this->eventDispatcher) {
|
|
||||||
$this->eventDispatcher->dispatch(new AuthenticationFailureEvent($token, $exception), AuthenticationEvents::AUTHENTICATION_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
$exception->setToken($token);
|
|
||||||
|
|
||||||
throw $exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles an authentication failure and returns the Response for the authenticator.
|
* Handles an authentication failure and returns the Response for the authenticator.
|
||||||
*/
|
*/
|
||||||
private function handleAuthenticatorFailure(AuthenticationException $authenticationException, Request $request, AuthenticatorInterface $authenticator): ?Response
|
private function handleAuthenticationFailure(AuthenticationException $authenticationException, Request $request, AuthenticatorInterface $authenticator): ?Response
|
||||||
{
|
{
|
||||||
$response = $authenticator->onAuthenticationFailure($request, $authenticationException);
|
$response = $authenticator->onAuthenticationFailure($request, $authenticationException);
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
|||||||
use Symfony\Component\Security\Core\Security;
|
use Symfony\Component\Security\Core\Security;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||||
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
|
|
||||||
use Symfony\Component\Security\Http\HttpUtils;
|
use Symfony\Component\Security\Http\HttpUtils;
|
||||||
use Symfony\Component\Security\Http\ParameterBagUtils;
|
use Symfony\Component\Security\Http\ParameterBagUtils;
|
||||||
use Symfony\Component\Security\Http\Util\TargetPathTrait;
|
use Symfony\Component\Security\Http\Util\TargetPathTrait;
|
||||||
@ -38,13 +37,11 @@ class FormLoginAuthenticator extends AbstractLoginFormAuthenticator implements P
|
|||||||
|
|
||||||
private $options;
|
private $options;
|
||||||
private $httpUtils;
|
private $httpUtils;
|
||||||
private $csrfTokenManager;
|
|
||||||
private $userProvider;
|
private $userProvider;
|
||||||
|
|
||||||
public function __construct(HttpUtils $httpUtils, ?CsrfTokenManagerInterface $csrfTokenManager, UserProviderInterface $userProvider, array $options)
|
public function __construct(HttpUtils $httpUtils, UserProviderInterface $userProvider, array $options)
|
||||||
{
|
{
|
||||||
$this->httpUtils = $httpUtils;
|
$this->httpUtils = $httpUtils;
|
||||||
$this->csrfTokenManager = $csrfTokenManager;
|
|
||||||
$this->options = array_merge([
|
$this->options = array_merge([
|
||||||
'username_parameter' => '_username',
|
'username_parameter' => '_username',
|
||||||
'password_parameter' => '_password',
|
'password_parameter' => '_password',
|
||||||
@ -75,10 +72,7 @@ class FormLoginAuthenticator extends AbstractLoginFormAuthenticator implements P
|
|||||||
public function getCredentials(Request $request): array
|
public function getCredentials(Request $request): array
|
||||||
{
|
{
|
||||||
$credentials = [];
|
$credentials = [];
|
||||||
|
$credentials['csrf_token'] = ParameterBagUtils::getRequestParameterValue($request, $this->options['csrf_parameter']);
|
||||||
if (null !== $this->csrfTokenManager) {
|
|
||||||
$credentials['csrf_token'] = ParameterBagUtils::getRequestParameterValue($request, $this->options['csrf_parameter']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->options['post_only']) {
|
if ($this->options['post_only']) {
|
||||||
$credentials['username'] = ParameterBagUtils::getParameterBagValue($request->request, $this->options['username_parameter']);
|
$credentials['username'] = ParameterBagUtils::getParameterBagValue($request->request, $this->options['username_parameter']);
|
||||||
|
@ -16,7 +16,6 @@ use Symfony\Component\HttpFoundation\Request;
|
|||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
|
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
|
||||||
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
|
|
||||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||||
@ -33,14 +32,12 @@ class HttpBasicAuthenticator implements AuthenticatorInterface, AuthenticationEn
|
|||||||
{
|
{
|
||||||
private $realmName;
|
private $realmName;
|
||||||
private $userProvider;
|
private $userProvider;
|
||||||
private $encoderFactory;
|
|
||||||
private $logger;
|
private $logger;
|
||||||
|
|
||||||
public function __construct(string $realmName, UserProviderInterface $userProvider, EncoderFactoryInterface $encoderFactory, ?LoggerInterface $logger = null)
|
public function __construct(string $realmName, UserProviderInterface $userProvider, ?LoggerInterface $logger = null)
|
||||||
{
|
{
|
||||||
$this->realmName = $realmName;
|
$this->realmName = $realmName;
|
||||||
$this->userProvider = $userProvider;
|
$this->userProvider = $userProvider;
|
||||||
$this->encoderFactory = $encoderFactory;
|
|
||||||
$this->logger = $logger;
|
$this->logger = $logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Symfony\Component\Security\Http\Authenticator\Token;
|
namespace Symfony\Component\Security\Http\Authenticator;
|
||||||
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
@ -18,9 +18,7 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInt
|
|||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
|
||||||
use Symfony\Component\Security\Http\RememberMe\AbstractRememberMeServices;
|
use Symfony\Component\Security\Http\RememberMe\AbstractRememberMeServices;
|
||||||
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The RememberMe *Authenticator* performs remember me authentication.
|
* The RememberMe *Authenticator* performs remember me authentication.
|
||||||
@ -35,21 +33,22 @@ use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy;
|
|||||||
*
|
*
|
||||||
* @final
|
* @final
|
||||||
*/
|
*/
|
||||||
class RememberMeAuthenticator implements AuthenticatorInterface
|
class RememberMeAuthenticator implements AuthenticatorInterface, CustomAuthenticatedInterface
|
||||||
{
|
{
|
||||||
private $rememberMeServices;
|
private $rememberMeServices;
|
||||||
private $secret;
|
private $secret;
|
||||||
private $tokenStorage;
|
private $tokenStorage;
|
||||||
private $options;
|
private $options = [
|
||||||
private $sessionStrategy;
|
'secure' => false,
|
||||||
|
'httponly' => true,
|
||||||
|
];
|
||||||
|
|
||||||
public function __construct(AbstractRememberMeServices $rememberMeServices, string $secret, TokenStorageInterface $tokenStorage, array $options, ?SessionAuthenticationStrategy $sessionStrategy = null)
|
public function __construct(AbstractRememberMeServices $rememberMeServices, string $secret, TokenStorageInterface $tokenStorage, array $options)
|
||||||
{
|
{
|
||||||
$this->rememberMeServices = $rememberMeServices;
|
$this->rememberMeServices = $rememberMeServices;
|
||||||
$this->secret = $secret;
|
$this->secret = $secret;
|
||||||
$this->tokenStorage = $tokenStorage;
|
$this->tokenStorage = $tokenStorage;
|
||||||
$this->options = $options;
|
$this->options = array_merge($this->options, $options);
|
||||||
$this->sessionStrategy = $sessionStrategy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function supports(Request $request): ?bool
|
public function supports(Request $request): ?bool
|
||||||
@ -87,6 +86,12 @@ class RememberMeAuthenticator implements AuthenticatorInterface
|
|||||||
return $this->rememberMeServices->performLogin($credentials['cookie_parts'], $credentials['request']);
|
return $this->rememberMeServices->performLogin($credentials['cookie_parts'], $credentials['request']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function checkCredentials($credentials, UserInterface $user): bool
|
||||||
|
{
|
||||||
|
// remember me always is valid (if a user could be found)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public function createAuthenticatedToken(UserInterface $user, string $providerKey): TokenInterface
|
public function createAuthenticatedToken(UserInterface $user, string $providerKey): TokenInterface
|
||||||
{
|
{
|
||||||
return new RememberMeToken($user, $providerKey, $this->secret);
|
return new RememberMeToken($user, $providerKey, $this->secret);
|
||||||
@ -101,10 +106,6 @@ class RememberMeAuthenticator implements AuthenticatorInterface
|
|||||||
|
|
||||||
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey): ?Response
|
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey): ?Response
|
||||||
{
|
{
|
||||||
if ($request->hasSession() && $request->getSession()->isStarted()) {
|
|
||||||
$this->sessionStrategy->onAuthentication($request, $token);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ class PasswordMigratingListener implements EventSubscriberInterface
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null !== $password = $authenticator->getPassword($event->getCredentials())) {
|
if (null === $password = $authenticator->getPassword($event->getCredentials())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,11 +46,11 @@ class PasswordMigratingListener implements EventSubscriberInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
$passwordEncoder = $this->encoderFactory->getEncoder($user);
|
$passwordEncoder = $this->encoderFactory->getEncoder($user);
|
||||||
if (!method_exists($passwordEncoder, 'needsRehash') || !$passwordEncoder->needsRehash($user)) {
|
if (!$passwordEncoder->needsRehash($user->getPassword())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$authenticator->upgradePassword($user, $passwordEncoder->encodePassword($user, $password));
|
$authenticator->upgradePassword($user, $passwordEncoder->encodePassword($password, $user->getSalt()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getSubscribedEvents(): array
|
public static function getSubscribedEvents(): array
|
||||||
|
@ -39,7 +39,15 @@ class RememberMeListener implements EventSubscriberInterface
|
|||||||
|
|
||||||
public function onSuccessfulLogin(LoginSuccessEvent $event): void
|
public function onSuccessfulLogin(LoginSuccessEvent $event): void
|
||||||
{
|
{
|
||||||
if (!$this->isRememberMeEnabled($event->getAuthenticator(), $event->getProviderKey())) {
|
if (!$this->isRememberMeEnabled($event->getProviderKey(), $event->getAuthenticator())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $event->getResponse()) {
|
||||||
|
if (null !== $this->logger) {
|
||||||
|
$this->logger->debug('Remember me skipped: the authenticator did not set a success response.', ['authenticator' => \get_class($event->getAuthenticator())]);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,21 +56,21 @@ class RememberMeListener implements EventSubscriberInterface
|
|||||||
|
|
||||||
public function onFailedLogin(LoginFailureEvent $event): void
|
public function onFailedLogin(LoginFailureEvent $event): void
|
||||||
{
|
{
|
||||||
if (!$this->isRememberMeEnabled($event->getAuthenticator(), $event->getProviderKey())) {
|
if (!$this->isRememberMeEnabled($event->getProviderKey())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->rememberMeServices->loginFail($event->getRequest(), $event->getException());
|
$this->rememberMeServices->loginFail($event->getRequest(), $event->getException());
|
||||||
}
|
}
|
||||||
|
|
||||||
private function isRememberMeEnabled(AuthenticatorInterface $authenticator, string $providerKey): bool
|
private function isRememberMeEnabled(string $providerKey, ?AuthenticatorInterface $authenticator = null): bool
|
||||||
{
|
{
|
||||||
if ($providerKey !== $this->providerKey) {
|
if ($providerKey !== $this->providerKey) {
|
||||||
// This listener is created for a different firewall.
|
// This listener is created for a different firewall.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$authenticator instanceof RememberMeAuthenticatorInterface || !$authenticator->supportsRememberMe()) {
|
if (null !== $authenticator && (!$authenticator instanceof RememberMeAuthenticatorInterface || !$authenticator->supportsRememberMe())) {
|
||||||
if (null !== $this->logger) {
|
if (null !== $this->logger) {
|
||||||
$this->logger->debug('Remember me skipped: your authenticator does not support it.', ['authenticator' => \get_class($authenticator)]);
|
$this->logger->debug('Remember me skipped: your authenticator does not support it.', ['authenticator' => \get_class($authenticator)]);
|
||||||
}
|
}
|
||||||
|
@ -23,11 +23,19 @@ class UserCheckerListener implements EventSubscriberInterface
|
|||||||
|
|
||||||
public function preCredentialsVerification(VerifyAuthenticatorCredentialsEvent $event): void
|
public function preCredentialsVerification(VerifyAuthenticatorCredentialsEvent $event): void
|
||||||
{
|
{
|
||||||
|
if (null === $event->getUser()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$this->userChecker->checkPreAuth($event->getUser());
|
$this->userChecker->checkPreAuth($event->getUser());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function postCredentialsVerification(VerifyAuthenticatorCredentialsEvent $event): void
|
public function postCredentialsVerification(VerifyAuthenticatorCredentialsEvent $event): void
|
||||||
{
|
{
|
||||||
|
if (null === $event->getUser() || !$event->areCredentialsValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$this->userChecker->checkPostAuth($event->getUser());
|
$this->userChecker->checkPostAuth($event->getUser());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,10 @@ class VerifyAuthenticatorCredentialsListener implements EventSubscriberInterface
|
|||||||
|
|
||||||
public function onAuthenticating(VerifyAuthenticatorCredentialsEvent $event): void
|
public function onAuthenticating(VerifyAuthenticatorCredentialsEvent $event): void
|
||||||
{
|
{
|
||||||
|
if ($event->areCredentialsValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$authenticator = $event->getAuthenticator();
|
$authenticator = $event->getAuthenticator();
|
||||||
if ($authenticator instanceof PasswordAuthenticatedInterface) {
|
if ($authenticator instanceof PasswordAuthenticatedInterface) {
|
||||||
// Use the password encoder to validate the credentials
|
// Use the password encoder to validate the credentials
|
||||||
|
@ -0,0 +1,225 @@
|
|||||||
|
<?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\Http\Tests\Authentication;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent;
|
||||||
|
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
||||||
|
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authentication\AuthenticatorManager;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
||||||
|
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
|
||||||
|
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
|
||||||
|
use Symfony\Component\Security\Http\Event\VerifyAuthenticatorCredentialsEvent;
|
||||||
|
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||||
|
|
||||||
|
class AuthenticatorManagerTest extends TestCase
|
||||||
|
{
|
||||||
|
private $tokenStorage;
|
||||||
|
private $eventDispatcher;
|
||||||
|
private $request;
|
||||||
|
private $user;
|
||||||
|
private $token;
|
||||||
|
private $response;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->tokenStorage = $this->createMock(TokenStorageInterface::class);
|
||||||
|
$this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||||
|
$this->request = new Request();
|
||||||
|
$this->user = $this->createMock(UserInterface::class);
|
||||||
|
$this->token = $this->createMock(TokenInterface::class);
|
||||||
|
$this->response = $this->createMock(Response::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideSupportsData
|
||||||
|
*/
|
||||||
|
public function testSupports($authenticators, $result)
|
||||||
|
{
|
||||||
|
$manager = $this->createManager($authenticators);
|
||||||
|
|
||||||
|
$this->assertEquals($result, $manager->supports($this->request));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideSupportsData()
|
||||||
|
{
|
||||||
|
yield [[$this->createAuthenticator(null), $this->createAuthenticator(null)], null];
|
||||||
|
yield [[$this->createAuthenticator(null), $this->createAuthenticator(false)], null];
|
||||||
|
|
||||||
|
yield [[$this->createAuthenticator(null), $this->createAuthenticator(true)], true];
|
||||||
|
yield [[$this->createAuthenticator(true), $this->createAuthenticator(false)], true];
|
||||||
|
|
||||||
|
yield [[$this->createAuthenticator(false), $this->createAuthenticator(false)], false];
|
||||||
|
yield [[], false];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSupportCheckedUponRequestAuthentication()
|
||||||
|
{
|
||||||
|
// the attribute stores the supported authenticators, returning false now
|
||||||
|
// means support changed between calling supports() and authenticateRequest()
|
||||||
|
// (which is the case with lazy firewalls and e.g. the AnonymousAuthenticator)
|
||||||
|
$authenticator = $this->createAuthenticator(false);
|
||||||
|
$this->request->attributes->set('_guard_authenticators', [$authenticator]);
|
||||||
|
|
||||||
|
$authenticator->expects($this->never())->method('getCredentials');
|
||||||
|
|
||||||
|
$manager = $this->createManager([$authenticator]);
|
||||||
|
$manager->authenticateRequest($this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideMatchingAuthenticatorIndex
|
||||||
|
*/
|
||||||
|
public function testAuthenticateRequest($matchingAuthenticatorIndex)
|
||||||
|
{
|
||||||
|
$authenticators = [$this->createAuthenticator(0 === $matchingAuthenticatorIndex), $this->createAuthenticator(1 === $matchingAuthenticatorIndex)];
|
||||||
|
$this->request->attributes->set('_guard_authenticators', $authenticators);
|
||||||
|
$matchingAuthenticator = $authenticators[$matchingAuthenticatorIndex];
|
||||||
|
|
||||||
|
$authenticators[($matchingAuthenticatorIndex + 1) % 2]->expects($this->never())->method('getCredentials');
|
||||||
|
|
||||||
|
$matchingAuthenticator->expects($this->any())->method('getCredentials')->willReturn(['password' => 'pa$$']);
|
||||||
|
$matchingAuthenticator->expects($this->any())->method('getUser')->willReturn($this->user);
|
||||||
|
$this->eventDispatcher->expects($this->exactly(4))
|
||||||
|
->method('dispatch')
|
||||||
|
->with($this->callback(function ($event) use ($matchingAuthenticator) {
|
||||||
|
if ($event instanceof VerifyAuthenticatorCredentialsEvent) {
|
||||||
|
return $event->getAuthenticator() === $matchingAuthenticator
|
||||||
|
&& $event->getCredentials() === ['password' => 'pa$$']
|
||||||
|
&& $event->getUser() === $this->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $event instanceof InteractiveLoginEvent || $event instanceof LoginSuccessEvent || $event instanceof AuthenticationSuccessEvent;
|
||||||
|
}))
|
||||||
|
->will($this->returnCallback(function ($event) {
|
||||||
|
if ($event instanceof VerifyAuthenticatorCredentialsEvent) {
|
||||||
|
$event->setCredentialsValid(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $event;
|
||||||
|
}));
|
||||||
|
$matchingAuthenticator->expects($this->any())->method('createAuthenticatedToken')->willReturn($this->token);
|
||||||
|
|
||||||
|
$this->tokenStorage->expects($this->once())->method('setToken')->with($this->token);
|
||||||
|
|
||||||
|
$matchingAuthenticator->expects($this->any())
|
||||||
|
->method('onAuthenticationSuccess')
|
||||||
|
->with($this->anything(), $this->token, 'main')
|
||||||
|
->willReturn($this->response);
|
||||||
|
|
||||||
|
$manager = $this->createManager($authenticators);
|
||||||
|
$this->assertSame($this->response, $manager->authenticateRequest($this->request));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideMatchingAuthenticatorIndex()
|
||||||
|
{
|
||||||
|
yield [0];
|
||||||
|
yield [1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUserNotFound()
|
||||||
|
{
|
||||||
|
$authenticator = $this->createAuthenticator();
|
||||||
|
$this->request->attributes->set('_guard_authenticators', [$authenticator]);
|
||||||
|
|
||||||
|
$authenticator->expects($this->any())->method('getCredentials')->willReturn(['username' => 'john']);
|
||||||
|
$authenticator->expects($this->any())->method('getUser')->with(['username' => 'john'])->willReturn(null);
|
||||||
|
|
||||||
|
$authenticator->expects($this->once())
|
||||||
|
->method('onAuthenticationFailure')
|
||||||
|
->with($this->request, $this->isInstanceOf(UsernameNotFoundException::class));
|
||||||
|
|
||||||
|
$manager = $this->createManager([$authenticator]);
|
||||||
|
$manager->authenticateRequest($this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoCredentialsValidated()
|
||||||
|
{
|
||||||
|
$authenticator = $this->createAuthenticator();
|
||||||
|
$this->request->attributes->set('_guard_authenticators', [$authenticator]);
|
||||||
|
|
||||||
|
$authenticator->expects($this->any())->method('getCredentials')->willReturn(['username' => 'john']);
|
||||||
|
$authenticator->expects($this->any())->method('getUser')->willReturn($this->user);
|
||||||
|
|
||||||
|
$authenticator->expects($this->once())
|
||||||
|
->method('onAuthenticationFailure')
|
||||||
|
->with($this->request, $this->isInstanceOf(BadCredentialsException::class));
|
||||||
|
|
||||||
|
$manager = $this->createManager([$authenticator]);
|
||||||
|
$manager->authenticateRequest($this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideEraseCredentialsData
|
||||||
|
*/
|
||||||
|
public function testEraseCredentials($eraseCredentials)
|
||||||
|
{
|
||||||
|
$authenticator = $this->createAuthenticator();
|
||||||
|
$this->request->attributes->set('_guard_authenticators', [$authenticator]);
|
||||||
|
|
||||||
|
$authenticator->expects($this->any())->method('getCredentials')->willReturn(['username' => 'john']);
|
||||||
|
$authenticator->expects($this->any())->method('getUser')->willReturn($this->user);
|
||||||
|
$this->eventDispatcher->expects($this->any())
|
||||||
|
->method('dispatch')
|
||||||
|
->will($this->returnCallback(function ($event) {
|
||||||
|
if ($event instanceof VerifyAuthenticatorCredentialsEvent) {
|
||||||
|
$event->setCredentialsValid(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $event;
|
||||||
|
}));
|
||||||
|
|
||||||
|
$authenticator->expects($this->any())->method('createAuthenticatedToken')->willReturn($this->token);
|
||||||
|
|
||||||
|
$this->token->expects($eraseCredentials ? $this->once() : $this->never())->method('eraseCredentials');
|
||||||
|
|
||||||
|
$manager = $this->createManager([$authenticator], 'main', $eraseCredentials);
|
||||||
|
$manager->authenticateRequest($this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideEraseCredentialsData()
|
||||||
|
{
|
||||||
|
yield [true];
|
||||||
|
yield [false];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAuthenticateUser()
|
||||||
|
{
|
||||||
|
$authenticator = $this->createAuthenticator();
|
||||||
|
$authenticator->expects($this->any())->method('createAuthenticatedToken')->willReturn($this->token);
|
||||||
|
$authenticator->expects($this->any())->method('onAuthenticationSuccess')->willReturn($this->response);
|
||||||
|
|
||||||
|
$this->tokenStorage->expects($this->once())->method('setToken')->with($this->token);
|
||||||
|
|
||||||
|
$manager = $this->createManager([$authenticator]);
|
||||||
|
$this->assertSame($this->response, $manager->authenticateUser($this->user, $authenticator, $this->request));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createAuthenticator($supports = true)
|
||||||
|
{
|
||||||
|
$authenticator = $this->createMock(AuthenticatorInterface::class);
|
||||||
|
$authenticator->expects($this->any())->method('supports')->willReturn($supports);
|
||||||
|
|
||||||
|
return $authenticator;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createManager($authenticators, $providerKey = 'main', $eraseCredentials = true)
|
||||||
|
{
|
||||||
|
return new AuthenticatorManager($authenticators, $this->tokenStorage, $this->eventDispatcher, $providerKey, null, $eraseCredentials);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
<?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\Http\Tests\Authenticator;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\AnonymousAuthenticator;
|
||||||
|
|
||||||
|
class AnonymousAuthenticatorTest extends TestCase
|
||||||
|
{
|
||||||
|
private $tokenStorage;
|
||||||
|
private $authenticator;
|
||||||
|
private $request;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->tokenStorage = $this->createMock(TokenStorageInterface::class);
|
||||||
|
$this->authenticator = new AnonymousAuthenticator('s3cr3t', $this->tokenStorage);
|
||||||
|
$this->request = new Request();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideSupportsData
|
||||||
|
*/
|
||||||
|
public function testSupports($tokenAlreadyAvailable, $result)
|
||||||
|
{
|
||||||
|
$this->tokenStorage->expects($this->any())->method('getToken')->willReturn($tokenAlreadyAvailable ? $this->createMock(TokenStorageInterface::class) : null);
|
||||||
|
|
||||||
|
$this->assertEquals($result, $this->authenticator->supports($this->request));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideSupportsData()
|
||||||
|
{
|
||||||
|
yield [true, null];
|
||||||
|
yield [false, false];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAlwaysValidCredentials()
|
||||||
|
{
|
||||||
|
$this->assertTrue($this->authenticator->checkCredentials([], $this->createMock(UserInterface::class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAuthenticatedToken()
|
||||||
|
{
|
||||||
|
$token = $this->authenticator->createAuthenticatedToken($this->authenticator->getUser([]), 'main');
|
||||||
|
|
||||||
|
$this->assertTrue($token->isAuthenticated());
|
||||||
|
$this->assertEquals('anon.', $token->getUser());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,141 @@
|
|||||||
|
<?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\Http\Tests\Authenticator;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||||
|
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\FormLoginAuthenticator;
|
||||||
|
use Symfony\Component\Security\Http\HttpUtils;
|
||||||
|
|
||||||
|
class FormLoginAuthenticatorTest extends TestCase
|
||||||
|
{
|
||||||
|
private $userProvider;
|
||||||
|
private $authenticator;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->userProvider = $this->createMock(UserProviderInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideUsernamesForLength
|
||||||
|
*/
|
||||||
|
public function testHandleWhenUsernameLength($username, $ok)
|
||||||
|
{
|
||||||
|
if ($ok) {
|
||||||
|
$this->expectNotToPerformAssertions();
|
||||||
|
} else {
|
||||||
|
$this->expectException(BadCredentialsException::class);
|
||||||
|
$this->expectExceptionMessage('Invalid username.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$request = Request::create('/login_check', 'POST', ['_username' => $username]);
|
||||||
|
$request->setSession($this->createSession());
|
||||||
|
|
||||||
|
$this->setUpAuthenticator();
|
||||||
|
$this->authenticator->getCredentials($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideUsernamesForLength()
|
||||||
|
{
|
||||||
|
yield [str_repeat('x', Security::MAX_USERNAME_LENGTH + 1), false];
|
||||||
|
yield [str_repeat('x', Security::MAX_USERNAME_LENGTH - 1), true];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider postOnlyDataProvider
|
||||||
|
*/
|
||||||
|
public function testHandleNonStringUsernameWithArray($postOnly)
|
||||||
|
{
|
||||||
|
$this->expectException(BadRequestHttpException::class);
|
||||||
|
$this->expectExceptionMessage('The key "_username" must be a string, "array" given.');
|
||||||
|
|
||||||
|
$request = Request::create('/login_check', 'POST', ['_username' => []]);
|
||||||
|
$request->setSession($this->createSession());
|
||||||
|
|
||||||
|
$this->setUpAuthenticator(['post_only' => $postOnly]);
|
||||||
|
$this->authenticator->getCredentials($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider postOnlyDataProvider
|
||||||
|
*/
|
||||||
|
public function testHandleNonStringUsernameWithInt($postOnly)
|
||||||
|
{
|
||||||
|
$this->expectException(BadRequestHttpException::class);
|
||||||
|
$this->expectExceptionMessage('The key "_username" must be a string, "integer" given.');
|
||||||
|
|
||||||
|
$request = Request::create('/login_check', 'POST', ['_username' => 42]);
|
||||||
|
$request->setSession($this->createSession());
|
||||||
|
|
||||||
|
$this->setUpAuthenticator(['post_only' => $postOnly]);
|
||||||
|
$this->authenticator->getCredentials($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider postOnlyDataProvider
|
||||||
|
*/
|
||||||
|
public function testHandleNonStringUsernameWithObject($postOnly)
|
||||||
|
{
|
||||||
|
$this->expectException(BadRequestHttpException::class);
|
||||||
|
$this->expectExceptionMessage('The key "_username" must be a string, "object" given.');
|
||||||
|
|
||||||
|
$request = Request::create('/login_check', 'POST', ['_username' => new \stdClass()]);
|
||||||
|
$request->setSession($this->createSession());
|
||||||
|
|
||||||
|
$this->setUpAuthenticator(['post_only' => $postOnly]);
|
||||||
|
$this->authenticator->getCredentials($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider postOnlyDataProvider
|
||||||
|
*/
|
||||||
|
public function testHandleNonStringUsernameWith__toString($postOnly)
|
||||||
|
{
|
||||||
|
$usernameObject = $this->getMockBuilder(DummyUserClass::class)->getMock();
|
||||||
|
$usernameObject->expects($this->once())->method('__toString')->willReturn('someUsername');
|
||||||
|
|
||||||
|
$request = Request::create('/login_check', 'POST', ['_username' => $usernameObject]);
|
||||||
|
$request->setSession($this->createSession());
|
||||||
|
|
||||||
|
$this->setUpAuthenticator(['post_only' => $postOnly]);
|
||||||
|
$this->authenticator->getCredentials($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function postOnlyDataProvider()
|
||||||
|
{
|
||||||
|
yield [true];
|
||||||
|
yield [false];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setUpAuthenticator(array $options = [])
|
||||||
|
{
|
||||||
|
$this->authenticator = new FormLoginAuthenticator(new HttpUtils(), $this->userProvider, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createSession()
|
||||||
|
{
|
||||||
|
return $this->createMock('Symfony\Component\HttpFoundation\Session\SessionInterface');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DummyUserClass
|
||||||
|
{
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Symfony\Component\Security\Core\Tests\Authentication\Authenticator;
|
namespace Symfony\Component\Security\Http\Tests\Authenticator;
|
||||||
|
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
@ -14,12 +14,10 @@ use Symfony\Component\Security\Http\Authenticator\HttpBasicAuthenticator;
|
|||||||
|
|
||||||
class HttpBasicAuthenticatorTest extends TestCase
|
class HttpBasicAuthenticatorTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var UserProviderInterface|MockObject */
|
|
||||||
private $userProvider;
|
private $userProvider;
|
||||||
/** @var EncoderFactoryInterface|MockObject */
|
|
||||||
private $encoderFactory;
|
private $encoderFactory;
|
||||||
/** @var PasswordEncoderInterface|MockObject */
|
|
||||||
private $encoder;
|
private $encoder;
|
||||||
|
private $authenticator;
|
||||||
|
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
@ -30,17 +28,18 @@ class HttpBasicAuthenticatorTest extends TestCase
|
|||||||
->expects($this->any())
|
->expects($this->any())
|
||||||
->method('getEncoder')
|
->method('getEncoder')
|
||||||
->willReturn($this->encoder);
|
->willReturn($this->encoder);
|
||||||
|
|
||||||
|
$this->authenticator = new HttpBasicAuthenticator('test', $this->userProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testValidUsernameAndPasswordServerParameters()
|
public function testExtractCredentialsAndUserFromRequest()
|
||||||
{
|
{
|
||||||
$request = new Request([], [], [], [], [], [
|
$request = new Request([], [], [], [], [], [
|
||||||
'PHP_AUTH_USER' => 'TheUsername',
|
'PHP_AUTH_USER' => 'TheUsername',
|
||||||
'PHP_AUTH_PW' => 'ThePassword',
|
'PHP_AUTH_PW' => 'ThePassword',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$authenticator = new HttpBasicAuthenticator('test', $this->userProvider, $this->encoderFactory);
|
$credentials = $this->authenticator->getCredentials($request);
|
||||||
$credentials = $authenticator->getCredentials($request);
|
|
||||||
$this->assertEquals([
|
$this->assertEquals([
|
||||||
'username' => 'TheUsername',
|
'username' => 'TheUsername',
|
||||||
'password' => 'ThePassword',
|
'password' => 'ThePassword',
|
||||||
@ -55,53 +54,20 @@ class HttpBasicAuthenticatorTest extends TestCase
|
|||||||
->with('TheUsername')
|
->with('TheUsername')
|
||||||
->willReturn($mockedUser);
|
->willReturn($mockedUser);
|
||||||
|
|
||||||
$user = $authenticator->getUser($credentials, $this->userProvider);
|
$user = $this->authenticator->getUser($credentials);
|
||||||
$this->assertSame($mockedUser, $user);
|
$this->assertSame($mockedUser, $user);
|
||||||
|
|
||||||
$this->encoder
|
$this->assertEquals('ThePassword', $this->authenticator->getPassword($credentials));
|
||||||
->expects($this->any())
|
|
||||||
->method('isPasswordValid')
|
|
||||||
->with('ThePassword', 'ThePassword', null)
|
|
||||||
->willReturn(true);
|
|
||||||
|
|
||||||
$checkCredentials = $authenticator->checkCredentials($credentials, $user);
|
|
||||||
$this->assertTrue($checkCredentials);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @dataProvider provideInvalidPasswords */
|
/**
|
||||||
public function testInvalidPassword($presentedPassword, $exceptionMessage)
|
* @dataProvider provideMissingHttpBasicServerParameters
|
||||||
{
|
*/
|
||||||
$authenticator = new HttpBasicAuthenticator('test', $this->userProvider, $this->encoderFactory);
|
|
||||||
|
|
||||||
$this->encoder
|
|
||||||
->expects($this->any())
|
|
||||||
->method('isPasswordValid')
|
|
||||||
->willReturn(false);
|
|
||||||
|
|
||||||
$this->expectException(BadCredentialsException::class);
|
|
||||||
$this->expectExceptionMessage($exceptionMessage);
|
|
||||||
|
|
||||||
$authenticator->checkCredentials([
|
|
||||||
'username' => 'TheUsername',
|
|
||||||
'password' => $presentedPassword,
|
|
||||||
], $this->getMockBuilder(UserInterface::class)->getMock());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function provideInvalidPasswords()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
['InvalidPassword', 'The presented password is invalid.'],
|
|
||||||
['', 'The presented password cannot be empty.'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @dataProvider provideMissingHttpBasicServerParameters */
|
|
||||||
public function testHttpBasicServerParametersMissing(array $serverParameters)
|
public function testHttpBasicServerParametersMissing(array $serverParameters)
|
||||||
{
|
{
|
||||||
$request = new Request([], [], [], [], [], $serverParameters);
|
$request = new Request([], [], [], [], [], $serverParameters);
|
||||||
|
|
||||||
$authenticator = new HttpBasicAuthenticator('test', $this->userProvider, $this->encoderFactory);
|
$this->assertFalse($this->authenticator->supports($request));
|
||||||
$this->assertFalse($authenticator->supports($request));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideMissingHttpBasicServerParameters()
|
public function provideMissingHttpBasicServerParameters()
|
@ -0,0 +1,92 @@
|
|||||||
|
<?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\Http\Tests\Authenticator;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\HttpFoundation\Cookie;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\RememberMeAuthenticator;
|
||||||
|
use Symfony\Component\Security\Http\RememberMe\AbstractRememberMeServices;
|
||||||
|
|
||||||
|
class RememberMeAuthenticatorTest extends TestCase
|
||||||
|
{
|
||||||
|
private $rememberMeServices;
|
||||||
|
private $tokenStorage;
|
||||||
|
private $authenticator;
|
||||||
|
private $request;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->rememberMeServices = $this->createMock(AbstractRememberMeServices::class);
|
||||||
|
$this->tokenStorage = $this->createMock(TokenStorage::class);
|
||||||
|
$this->authenticator = new RememberMeAuthenticator($this->rememberMeServices, 's3cr3t', $this->tokenStorage, [
|
||||||
|
'name' => '_remember_me_cookie',
|
||||||
|
]);
|
||||||
|
$this->request = new Request();
|
||||||
|
$this->request->cookies->set('_remember_me_cookie', $val = $this->generateCookieValue());
|
||||||
|
$this->request->attributes->set(AbstractRememberMeServices::COOKIE_ATTR_NAME, new Cookie('_remember_me_cookie', $val));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSupportsTokenStorageWithToken()
|
||||||
|
{
|
||||||
|
$this->tokenStorage->expects($this->any())->method('getToken')->willReturn(TokenInterface::class);
|
||||||
|
|
||||||
|
$this->assertFalse($this->authenticator->supports($this->request));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSupportsRequestWithoutAttribute()
|
||||||
|
{
|
||||||
|
$this->request->attributes->remove(AbstractRememberMeServices::COOKIE_ATTR_NAME);
|
||||||
|
|
||||||
|
$this->assertNull($this->authenticator->supports($this->request));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSupportsRequestWithoutCookie()
|
||||||
|
{
|
||||||
|
$this->request->cookies->remove('_remember_me_cookie');
|
||||||
|
|
||||||
|
$this->assertFalse($this->authenticator->supports($this->request));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSupports()
|
||||||
|
{
|
||||||
|
$this->assertNull($this->authenticator->supports($this->request));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAuthenticate()
|
||||||
|
{
|
||||||
|
$credentials = $this->authenticator->getCredentials($this->request);
|
||||||
|
$this->assertEquals(['part1', 'part2'], $credentials['cookie_parts']);
|
||||||
|
$this->assertSame($this->request, $credentials['request']);
|
||||||
|
|
||||||
|
$user = $this->createMock(UserInterface::class);
|
||||||
|
$this->rememberMeServices->expects($this->any())
|
||||||
|
->method('performLogin')
|
||||||
|
->with($credentials['cookie_parts'], $credentials['request'])
|
||||||
|
->willReturn($user);
|
||||||
|
|
||||||
|
$this->assertSame($user, $this->authenticator->getUser($credentials));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCredentialsAlwaysValid()
|
||||||
|
{
|
||||||
|
$this->assertTrue($this->authenticator->checkCredentials([], $this->createMock(UserInterface::class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateCookieValue()
|
||||||
|
{
|
||||||
|
return base64_encode(implode(AbstractRememberMeServices::COOKIE_DELIMITER, ['part1', 'part2']));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
<?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\Http\Tests\EventListener;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
|
||||||
|
use Symfony\Component\Security\Csrf\CsrfToken;
|
||||||
|
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\CsrfProtectedAuthenticatorInterface;
|
||||||
|
use Symfony\Component\Security\Http\Event\VerifyAuthenticatorCredentialsEvent;
|
||||||
|
use Symfony\Component\Security\Http\EventListener\CsrfProtectionListener;
|
||||||
|
|
||||||
|
class CsrfProtectionListenerTest extends TestCase
|
||||||
|
{
|
||||||
|
private $csrfTokenManager;
|
||||||
|
private $listener;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->csrfTokenManager = $this->createMock(CsrfTokenManagerInterface::class);
|
||||||
|
$this->listener = new CsrfProtectionListener($this->csrfTokenManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNonCsrfProtectedAuthenticator()
|
||||||
|
{
|
||||||
|
$this->csrfTokenManager->expects($this->never())->method('isTokenValid');
|
||||||
|
|
||||||
|
$event = $this->createEvent($this->createAuthenticator(false));
|
||||||
|
$this->listener->verifyCredentials($event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidCsrfToken()
|
||||||
|
{
|
||||||
|
$this->csrfTokenManager->expects($this->any())
|
||||||
|
->method('isTokenValid')
|
||||||
|
->with(new CsrfToken('authenticator_token_id', 'abc123'))
|
||||||
|
->willReturn(true);
|
||||||
|
|
||||||
|
$event = $this->createEvent($this->createAuthenticator(true), ['_csrf' => 'abc123']);
|
||||||
|
$this->listener->verifyCredentials($event);
|
||||||
|
|
||||||
|
$this->expectNotToPerformAssertions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInvalidCsrfToken()
|
||||||
|
{
|
||||||
|
$this->expectException(InvalidCsrfTokenException::class);
|
||||||
|
$this->expectExceptionMessage('Invalid CSRF token.');
|
||||||
|
|
||||||
|
$this->csrfTokenManager->expects($this->any())
|
||||||
|
->method('isTokenValid')
|
||||||
|
->with(new CsrfToken('authenticator_token_id', 'abc123'))
|
||||||
|
->willReturn(false);
|
||||||
|
|
||||||
|
$event = $this->createEvent($this->createAuthenticator(true), ['_csrf' => 'abc123']);
|
||||||
|
$this->listener->verifyCredentials($event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createEvent($authenticator, $credentials = null)
|
||||||
|
{
|
||||||
|
return new VerifyAuthenticatorCredentialsEvent($authenticator, $credentials, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createAuthenticator($supportsCsrf)
|
||||||
|
{
|
||||||
|
if (!$supportsCsrf) {
|
||||||
|
return $this->createMock(AuthenticatorInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
$authenticator = $this->createMock([AuthenticatorInterface::class, CsrfProtectedAuthenticatorInterface::class]);
|
||||||
|
$authenticator->expects($this->any())->method('getCsrfTokenId')->willReturn('authenticator_token_id');
|
||||||
|
$authenticator->expects($this->any())
|
||||||
|
->method('getCsrfToken')
|
||||||
|
->with(['_csrf' => 'abc123'])
|
||||||
|
->willReturn('abc123');
|
||||||
|
|
||||||
|
return $authenticator;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
<?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\Http\Tests\EventListener;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
|
||||||
|
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
|
||||||
|
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\PasswordAuthenticatedInterface;
|
||||||
|
use Symfony\Component\Security\Http\Event\VerifyAuthenticatorCredentialsEvent;
|
||||||
|
use Symfony\Component\Security\Http\EventListener\PasswordMigratingListener;
|
||||||
|
|
||||||
|
class PasswordMigratingListenerTest extends TestCase
|
||||||
|
{
|
||||||
|
private $encoderFactory;
|
||||||
|
private $listener;
|
||||||
|
private $user;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->encoderFactory = $this->createMock(EncoderFactoryInterface::class);
|
||||||
|
$this->listener = new PasswordMigratingListener($this->encoderFactory);
|
||||||
|
$this->user = $this->createMock(UserInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideUnsupportedEvents
|
||||||
|
*/
|
||||||
|
public function testUnsupportedEvents($event)
|
||||||
|
{
|
||||||
|
$this->encoderFactory->expects($this->never())->method('getEncoder');
|
||||||
|
|
||||||
|
$this->listener->onCredentialsVerification($event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideUnsupportedEvents()
|
||||||
|
{
|
||||||
|
// unsupported authenticators
|
||||||
|
yield [$this->createEvent($this->createMock(AuthenticatorInterface::class), $this->user)];
|
||||||
|
yield [$this->createEvent($this->createMock([AuthenticatorInterface::class, PasswordAuthenticatedInterface::class]), $this->user)];
|
||||||
|
|
||||||
|
// null password
|
||||||
|
yield [$this->createEvent($this->createAuthenticator(null), $this->user)];
|
||||||
|
|
||||||
|
// no user
|
||||||
|
yield [$this->createEvent($this->createAuthenticator('pa$$word'), null)];
|
||||||
|
|
||||||
|
// invalid password
|
||||||
|
yield [$this->createEvent($this->createAuthenticator('pa$$word'), $this->user, false)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUpgrade()
|
||||||
|
{
|
||||||
|
$encoder = $this->createMock(PasswordEncoderInterface::class);
|
||||||
|
$encoder->expects($this->any())->method('needsRehash')->willReturn(true);
|
||||||
|
$encoder->expects($this->any())->method('encodePassword')->with('pa$$word', null)->willReturn('new-encoded-password');
|
||||||
|
|
||||||
|
$this->encoderFactory->expects($this->any())->method('getEncoder')->with($this->user)->willReturn($encoder);
|
||||||
|
|
||||||
|
$this->user->expects($this->any())->method('getPassword')->willReturn('old-encoded-password');
|
||||||
|
|
||||||
|
$authenticator = $this->createAuthenticator('pa$$word');
|
||||||
|
$authenticator->expects($this->once())
|
||||||
|
->method('upgradePassword')
|
||||||
|
->with($this->user, 'new-encoded-password')
|
||||||
|
;
|
||||||
|
|
||||||
|
$event = $this->createEvent($authenticator, $this->user);
|
||||||
|
$this->listener->onCredentialsVerification($event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return AuthenticatorInterface
|
||||||
|
*/
|
||||||
|
private function createAuthenticator($password)
|
||||||
|
{
|
||||||
|
$authenticator = $this->createMock([AuthenticatorInterface::class, PasswordAuthenticatedInterface::class, PasswordUpgraderInterface::class]);
|
||||||
|
$authenticator->expects($this->any())->method('getPassword')->willReturn($password);
|
||||||
|
|
||||||
|
return $authenticator;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createEvent($authenticator, $user, $credentialsValid = true)
|
||||||
|
{
|
||||||
|
$event = new VerifyAuthenticatorCredentialsEvent($authenticator, [], $user);
|
||||||
|
$event->setCredentialsValid($credentialsValid);
|
||||||
|
|
||||||
|
return $event;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
<?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\Http\Tests\EventListener;
|
||||||
|
|
||||||
|
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\Core\Exception\AuthenticationException;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\RememberMeAuthenticatorInterface;
|
||||||
|
use Symfony\Component\Security\Http\Event\LoginFailureEvent;
|
||||||
|
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
|
||||||
|
use Symfony\Component\Security\Http\EventListener\RememberMeListener;
|
||||||
|
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
|
||||||
|
|
||||||
|
class RememberMeListenerTest extends TestCase
|
||||||
|
{
|
||||||
|
private $rememberMeServices;
|
||||||
|
private $listener;
|
||||||
|
private $request;
|
||||||
|
private $response;
|
||||||
|
private $token;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->rememberMeServices = $this->createMock(RememberMeServicesInterface::class);
|
||||||
|
$this->listener = new RememberMeListener($this->rememberMeServices);
|
||||||
|
$this->request = $this->getMockBuilder(Request::class)->disableOriginalConstructor()->getMock();
|
||||||
|
$this->response = $this->createMock(Response::class);
|
||||||
|
$this->token = $this->createMock(TokenInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideUnsupportingAuthenticators
|
||||||
|
*/
|
||||||
|
public function testSuccessfulLoginWithoutSupportingAuthenticator($authenticator)
|
||||||
|
{
|
||||||
|
$this->rememberMeServices->expects($this->never())->method('loginSuccess');
|
||||||
|
|
||||||
|
$event = $this->createLoginSuccessfulEvent('main_firewall', $this->response, $authenticator);
|
||||||
|
$this->listener->onSuccessfulLogin($event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideUnsupportingAuthenticators()
|
||||||
|
{
|
||||||
|
yield [$this->createMock(AuthenticatorInterface::class)];
|
||||||
|
|
||||||
|
$authenticator = $this->createMock([AuthenticatorInterface::class, RememberMeAuthenticatorInterface::class]);
|
||||||
|
$authenticator->expects($this->any())->method('supportsRememberMe')->willReturn(false);
|
||||||
|
yield [$authenticator];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSuccessfulLoginWithoutSuccessResponse()
|
||||||
|
{
|
||||||
|
$this->rememberMeServices->expects($this->never())->method('loginSuccess');
|
||||||
|
|
||||||
|
$event = $this->createLoginSuccessfulEvent('main_firewall', null);
|
||||||
|
$this->listener->onSuccessfulLogin($event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSuccessfulLogin()
|
||||||
|
{
|
||||||
|
$this->rememberMeServices->expects($this->once())->method('loginSuccess')->with($this->request, $this->response, $this->token);
|
||||||
|
|
||||||
|
$event = $this->createLoginSuccessfulEvent('main_firewall', $this->response);
|
||||||
|
$this->listener->onSuccessfulLogin($event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCredentialsInvalid()
|
||||||
|
{
|
||||||
|
$this->rememberMeServices->expects($this->once())->method('loginFail')->with($this->request, $this->isInstanceOf(AuthenticationException::class));
|
||||||
|
|
||||||
|
$event = $this->createLoginFailureEvent('main_firewall');
|
||||||
|
$this->listener->onFailedLogin($event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createLoginSuccessfulEvent($providerKey, $response, $authenticator = null)
|
||||||
|
{
|
||||||
|
if (null === $authenticator) {
|
||||||
|
$authenticator = $this->createMock([AuthenticatorInterface::class, RememberMeAuthenticatorInterface::class]);
|
||||||
|
$authenticator->expects($this->any())->method('supportsRememberMe')->willReturn(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new LoginSuccessEvent($authenticator, $this->token, $this->request, $response, $providerKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createLoginFailureEvent($providerKey)
|
||||||
|
{
|
||||||
|
return new LoginFailureEvent(new AuthenticationException(), $this->createMock(AuthenticatorInterface::class), $this->request, null, $providerKey);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
<?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\Http\Tests\EventListener;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
||||||
|
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
|
||||||
|
use Symfony\Component\Security\Http\EventListener\SessionStrategyListener;
|
||||||
|
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
|
||||||
|
|
||||||
|
class SessionListenerTest extends TestCase
|
||||||
|
{
|
||||||
|
private $sessionAuthenticationStrategy;
|
||||||
|
private $listener;
|
||||||
|
private $request;
|
||||||
|
private $token;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->sessionAuthenticationStrategy = $this->createMock(SessionAuthenticationStrategyInterface::class);
|
||||||
|
$this->listener = new SessionStrategyListener($this->sessionAuthenticationStrategy);
|
||||||
|
$this->request = new Request();
|
||||||
|
$this->token = $this->createMock(TokenInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRequestWithSession()
|
||||||
|
{
|
||||||
|
$this->configurePreviousSession();
|
||||||
|
|
||||||
|
$this->sessionAuthenticationStrategy->expects($this->once())->method('onAuthentication')->with($this->request, $this->token);
|
||||||
|
|
||||||
|
$this->listener->onSuccessfulLogin($this->createEvent('main_firewall'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRequestWithoutPreviousSession()
|
||||||
|
{
|
||||||
|
$this->sessionAuthenticationStrategy->expects($this->never())->method('onAuthentication')->with($this->request, $this->token);
|
||||||
|
|
||||||
|
$this->listener->onSuccessfulLogin($this->createEvent('main_firewall'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testStatelessFirewalls()
|
||||||
|
{
|
||||||
|
$this->sessionAuthenticationStrategy->expects($this->never())->method('onAuthentication');
|
||||||
|
|
||||||
|
$listener = new SessionStrategyListener($this->sessionAuthenticationStrategy, ['api_firewall']);
|
||||||
|
$listener->onSuccessfulLogin($this->createEvent('api_firewall'));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createEvent($providerKey)
|
||||||
|
{
|
||||||
|
return new LoginSuccessEvent($this->createMock(AuthenticatorInterface::class), $this->token, $this->request, null, $providerKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
<?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\Http\Tests\EventListener;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Security\Core\User\UserCheckerInterface;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
||||||
|
use Symfony\Component\Security\Http\Event\VerifyAuthenticatorCredentialsEvent;
|
||||||
|
use Symfony\Component\Security\Http\EventListener\UserCheckerListener;
|
||||||
|
|
||||||
|
class UserCheckerListenerTest extends TestCase
|
||||||
|
{
|
||||||
|
private $userChecker;
|
||||||
|
private $listener;
|
||||||
|
private $user;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->userChecker = $this->createMock(UserCheckerInterface::class);
|
||||||
|
$this->listener = new UserCheckerListener($this->userChecker);
|
||||||
|
$this->user = $this->createMock(UserInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPreAuth()
|
||||||
|
{
|
||||||
|
$this->userChecker->expects($this->once())->method('checkPreAuth')->with($this->user);
|
||||||
|
|
||||||
|
$this->listener->preCredentialsVerification($this->createEvent());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPreAuthNoUser()
|
||||||
|
{
|
||||||
|
$this->userChecker->expects($this->never())->method('checkPreAuth');
|
||||||
|
|
||||||
|
$this->listener->preCredentialsVerification($this->createEvent(true, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPostAuthValidCredentials()
|
||||||
|
{
|
||||||
|
$this->userChecker->expects($this->once())->method('checkPostAuth')->with($this->user);
|
||||||
|
|
||||||
|
$this->listener->postCredentialsVerification($this->createEvent(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPostAuthInvalidCredentials()
|
||||||
|
{
|
||||||
|
$this->userChecker->expects($this->never())->method('checkPostAuth')->with($this->user);
|
||||||
|
|
||||||
|
$this->listener->postCredentialsVerification($this->createEvent());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPostAuthNoUser()
|
||||||
|
{
|
||||||
|
$this->userChecker->expects($this->never())->method('checkPostAuth');
|
||||||
|
|
||||||
|
$this->listener->postCredentialsVerification($this->createEvent(true, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createEvent($credentialsValid = false, $customUser = false)
|
||||||
|
{
|
||||||
|
$event = new VerifyAuthenticatorCredentialsEvent($this->createMock(AuthenticatorInterface::class), [], false === $customUser ? $this->user : $customUser);
|
||||||
|
if ($credentialsValid) {
|
||||||
|
$event->setCredentialsValid(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $event;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,167 @@
|
|||||||
|
<?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\Http\Tests\EventListener;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
|
||||||
|
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
|
||||||
|
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
||||||
|
use Symfony\Component\Security\Core\Exception\LogicException;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\CustomAuthenticatedInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\PasswordAuthenticatedInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\TokenAuthenticatedInterface;
|
||||||
|
use Symfony\Component\Security\Http\Event\VerifyAuthenticatorCredentialsEvent;
|
||||||
|
use Symfony\Component\Security\Http\EventListener\VerifyAuthenticatorCredentialsListener;
|
||||||
|
|
||||||
|
class VerifyAuthenticatorCredentialsListenerTest extends TestCase
|
||||||
|
{
|
||||||
|
private $encoderFactory;
|
||||||
|
private $listener;
|
||||||
|
private $user;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->encoderFactory = $this->createMock(EncoderFactoryInterface::class);
|
||||||
|
$this->listener = new VerifyAuthenticatorCredentialsListener($this->encoderFactory);
|
||||||
|
$this->user = $this->createMock(UserInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider providePasswords
|
||||||
|
*/
|
||||||
|
public function testPasswordAuthenticated($password, $passwordValid, $result)
|
||||||
|
{
|
||||||
|
$this->user->expects($this->any())->method('getPassword')->willReturn('encoded-password');
|
||||||
|
|
||||||
|
$encoder = $this->createMock(PasswordEncoderInterface::class);
|
||||||
|
$encoder->expects($this->any())->method('isPasswordValid')->with('encoded-password', $password)->willReturn($passwordValid);
|
||||||
|
|
||||||
|
$this->encoderFactory->expects($this->any())->method('getEncoder')->with($this->identicalTo($this->user))->willReturn($encoder);
|
||||||
|
|
||||||
|
$event = new VerifyAuthenticatorCredentialsEvent($this->createAuthenticator('password', $password), ['password' => $password], $this->user);
|
||||||
|
$this->listener->onAuthenticating($event);
|
||||||
|
$this->assertEquals($result, $event->areCredentialsValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function providePasswords()
|
||||||
|
{
|
||||||
|
yield ['ThePa$$word', true, true];
|
||||||
|
yield ['Invalid', false, false];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEmptyPassword()
|
||||||
|
{
|
||||||
|
$this->expectException(BadCredentialsException::class);
|
||||||
|
$this->expectExceptionMessage('The presented password cannot be empty.');
|
||||||
|
|
||||||
|
$this->encoderFactory->expects($this->never())->method('getEncoder');
|
||||||
|
|
||||||
|
$event = new VerifyAuthenticatorCredentialsEvent($this->createAuthenticator('password', ''), ['password' => ''], $this->user);
|
||||||
|
$this->listener->onAuthenticating($event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTokenAuthenticated()
|
||||||
|
{
|
||||||
|
$this->encoderFactory->expects($this->never())->method('getEncoder');
|
||||||
|
|
||||||
|
$event = new VerifyAuthenticatorCredentialsEvent($this->createAuthenticator('token', 'some_token'), ['token' => 'abc'], $this->user);
|
||||||
|
$this->listener->onAuthenticating($event);
|
||||||
|
|
||||||
|
$this->assertTrue($event->areCredentialsValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTokenAuthenticatedReturningNull()
|
||||||
|
{
|
||||||
|
$this->encoderFactory->expects($this->never())->method('getEncoder');
|
||||||
|
|
||||||
|
$event = new VerifyAuthenticatorCredentialsEvent($this->createAuthenticator('token', null), ['token' => 'abc'], $this->user);
|
||||||
|
$this->listener->onAuthenticating($event);
|
||||||
|
|
||||||
|
$this->assertFalse($event->areCredentialsValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideCustomAuthenticatedResults
|
||||||
|
*/
|
||||||
|
public function testCustomAuthenticated($result)
|
||||||
|
{
|
||||||
|
$this->encoderFactory->expects($this->never())->method('getEncoder');
|
||||||
|
|
||||||
|
$event = new VerifyAuthenticatorCredentialsEvent($this->createAuthenticator('custom', $result), [], $this->user);
|
||||||
|
$this->listener->onAuthenticating($event);
|
||||||
|
|
||||||
|
$this->assertEquals($result, $event->areCredentialsValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideCustomAuthenticatedResults()
|
||||||
|
{
|
||||||
|
yield [true];
|
||||||
|
yield [false];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAlreadyAuthenticated()
|
||||||
|
{
|
||||||
|
$event = new VerifyAuthenticatorCredentialsEvent($this->createAuthenticator(), [], $this->user);
|
||||||
|
$event->setCredentialsValid(true);
|
||||||
|
$this->listener->onAuthenticating($event);
|
||||||
|
|
||||||
|
$this->assertTrue($event->areCredentialsValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoAuthenticatedInterfaceImplemented()
|
||||||
|
{
|
||||||
|
$authenticator = $this->createAuthenticator();
|
||||||
|
$this->expectException(LogicException::class);
|
||||||
|
$this->expectExceptionMessage(sprintf('Authenticator %s does not have valid credentials. Authenticators must implement one of the authenticated interfaces (%s, %s or %s).', \get_class($authenticator), PasswordAuthenticatedInterface::class, TokenAuthenticatedInterface::class, CustomAuthenticatedInterface::class));
|
||||||
|
|
||||||
|
$this->encoderFactory->expects($this->never())->method('getEncoder');
|
||||||
|
|
||||||
|
$event = new VerifyAuthenticatorCredentialsEvent($authenticator, [], $this->user);
|
||||||
|
$this->listener->onAuthenticating($event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return AuthenticatorInterface
|
||||||
|
*/
|
||||||
|
private function createAuthenticator(?string $type = null, $result = null)
|
||||||
|
{
|
||||||
|
$interfaces = [AuthenticatorInterface::class];
|
||||||
|
switch ($type) {
|
||||||
|
case 'password':
|
||||||
|
$interfaces[] = PasswordAuthenticatedInterface::class;
|
||||||
|
break;
|
||||||
|
case 'token':
|
||||||
|
$interfaces[] = TokenAuthenticatedInterface::class;
|
||||||
|
break;
|
||||||
|
case 'custom':
|
||||||
|
$interfaces[] = CustomAuthenticatedInterface::class;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$authenticator = $this->createMock(1 === \count($interfaces) ? $interfaces[0] : $interfaces);
|
||||||
|
switch ($type) {
|
||||||
|
case 'password':
|
||||||
|
$authenticator->expects($this->any())->method('getPassword')->willReturn($result);
|
||||||
|
break;
|
||||||
|
case 'token':
|
||||||
|
$authenticator->expects($this->any())->method('getToken')->willReturn($result);
|
||||||
|
break;
|
||||||
|
case 'custom':
|
||||||
|
$authenticator->expects($this->any())->method('checkCredentials')->willReturn($result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $authenticator;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user