From 7684663818673345eb4aee16fb4fd7eebbb5bc91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20Schlu=CC=88ter?= Date: Wed, 2 Sep 2020 14:34:37 +0200 Subject: [PATCH] Translate failure messages of json authentication --- .../config/security_authenticator.php | 1 + .../Resources/config/security_listeners.php | 1 + src/Symfony/Component/Security/CHANGELOG.md | 3 ++- .../Authenticator/JsonLoginAuthenticator.php | 19 ++++++++++++++++++- ...namePasswordJsonAuthenticationListener.php | 19 ++++++++++++++++++- 5 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator.php b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator.php index 4a7453136b2e6..158da4babb74e 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator.php +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator.php @@ -143,6 +143,7 @@ abstract_arg('options'), service('property_accessor')->nullOnInvalid(), ]) + ->call('setTranslator', [service('translator')->ignoreOnInvalid()]) ->set('security.authenticator.remember_me', RememberMeAuthenticator::class) ->abstract() diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.php b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.php index de0b583dd4d99..7683ea2484031 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.php +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.php @@ -189,6 +189,7 @@ service('event_dispatcher')->nullOnInvalid(), service('property_accessor')->nullOnInvalid(), ]) + ->call('setTranslator', [service('translator')->ignoreOnInvalid()]) ->tag('monolog.logger', ['channel' => 'security']) ->set('security.authentication.listener.remote_user', RemoteUserAuthenticationListener::class) diff --git a/src/Symfony/Component/Security/CHANGELOG.md b/src/Symfony/Component/Security/CHANGELOG.md index 6a3cb2774fe87..0257630f8b1c5 100644 --- a/src/Symfony/Component/Security/CHANGELOG.md +++ b/src/Symfony/Component/Security/CHANGELOG.md @@ -8,8 +8,9 @@ CHANGELOG * Changed `AuthorizationChecker` to call the access decision manager in unauthenticated sessions with a `NullToken` * [BC break] Removed `AccessListener::PUBLIC_ACCESS` in favor of `AuthenticatedVoter::PUBLIC_ACCESS` * Added `Passport` to `LoginFailureEvent`. - * Deprecated `setProviderKey()`/`getProviderKey()` in favor of `setFirewallName()/getFirewallName()` in `PreAuthenticatedToken`, `RememberMeToken`, `SwitchUserToken`, `UsernamePasswordToken`, `DefaultAuthenticationSuccessHandler`; and deprecated the `AbstractRememberMeServices::$providerKey` property in favor of `AbstractRememberMeServices::$firewallName` + * Deprecated `setProviderKey()`/`getProviderKey()` in favor of `setFirewallName()/getFirewallName()` in `PreAuthenticatedToken`, `RememberMeToken`, `SwitchUserToken`, `UsernamePasswordToken`, `DefaultAuthenticationSuccessHandler`; and deprecated the `AbstractRememberMeServices::$providerKey` property in favor of `AbstractRememberMeServices::$firewallName` * Added `FirewallListenerInterface` to make the execution order of firewall listeners configurable + * Added translator to `\Symfony\Component\Security\Http\Authenticator\JsonLoginAuthenticator` and `\Symfony\Component\Security\Http\Firewall\UsernamePasswordJsonAuthenticationListener` to translate authentication failure messages 5.1.0 ----- diff --git a/src/Symfony/Component/Security/Http/Authenticator/JsonLoginAuthenticator.php b/src/Symfony/Component/Security/Http/Authenticator/JsonLoginAuthenticator.php index 282cd48c12281..f8524695bacab 100644 --- a/src/Symfony/Component/Security/Http/Authenticator/JsonLoginAuthenticator.php +++ b/src/Symfony/Component/Security/Http/Authenticator/JsonLoginAuthenticator.php @@ -35,6 +35,7 @@ use Symfony\Component\Security\Http\Authenticator\Passport\Passport; use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface; use Symfony\Component\Security\Http\HttpUtils; +use Symfony\Contracts\Translation\TranslatorInterface; /** * Provides a stateless implementation of an authentication via @@ -55,6 +56,11 @@ class JsonLoginAuthenticator implements InteractiveAuthenticatorInterface private $successHandler; private $failureHandler; + /** + * @var TranslatorInterface|null + */ + private $translator; + public function __construct(HttpUtils $httpUtils, UserProviderInterface $userProvider, ?AuthenticationSuccessHandlerInterface $successHandler = null, ?AuthenticationFailureHandlerInterface $failureHandler = null, array $options = [], ?PropertyAccessorInterface $propertyAccessor = null) { $this->options = array_merge(['username_path' => 'username', 'password_path' => 'password'], $options); @@ -120,7 +126,13 @@ public function onAuthenticationSuccess(Request $request, TokenInterface $token, public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response { if (null === $this->failureHandler) { - return new JsonResponse(['error' => $exception->getMessageKey()], JsonResponse::HTTP_UNAUTHORIZED); + $errorMessage = $exception->getMessageKey(); + + if (null !== $this->translator) { + $errorMessage = $this->translator->trans($exception->getMessageKey(), $exception->getMessageData(), 'security'); + } + + return new JsonResponse(['error' => $errorMessage], JsonResponse::HTTP_UNAUTHORIZED); } return $this->failureHandler->onAuthenticationFailure($request, $exception); @@ -131,6 +143,11 @@ public function isInteractive(): bool return true; } + public function setTranslator(TranslatorInterface $translator) + { + $this->translator = $translator; + } + private function getCredentials(Request $request) { $data = json_decode($request->getContent()); diff --git a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordJsonAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordJsonAuthenticationListener.php index ef9a76abd31b6..4363ee93a6ac7 100644 --- a/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordJsonAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordJsonAuthenticationListener.php @@ -34,6 +34,7 @@ use Symfony\Component\Security\Http\SecurityEvents; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * UsernamePasswordJsonAuthenticationListener is a stateless implementation of @@ -57,6 +58,11 @@ class UsernamePasswordJsonAuthenticationListener extends AbstractListener private $propertyAccessor; private $sessionStrategy; + /** + * @var TranslatorInterface|null + */ + private $translator; + public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, HttpUtils $httpUtils, string $providerKey, AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, array $options = [], LoggerInterface $logger = null, EventDispatcherInterface $eventDispatcher = null, PropertyAccessorInterface $propertyAccessor = null) { $this->tokenStorage = $tokenStorage; @@ -182,7 +188,13 @@ private function onFailure(Request $request, AuthenticationException $failed): R } if (!$this->failureHandler) { - return new JsonResponse(['error' => $failed->getMessageKey()], 401); + $errorMessage = $failed->getMessageKey(); + + if (null !== $this->translator) { + $errorMessage = $this->translator->trans($failed->getMessageKey(), $failed->getMessageData(), 'security'); + } + + return new JsonResponse(['error' => $errorMessage], 401); } $response = $this->failureHandler->onAuthenticationFailure($request, $failed); @@ -204,6 +216,11 @@ public function setSessionAuthenticationStrategy(SessionAuthenticationStrategyIn $this->sessionStrategy = $sessionStrategy; } + public function setTranslator(TranslatorInterface $translator) + { + $this->translator = $translator; + } + private function migrateSession(Request $request, TokenInterface $token) { if (!$this->sessionStrategy || !$request->hasSession() || !$request->hasPreviousSession()) {