From 0b0c15c019929ecf51d2444e69c7dc624d162f8c Mon Sep 17 00:00:00 2001 From: Anthony Massard Date: Tue, 3 Aug 2021 15:43:15 +0200 Subject: [PATCH 1/2] [Ldap] Make LdapAuthenticator an EntryPoint --- .../Ldap/Security/LdapAuthenticator.php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Ldap/Security/LdapAuthenticator.php b/src/Symfony/Component/Ldap/Security/LdapAuthenticator.php index 30928f62278cd..e3f2402cbe7d6 100644 --- a/src/Symfony/Component/Ldap/Security/LdapAuthenticator.php +++ b/src/Symfony/Component/Ldap/Security/LdapAuthenticator.php @@ -15,8 +15,11 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Core\Exception\LogicException; use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; +use Symfony\Component\Security\Http\Authenticator\InteractiveAuthenticatorInterface; use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface; +use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; /** * This class decorates internal authenticators to add the LDAP integration. @@ -29,7 +32,7 @@ * * @final */ -class LdapAuthenticator implements AuthenticatorInterface +class LdapAuthenticator implements AuthenticationEntryPointInterface, InteractiveAuthenticatorInterface { private $authenticator; private $ldapServiceId; @@ -75,4 +78,18 @@ public function onAuthenticationFailure(Request $request, AuthenticationExceptio { return $this->authenticator->onAuthenticationFailure($request, $exception); } + + public function start(Request $request, AuthenticationException $authException = null): Response + { + if (!$this->authenticator instanceof AuthenticationEntryPointInterface) { + throw new LogicException(sprintf('Decorated authenticator "%s" must implement interface "%s".', get_debug_type($this->authenticator), AuthenticationEntryPointInterface::class)); + } + + return $this->authenticator->start($request, $authException); + } + + public function isInteractive(): bool + { + return true; + } } From 4daad9e784233a429d10170402614c44e1f52c23 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Thu, 16 Sep 2021 15:20:10 +0200 Subject: [PATCH 2/2] Fix decorating non-entrypoint authenticators --- .../Ldap/Security/LdapAuthenticator.php | 10 ++++++--- .../Exception/NotAnEntryPointException.php | 22 +++++++++++++++++++ .../Http/Firewall/ExceptionListener.php | 22 ++++++++++++++----- 3 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 src/Symfony/Component/Security/Http/EntryPoint/Exception/NotAnEntryPointException.php diff --git a/src/Symfony/Component/Ldap/Security/LdapAuthenticator.php b/src/Symfony/Component/Ldap/Security/LdapAuthenticator.php index e3f2402cbe7d6..2bc3f5925b389 100644 --- a/src/Symfony/Component/Ldap/Security/LdapAuthenticator.php +++ b/src/Symfony/Component/Ldap/Security/LdapAuthenticator.php @@ -15,11 +15,11 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\Exception\LogicException; use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; use Symfony\Component\Security\Http\Authenticator\InteractiveAuthenticatorInterface; use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; +use Symfony\Component\Security\Http\EntryPoint\Exception\NotAnEntryPointException; /** * This class decorates internal authenticators to add the LDAP integration. @@ -82,7 +82,7 @@ public function onAuthenticationFailure(Request $request, AuthenticationExceptio public function start(Request $request, AuthenticationException $authException = null): Response { if (!$this->authenticator instanceof AuthenticationEntryPointInterface) { - throw new LogicException(sprintf('Decorated authenticator "%s" must implement interface "%s".', get_debug_type($this->authenticator), AuthenticationEntryPointInterface::class)); + throw new NotAnEntryPointException(sprintf('Decorated authenticator "%s" does not implement interface "%s".', get_debug_type($this->authenticator), AuthenticationEntryPointInterface::class)); } return $this->authenticator->start($request, $authException); @@ -90,6 +90,10 @@ public function start(Request $request, AuthenticationException $authException = public function isInteractive(): bool { - return true; + if ($this->authenticator instanceof InteractiveAuthenticatorInterface) { + return $this->authenticator->isInteractive(); + } + + return false; } } diff --git a/src/Symfony/Component/Security/Http/EntryPoint/Exception/NotAnEntryPointException.php b/src/Symfony/Component/Security/Http/EntryPoint/Exception/NotAnEntryPointException.php new file mode 100644 index 0000000000000..e421dcf0cd67b --- /dev/null +++ b/src/Symfony/Component/Security/Http/EntryPoint/Exception/NotAnEntryPointException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Http\EntryPoint\Exception; + +/** + * Thrown by generic decorators when a decorated authenticator does not implement + * {@see AuthenticationEntryPointInterface}. + * + * @author Robin Chalas + */ +class NotAnEntryPointException extends \RuntimeException +{ +} diff --git a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php index 73f4f4553d8c0..32a1b60d60452 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php @@ -31,6 +31,7 @@ use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; +use Symfony\Component\Security\Http\EntryPoint\Exception\NotAnEntryPointException; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Http\Util\TargetPathTrait; @@ -195,11 +196,7 @@ private function handleLogoutException(ExceptionEvent $event, LogoutException $e private function startAuthentication(Request $request, AuthenticationException $authException): Response { if (null === $this->authenticationEntryPoint) { - if (null !== $this->logger) { - $this->logger->notice(sprintf('No Authentication entry point configured, returning a %s HTTP response. Configure "entry_point" on the firewall "%s" if you want to modify the response.', Response::HTTP_UNAUTHORIZED, $this->firewallName)); - } - - throw new HttpException(Response::HTTP_UNAUTHORIZED, $authException->getMessage(), $authException, [], $authException->getCode()); + $this->throwUnauthorizedException($authException); } if (null !== $this->logger) { @@ -219,7 +216,11 @@ private function startAuthentication(Request $request, AuthenticationException $ } } - $response = $this->authenticationEntryPoint->start($request, $authException); + try { + $response = $this->authenticationEntryPoint->start($request, $authException); + } catch (NotAnEntryPointException $e) { + $this->throwUnauthorizedException($authException); + } if (!$response instanceof Response) { $given = get_debug_type($response); @@ -237,4 +238,13 @@ protected function setTargetPath(Request $request) $this->saveTargetPath($request->getSession(), $this->firewallName, $request->getUri()); } } + + private function throwUnauthorizedException(AuthenticationException $authException) + { + if (null !== $this->logger) { + $this->logger->notice(sprintf('No Authentication entry point configured, returning a %s HTTP response. Configure "entry_point" on the firewall "%s" if you want to modify the response.', Response::HTTP_UNAUTHORIZED, $this->firewallName)); + } + + throw new HttpException(Response::HTTP_UNAUTHORIZED, $authException->getMessage(), $authException, [], $authException->getCode()); + } }