diff --git a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md
index ffb44752149b4..3750985e24a29 100644
--- a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md
+++ b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md
@@ -5,6 +5,7 @@ CHANGELOG
---
* Add `Security::isGrantedForUser()` to test user authorization without relying on the session. For example, users not currently logged in, or while processing a message from a message queue
+ * Add `security.firewalls.not_full_fledged_handler` option to configure behavior where user is not full fledged
7.2
---
diff --git a/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php b/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php
index f3c1cd1fe34af..23d031087b2db 100644
--- a/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php
+++ b/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php
@@ -176,6 +176,7 @@ public function collect(Request $request, Response $response, ?\Throwable $excep
'access_denied_url' => $firewallConfig->getAccessDeniedUrl(),
'user_checker' => $firewallConfig->getUserChecker(),
'authenticators' => $firewallConfig->getAuthenticators(),
+ 'not_full_fledged_handler' => $firewallConfig->getNotFullFledgedHandler(),
];
// generate exit impersonation path from current request
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php
index a45276066484c..2d5fb8cf53f75 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php
@@ -17,6 +17,8 @@
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
+use Symfony\Component\Security\Http\Authorization\NotFullFledgedEqualNormalLoginHandler;
+use Symfony\Component\Security\Http\Authorization\NotFullFledgedRedirectToStartAuthenticationHandler;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy;
@@ -214,6 +216,17 @@ private function addFirewallsSection(ArrayNodeDefinition $rootNode, array $facto
->booleanNode('stateless')->defaultFalse()->end()
->booleanNode('lazy')->defaultFalse()->end()
->scalarNode('context')->cannotBeEmpty()->end()
+ ->scalarNode('not_full_fledged_handler')
+ ->defaultValue(NotFullFledgedRedirectToStartAuthenticationHandler::class)
+ ->beforeNormalization()
+ ->ifTrue(fn ($v): bool => 'redirect' == $v)
+ ->then(fn ($v) => NotFullFledgedRedirectToStartAuthenticationHandler::class)
+ ->end()
+ ->beforeNormalization()
+ ->ifTrue(fn ($v): bool => 'equal' == $v)
+ ->then(fn ($v) => NotFullFledgedEqualNormalLoginHandler::class)
+ ->end()
+ ->end()
->arrayNode('logout')
->treatTrueLike([])
->canBeUnset()
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
index f454b9318c183..e834df36b6002 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
@@ -582,6 +582,7 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
$config->replaceArgument(10, $listenerKeys);
$config->replaceArgument(11, $firewall['switch_user'] ?? null);
+ $config->replaceArgument(13, $firewall['not_full_fledged_handler'] ?? null);
return [$matcher, $listeners, $exceptionListener, null !== $logoutListenerId ? new Reference($logoutListenerId) : null, $firewallAuthenticationProviders];
}
@@ -890,6 +891,10 @@ private function createExceptionListener(ContainerBuilder $container, array $con
$listener->replaceArgument(5, $config['access_denied_url']);
}
+ if (isset($config['not_full_fledged_handler'])) {
+ $listener->replaceArgument(9, new Reference($config['not_full_fledged_handler']));
+ }
+
return $exceptionListenerId;
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.php b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.php
index bd879973b49a3..0d60bbd2091ac 100644
--- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.php
+++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.php
@@ -45,6 +45,8 @@
use Symfony\Component\Security\Core\User\MissingUserProvider;
use Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
+use Symfony\Component\Security\Http\Authorization\NotFullFledgedEqualNormalLoginHandler;
+use Symfony\Component\Security\Http\Authorization\NotFullFledgedRedirectToStartAuthenticationHandler;
use Symfony\Component\Security\Http\Controller\SecurityTokenValueResolver;
use Symfony\Component\Security\Http\Controller\UserValueResolver;
use Symfony\Component\Security\Http\EventListener\IsGrantedAttributeListener;
@@ -230,6 +232,7 @@
[], // listeners
null, // switch_user
null, // logout
+ null, // not_full_fledged_handler
])
->set('security.logout_url_generator', LogoutUrlGenerator::class)
@@ -322,5 +325,8 @@
->set('cache.security_is_csrf_token_valid_attribute_expression_language')
->parent('cache.system')
->tag('cache.pool')
+
+ ->set(NotFullFledgedRedirectToStartAuthenticationHandler::class)
+ ->set(NotFullFledgedEqualNormalLoginHandler::class)
;
};
diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.php b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.php
index 952b1d75625ad..a378e393773df 100644
--- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.php
+++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.php
@@ -139,6 +139,7 @@
service('security.access.denied_handler')->nullOnInvalid(),
service('logger')->nullOnInvalid(),
false, // Stateless
+ service('security.not.full.fledged_handler')->nullOnInvalid(),
])
->tag('monolog.logger', ['channel' => 'security'])
diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig
index 635d61e2dd2c8..5694ef3f42754 100644
--- a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig
+++ b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig
@@ -334,6 +334,10 @@
authenticators |
{{ collector.firewall.authenticators is empty ? '(none)' : profiler_dump(collector.firewall.authenticators, maxDepth=1) }} |
+
+ not_full_fledged_handler |
+ {{ collector.firewall.not_full_fledged_handler ?: '(none)' }} |
+
{% endif %}
diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallConfig.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallConfig.php
index 16edc6319a806..c4615b6be2fd5 100644
--- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallConfig.php
+++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallConfig.php
@@ -30,6 +30,7 @@ public function __construct(
private readonly array $authenticators = [],
private readonly ?array $switchUser = null,
private readonly ?array $logout = null,
+ private readonly ?string $notFullFledgedHandler = null,
) {
}
@@ -104,4 +105,9 @@ public function getLogout(): ?array
{
return $this->logout;
}
+
+ public function getNotFullFledgedHandler(): ?string
+ {
+ return $this->notFullFledgedHandler;
+ }
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTestCase.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTestCase.php
index 04fba9fe584d3..bc8a468a237b9 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTestCase.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTestCase.php
@@ -35,6 +35,7 @@
use Symfony\Component\Security\Http\Authentication\AuthenticatorManager;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
+use Symfony\Component\Security\Http\Authorization\NotFullFledgedRedirectToStartAuthenticationHandler;
abstract class CompleteConfigurationTestCase extends TestCase
{
@@ -149,6 +150,7 @@ public function testFirewalls()
[],
null,
null,
+ null,
],
[
'secure',
@@ -184,6 +186,7 @@ public function testFirewalls()
'enable_csrf' => null,
'clear_site_data' => [],
],
+ NotFullFledgedRedirectToStartAuthenticationHandler::class,
],
[
'host',
@@ -201,6 +204,7 @@ public function testFirewalls()
],
null,
null,
+ NotFullFledgedRedirectToStartAuthenticationHandler::class,
],
[
'with_user_checker',
@@ -218,6 +222,7 @@ public function testFirewalls()
],
null,
null,
+ NotFullFledgedRedirectToStartAuthenticationHandler::class,
],
], $configs);
diff --git a/src/Symfony/Component/Security/Http/Authorization/NotFullFledgedEqualNormalLoginHandler.php b/src/Symfony/Component/Security/Http/Authorization/NotFullFledgedEqualNormalLoginHandler.php
new file mode 100644
index 0000000000000..19759ed5fbf93
--- /dev/null
+++ b/src/Symfony/Component/Security/Http/Authorization/NotFullFledgedEqualNormalLoginHandler.php
@@ -0,0 +1,64 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\Http\Authorization;
+
+use Psr\Log\LoggerInterface;
+use Symfony\Component\HttpKernel\Event\ExceptionEvent;
+use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter;
+use Symfony\Component\Security\Core\Exception\AccessDeniedException;
+use Symfony\Component\Security\Core\Exception\InsufficientAuthenticationException;
+
+/**
+ * NotFullFledgedHandler for considering NotFullFledged Login equal to Normal Login except if IS_AUTHENTICATED_FULLY is asked
+ * If IS_AUTHENTICATED_FULLY is in access denied Exception Attrribute, user is redirect to
+ * startAuthentication function in AuthenticationTrustResolver
+ * Otherwise The original AccessDeniedException is passing to accessDeniedHandler or ErrorPage or Thrown.
+ *
+ * @author Roman JOLY
+ */
+class NotFullFledgedEqualNormalLoginHandler implements NotFullFledgedHandlerInterface
+{
+ public function handle(ExceptionEvent $event, AccessDeniedException $exception, AuthenticationTrustResolverInterface $trustResolver, ?TokenInterface $token, ?LoggerInterface $logger, callable $startAuthenticationCallback): bool
+ {
+ if (!$trustResolver->isAuthenticated($token)) {
+ $this->reauthenticate($startAuthenticationCallback, $event, $token, $exception, $logger);
+ }
+
+ foreach ($exception->getAttributes() as $attribute) {
+ if (\in_array($attribute, [AuthenticatedVoter::IS_AUTHENTICATED_FULLY])) {
+ $this->reauthenticate($startAuthenticationCallback, $event, $token, $exception, $logger);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private function reauthenticate(callable $startAuthenticationCallback, ExceptionEvent $event, ?TokenInterface $token, AccessDeniedException $exception, ?LoggerInterface $logger): void
+ {
+ $logger?->debug('Access denied, the user is not fully authenticated; redirecting to authentication entry point.', ['exception' => $exception]);
+
+ try {
+ $insufficientAuthenticationException = new InsufficientAuthenticationException('Full authentication is required to access this resource.', 0, $exception);
+ if (null !== $token) {
+ $insufficientAuthenticationException->setToken($token);
+ }
+
+ $event->setResponse($startAuthenticationCallback($event->getRequest(), $insufficientAuthenticationException));
+ } catch (\Exception $e) {
+ $event->setThrowable($e);
+ }
+ }
+}
diff --git a/src/Symfony/Component/Security/Http/Authorization/NotFullFledgedHandlerInterface.php b/src/Symfony/Component/Security/Http/Authorization/NotFullFledgedHandlerInterface.php
new file mode 100644
index 0000000000000..bffaf9c2ab7bb
--- /dev/null
+++ b/src/Symfony/Component/Security/Http/Authorization/NotFullFledgedHandlerInterface.php
@@ -0,0 +1,38 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\Http\Authorization;
+
+use Psr\Log\LoggerInterface;
+use Symfony\Component\HttpKernel\Event\ExceptionEvent;
+use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+use Symfony\Component\Security\Core\Exception\AccessDeniedException;
+
+/**
+ * This is used by the ExceptionListener to translate an AccessDeniedException
+ * to a Response object.
+ *
+ * @author Roman JOLY
+ */
+interface NotFullFledgedHandlerInterface
+{
+ /**
+ * Allow to manage NotFullFledged cases when ExceptionListener catch AccessDeniedException
+ * This function can make checks and event / exception changes to change the Response
+ * It returns a boolean for break or not after that or continue the ExceptionListener process to decorate Exception and their response.
+ *
+ * @param $startAuthenticationCallback callable for call start function from
+ *
+ * @return bool break handleAccessDeniedException function in ExceptionListener after handle
+ */
+ public function handle(ExceptionEvent $event, AccessDeniedException $exception, AuthenticationTrustResolverInterface $trustResolver, ?TokenInterface $token, ?LoggerInterface $logger, callable $startAuthenticationCallback): bool;
+}
diff --git a/src/Symfony/Component/Security/Http/Authorization/NotFullFledgedRedirectToStartAuthenticationHandler.php b/src/Symfony/Component/Security/Http/Authorization/NotFullFledgedRedirectToStartAuthenticationHandler.php
new file mode 100644
index 0000000000000..d959b017d5e65
--- /dev/null
+++ b/src/Symfony/Component/Security/Http/Authorization/NotFullFledgedRedirectToStartAuthenticationHandler.php
@@ -0,0 +1,51 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\Http\Authorization;
+
+use Psr\Log\LoggerInterface;
+use Symfony\Component\HttpKernel\Event\ExceptionEvent;
+use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+use Symfony\Component\Security\Core\Exception\AccessDeniedException;
+use Symfony\Component\Security\Core\Exception\InsufficientAuthenticationException;
+
+/**
+ * NotFullFledgedHandler for considering NotFullFledged Login has to be redirect to login page if AccessDeniedException is thrown
+ * When an AccessDeniedException is thrown and user is not full fledged logged, he is redirected to login page with
+ * start function from authenticationEntryPoint.
+ *
+ * @author Roman JOLY
+ */
+class NotFullFledgedRedirectToStartAuthenticationHandler implements NotFullFledgedHandlerInterface
+{
+ public function handle(ExceptionEvent $event, AccessDeniedException $exception, AuthenticationTrustResolverInterface $trustResolver, ?TokenInterface $token, ?LoggerInterface $logger, callable $startAuthenticationCallback): bool
+ {
+ if (!$trustResolver->isFullFledged($token)) {
+ $logger?->debug('Access denied, the user is not fully authenticated; redirecting to authentication entry point.', ['exception' => $exception]);
+
+ try {
+ $insufficientAuthenticationException = new InsufficientAuthenticationException('Full authentication is required to access this resource.', 0, $exception);
+ if (null !== $token) {
+ $insufficientAuthenticationException->setToken($token);
+ }
+
+ $event->setResponse($startAuthenticationCallback($event->getRequest(), $insufficientAuthenticationException));
+ } catch (\Exception $e) {
+ $event->setThrowable($e);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php
index a85ff958f2049..220b9991e0ccd 100644
--- a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php
@@ -25,10 +25,10 @@
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Exception\AccountStatusException;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
-use Symfony\Component\Security\Core\Exception\InsufficientAuthenticationException;
use Symfony\Component\Security\Core\Exception\LazyResponseException;
use Symfony\Component\Security\Core\Exception\LogoutException;
use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface;
+use Symfony\Component\Security\Http\Authorization\NotFullFledgedHandlerInterface;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Symfony\Component\Security\Http\EntryPoint\Exception\NotAnEntryPointException;
use Symfony\Component\Security\Http\HttpUtils;
@@ -57,6 +57,7 @@ public function __construct(
private ?AccessDeniedHandlerInterface $accessDeniedHandler = null,
private ?LoggerInterface $logger = null,
private bool $stateless = false,
+ private ?NotFullFledgedHandlerInterface $notFullFledgedHandler = null,
) {
}
@@ -124,22 +125,9 @@ private function handleAuthenticationException(ExceptionEvent $event, Authentica
private function handleAccessDeniedException(ExceptionEvent $event, AccessDeniedException $exception): void
{
$event->setThrowable(new AccessDeniedHttpException($exception->getMessage(), $exception));
-
$token = $this->tokenStorage->getToken();
- if (!$this->authenticationTrustResolver->isFullFledged($token)) {
- $this->logger?->debug('Access denied, the user is not fully authenticated; redirecting to authentication entry point.', ['exception' => $exception]);
-
- try {
- $insufficientAuthenticationException = new InsufficientAuthenticationException('Full authentication is required to access this resource.', 0, $exception);
- if (null !== $token) {
- $insufficientAuthenticationException->setToken($token);
- }
-
- $event->setResponse($this->startAuthentication($event->getRequest(), $insufficientAuthenticationException));
- } catch (\Exception $e) {
- $event->setThrowable($e);
- }
+ if ($this->notFullFledgedHandler?->handle($event, $exception, $this->authenticationTrustResolver, $token, $this->logger, function ($request, $exception) {return $this->startAuthentication($request, $exception); })) {
return;
}
diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/ExceptionListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/ExceptionListenerTest.php
index 8bcc958854275..bf0b881224bb0 100644
--- a/src/Symfony/Component/Security/Http/Tests/Firewall/ExceptionListenerTest.php
+++ b/src/Symfony/Component/Security/Http/Tests/Firewall/ExceptionListenerTest.php
@@ -25,6 +25,7 @@
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\LogoutException;
use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface;
+use Symfony\Component\Security\Http\Authorization\NotFullFledgedHandlerInterface;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Symfony\Component\Security\Http\Firewall\ExceptionListener;
use Symfony\Component\Security\Http\HttpUtils;
@@ -142,7 +143,55 @@ public function testAccessDeniedExceptionNotFullFledged(\Exception $exception, ?
$listener = $this->createExceptionListener($tokenStorage, $this->createTrustResolver(false), null, $this->createEntryPoint());
$listener->onKernelException($event);
- $this->assertEquals('OK', $event->getResponse()->getContent());
+ $this->assertSame($eventException ?? $exception, $event->getThrowable()->getPrevious());
+ }
+
+ /**
+ * @dataProvider getAccessDeniedExceptionProvider
+ */
+ public function testAccessDeniedExceptionNotFullFledgedWithHandlerResponseCustomNotAuthenticated(\Exception $exception, ?\Exception $eventException = null)
+ {
+ $event = $this->createEvent($exception);
+
+ $tokenStorage = $this->createMock(TokenStorageInterface::class);
+ $tokenStorage->expects($this->once())->method('getToken')->willReturn($this->createMock(TokenInterface::class));
+
+ $listener = $this->createExceptionListener($tokenStorage, $this->createTrustResolver(false, false), null, $this->createEntryPointWithoutStartCalled(), null, null, $this->createNotFullFledgedHandler(true));
+ $listener->onKernelException($event);
+
+ $this->assertSame($eventException ?? $exception, $event->getThrowable()->getPrevious());
+ }
+
+ /**
+ * @dataProvider getAccessDeniedExceptionProvider
+ */
+ public function testAccessDeniedExceptionNotFullFledgedWithoutHandlerResponseAuthenticated(\Exception $exception, ?\Exception $eventException = null)
+ {
+ $event = $this->createEvent($exception);
+
+ $tokenStorage = $this->createMock(TokenStorageInterface::class);
+ $tokenStorage->expects($this->once())->method('getToken')->willReturn($this->createMock(TokenInterface::class));
+
+ $listener = $this->createExceptionListener($tokenStorage, $this->createTrustResolver(false, true), null, $this->createEntryPointWithoutStartCalled(), null, null, $this->createNotFullFledgedHandler(false));
+ $listener->onKernelException($event);
+
+ $this->assertNull($event->getResponse());
+ $this->assertEquals($eventException?->getMessage() ?? $exception->getMessage(), $event->getThrowable()->getMessage());
+ }
+
+ /**
+ * @dataProvider getAccessDeniedExceptionProvider
+ */
+ public function testAccessDeniedExceptionNotFullFledgedWithHandlerResponseCustomAuthenticated(\Exception $exception, ?\Exception $eventException = null)
+ {
+ $event = $this->createEvent($exception);
+
+ $tokenStorage = $this->createMock(TokenStorageInterface::class);
+ $tokenStorage->expects($this->once())->method('getToken')->willReturn($this->createMock(TokenInterface::class));
+
+ $listener = $this->createExceptionListener($tokenStorage, $this->createTrustResolver(false, true), null, $this->createEntryPointWithoutStartCalled(), null, null, $this->createNotFullFledgedHandler(true));
+ $listener->onKernelException($event);
+
$this->assertSame($eventException ?? $exception, $event->getThrowable()->getPrevious());
}
@@ -183,15 +232,24 @@ public static function getAccessDeniedExceptionProvider()
private function createEntryPoint(?Response $response = null)
{
$entryPoint = $this->createMock(AuthenticationEntryPointInterface::class);
- $entryPoint->expects($this->once())->method('start')->willReturn($response ?? new Response('OK'));
+ $entryPoint->method('start')->willReturn($response ?? new Response('OK'));
return $entryPoint;
}
- private function createTrustResolver($fullFledged)
+ private function createEntryPointWithoutStartCalled()
+ {
+ $entryPoint = $this->createMock(AuthenticationEntryPointInterface::class);
+ $entryPoint->expects($this->never())->method('start');
+
+ return $entryPoint;
+ }
+
+ private function createTrustResolver($fullFledged, $authenticate = false)
{
$trustResolver = $this->createMock(AuthenticationTrustResolverInterface::class);
- $trustResolver->expects($this->once())->method('isFullFledged')->willReturn($fullFledged);
+ $trustResolver->method('isFullFledged')->willReturn($fullFledged);
+ $trustResolver->method('isAuthenticated')->willReturn($authenticate);
return $trustResolver;
}
@@ -203,7 +261,7 @@ private function createEvent(\Exception $exception, $kernel = null)
return new ExceptionEvent($kernel, Request::create('/'), HttpKernelInterface::MAIN_REQUEST, $exception);
}
- private function createExceptionListener(?TokenStorageInterface $tokenStorage = null, ?AuthenticationTrustResolverInterface $trustResolver = null, ?HttpUtils $httpUtils = null, ?AuthenticationEntryPointInterface $authenticationEntryPoint = null, $errorPage = null, ?AccessDeniedHandlerInterface $accessDeniedHandler = null)
+ private function createExceptionListener(?TokenStorageInterface $tokenStorage = null, ?AuthenticationTrustResolverInterface $trustResolver = null, ?HttpUtils $httpUtils = null, ?AuthenticationEntryPointInterface $authenticationEntryPoint = null, $errorPage = null, ?AccessDeniedHandlerInterface $accessDeniedHandler = null, ?NotFullFledgedHandlerInterface $notFullFledgedHandler = null)
{
return new ExceptionListener(
$tokenStorage ?? $this->createMock(TokenStorageInterface::class),
@@ -212,7 +270,18 @@ private function createExceptionListener(?TokenStorageInterface $tokenStorage =
'key',
$authenticationEntryPoint,
$errorPage,
- $accessDeniedHandler
+ $accessDeniedHandler,
+ null,
+ false,
+ $notFullFledgedHandler,
);
}
+
+ private function createNotFullFledgedHandler(bool $break = false)
+ {
+ $handler = $this->createMock(NotFullFledgedHandlerInterface::class);
+ $handler->method('handle')->willReturn($break);
+
+ return $handler;
+ }
}