8000 Show user account status errors · symfony/symfony@26852b9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 26852b9

Browse files
committed
Show user account status errors
1 parent 095d286 commit 26852b9

File tree

10 files changed

+71
-10
lines changed

10 files changed

+71
-10
lines changed

src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ public function getConfigTreeBuilder()
9191
->defaultValue(SessionAuthenticationStrategy::MIGRATE)
9292
->end()
9393
->booleanNode('hide_user_not_found')->defaultTrue()->end()
94+
->booleanNode('show_account_status_exceptions')->defaultFalse()->end()
9495
->booleanNode('always_authenticate_before_granting')
9596
->defaultFalse()
9697
->setDeprecated('symfony/security-bundle', 8000 '5.4')

src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ public function load(array $configs, ContainerBuilder $container)
170170

171171
$container->setParameter('security.access.always_authenticate_before_granting', $config['always_authenticate_before_granting']);
172172
$container->setParameter('security.authentication.hide_user_not_found', $config['hide_user_not_found']);
173+
$container->setParameter('security.authentication.show_account_status_exceptions', $config['show_account_status_exceptions']);
173174

174175
if (class_exists(Application::class)) {
175176
$loader->load('debug_console.php');

src/Symfony/Bundle/SecurityBundle/Resources/config/guard.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
service('logger')->nullOnInvalid(),
5151
param('security.authentication.hide_user_not_found'),
5252
service('security.token_storage'),
53+
param('security.authentication.show_account_status_exceptions'),
5354
])
5455
->tag('monolog.logger', ['channel' => 'security'])
5556
->deprecate('symfony/security-bundle', '5.3', 'The "%service_id%" service is deprecated, use the new authenticator system instead.')

src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
param('security.authentication.manager.erase_credentials'),
4747
param('security.authentication.hide_user_not_found'),
4848
abstract_arg('required badges'),
49+
param('security.authentication.show_account_status_exceptions'),
4950
])
5051
->tag('monolog.logger', ['channel' => 'security'])
5152

src/Symfony/Bundle/SecurityBundle/Resources/config/security_legacy.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@
122122
abstract_arg('Provider-shared Key'),
123123
service('security.password_hasher_factory'),
124124
param('security.authentication.hide_user_not_found'),
125+
param('security.authentication.show_account_status_exceptions'),
125126
])
126127
->deprecate('symfony/security-bundle', '5.3', 'The "%service_id%" service is deprecated, use the new authenticator system instead.')
127128

@@ -136,6 +137,7 @@
136137
param('security.authentication.hide_user_not_found'),
137138
abstract_arg('search dn'),
138139
abstract_arg('search password'),
140+
param('security.authentication.show_account_status_exceptions'),
139141
])
140142
->deprecate('symfony/security-bundle', '5.3', 'The "%service_id%" service is deprecated, use the new authenticator system instead.')
141143

src/Symfony/Component/Security/Core/Authentication/Provider/DaoAuthenticationProvider.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ class DaoAuthenticationProvider extends UserAuthenticationProvider
4242
/**
4343
* @param PasswordHasherFactoryInterface $hasherFactory
4444
*/
45-
public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, string $providerKey, $hasherFactory, bool $hideUserNotFoundExceptions = true)
45+
public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, string $providerKey, $hasherFactory, bool $hideUserNotFoundExceptions = true, bool $showAccountStatusExceptions = false)
4646
{
47-
parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions);
47+
parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions, $showAccountStatusExceptions);
4848

4949
if ($hasherFactory instanceof EncoderFactoryInterface) {
5050
trigger_deprecation('symfony/security-core', '5.3', 'Passing a "%s" instance to the "%s" constructor is deprecated, use "%s" instead.', EncoderFactoryInterface::class, __CLASS__, PasswordHasherFactoryInterface::class);

src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ class LdapBindAuthenticationProvider extends UserAuthenticationProvider
4242
private $searchDn;
4343
private $searchPassword;
4444

45-
public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, string $providerKey, LdapInterface $ldap, string $dnString = '{user_identifier}', bool $hideUserNotFoundExceptions = true, string $searchDn = '', string $searchPassword = '')
45+
public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, string $providerKey, LdapInterface $ldap, string $dnString = '{user_identifier}', bool $hideUserNotFoundExceptions = true, string $searchDn = '', string $searchPassword = '', bool $showAccountStatusExceptions = false)
4646
{
47-
parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions);
47+
parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions, $showAccountStatusExceptions);
4848

4949
$this->userProvider = $userProvider;
5050
$this->ldap = $ldap;

src/Symfony/Component/Security/Core/Authentication/Provider/UserAuthenticationProvider.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Security\Core\Authentication\Provider;
1313

14+
use Exception;
1415
use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
1516
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
1617
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
@@ -37,11 +38,12 @@ abstract class UserAuthenticationProvider implements AuthenticationProviderInter
3738
private $hideUserNotFoundExceptions;
3839
private $userChecker;
3940
private $providerKey;
41+
private $showAccountStatusExceptions;
4042

4143
/**
4244
* @throws \InvalidArgumentException
4345
*/
44-
public function __construct(UserCheckerInterface $userChecker, string $providerKey, bool $hideUserNotFoundExceptions = true)
46+
public function __construct(UserCheckerInterface $userChecker, string $providerKey, bool $hideUserNotFoundExceptions = true, bool $showAccountStatusExceptions = false)
4547
{
4648
if (empty($providerKey)) {
4749
throw new \InvalidArgumentException('$providerKey must not be empty.');
@@ -50,6 +52,7 @@ public function __construct(UserCheckerInterface $userChecker, string $providerK
5052
$this->userChecker = $userChecker;
5153
$this->providerKey = $providerKey;
5254
$this->hideUserNotFoundExceptions = $hideUserNotFoundExceptions;
55+
$this->showAccountStatusExceptions = $showAccountStatusExceptions;
5356
}
5457

5558
/**
@@ -86,7 +89,7 @@ public function authenticate(TokenInterface $token)
8689
$this->checkAuthentication($user, $token);
8790
$this->userChecker->checkPostAuth($user);
8891
} catch (AccountStatusException|BadCredentialsException $e) {
89-
if ($this->hideUserNotFoundExceptions && !$e instanceof CustomUserMessageAccountStatusException) {
92+
if ($this->isFilteredException($e)) {
9093
throw new BadCredentialsException('Bad credentials.', 0, $e);
9194
}
9295

@@ -131,4 +134,17 @@ abstract protected function retrieveUser(string $username, UsernamePasswordToken
131134
* @throws AuthenticationException if the credentials could not be validated
132135
*/
133136
abstract protected function checkAuthentication(UserInterface $user, UsernamePasswordToken $token);
137+
138+
private function isFilteredException(Exception $exception): bool
139+
{
140+
if (!$this->hideUserNotFoundExceptions) {
141+
return false;
142+
}
143+
144+
if ($this->showAccountStatusExceptions) {
145+
return false;
146+
}
147+
148+
return !$exception instanceof CustomUserMessageAccountStatusException;
149+
}
134150
}

src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Security\Guard\Firewall;
1313

14+
use Exception;
1415
use Psr\Log\LoggerInterface;
1516
use Symfony\Component\HttpFoundation\Request;
1617
use Symfony\Component\HttpFoundation\Response;
@@ -51,12 +52,13 @@ class GuardAuthenticationListener extends AbstractListener
5152
private $rememberMeServices;
5253
private $hideUserNotFoundExceptions;
5354
private $tokenStorage;
55+
private $showAccountStatusExceptions;
5456

5557
/**
5658
* @param string $providerKey The provider (i.e. firewall) key
5759
* @param iterable<array-key, AuthenticatorInterface> $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationProvider
5860
*/
59-
public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, string $providerKey, iterable $guardAuthenticators, ?LoggerInterface $logger = null, bool $hideUserNotFoundExceptions = true, ?TokenStorageInterface $tokenStorage = null)
61+
public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, string $providerKey, iterable $guardAuthenticators, ?LoggerInterface $logger = null, bool $hideUserNotFoundExceptions = true, ?TokenStorageInterface $tokenStorage = null, bool $showAccountStatusExceptions = false)
6062
{
6163
if (empty($providerKey)) {
6264
throw new \InvalidArgumentException('$providerKey must not be empty.');
@@ -69,6 +71,7 @@ public function __construct(GuardAuthenticatorHandler $guardHandler, Authenticat
6971
$this->logger = $logger;
7072
$this->hideUserNotFoundExceptions = $hideUserNotFoundExceptions;
7173
$this->tokenStorage = $tokenStorage;
74+
$this->showAccountStatusExceptions = $showAccountStatusExceptions;
7275
}
7376

7477
/**
@@ -180,7 +183,7 @@ private function executeGuardAuthenticator(string $uniqueGuardKey, Authenticator
180183

181184
// Avoid leaking error details in case of invalid user (e.g. user not found or invalid account status)
182185
// to prevent user enumeration via response content
183-
if ($this->hideUserNotFoundExceptions && ($e instanceof UsernameNotFoundException || ($e instanceof AccountStatusException && !$e instanceof CustomUserMessageAccountStatusException))) {
186+
if ($this->isFilteredException($e)) {
184187
$e = new BadCredentialsException('Bad credentials.', 0, $e);
185188
}
186189

@@ -247,4 +250,21 @@ private function triggerRememberMe(AuthenticatorInterface $guardAuthenticator, R
247250

248251
$this->rememberMeServices->loginSuccess($request, $response, $token);
249252
}
253+
254+
private function isFilteredException(Exception $exception): bool
255+
{
256+
if (!$this->hideUserNotFoundExceptions) {
257+
return false;
258+
}
259+
260+
if ($exception instanceof UsernameNotFoundException) {
261+
return true;
262+
}
263+
264+
if ($this->showAccountStatusExceptions) {
265+
return false;
266+
}
267+
268+
return $exception instanceof AccountStatusException && !$exception instanceof CustomUserMessageAccountStatusException;
269+
}
250270
}

src/Symfony/Component/Security/Http/Authentication/AuthenticatorManager.php

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,12 @@ class AuthenticatorManager implements AuthenticatorManagerInterface, UserAuthent
5454
private $firewallName;
5555
private $hideUserNotFoundExceptions;
5656
private $requiredBadges;
57+
private $showAccountStatusExceptions;
5758

5859
/**
5960
* @param iterable<mixed, AuthenticatorInterface> $authenticators
6061
*/
61-
public function __construct(iterable $authenticators, TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher, string $firewallName, ?LoggerInterface $logger = null, bool $eraseCredentials = true, bool $hideUserNotFoundExceptions = true, array $requiredBadges = [])
62+
public function __construct(iterable $authenticators, TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher, string $firewallName, ?LoggerInterface $logger = null, bool $eraseCredentials = true, bool $hideUserNotFoundExceptions = true, array $requiredBadges = [], bool $showAccountStatusExceptions = false)
6263
{
6364
$this->authenticators = $authenticators;
6465
$this->tokenStorage = $tokenStorage;
@@ -68,6 +69,7 @@ public function __construct(iterable $authenticators, TokenStorageInterface $tok
6869
$this->eraseCredentials = $eraseCredentials;
6970
$this->hideUserNotFoundExceptions = $hideUserNotFoundExceptions;
7071
$this->requiredBadges = $requiredBadges;
72+
$this->showAccountStatusExceptions = $showAccountStatusExceptions;
7173
}
7274

7375
/**
@@ -269,7 +271,7 @@ private function handleAuthenticationFailure(AuthenticationException $authentica
269271

270272
// Avoid leaking error details in case of invalid user (e.g. user not found or invalid account status)
271273
// to prevent user enumeration via response content comparison
272-
if ($this->hideUserNotFoundExceptions && ($authenticationException instanceof UserNotFoundException || ($authenticationException instanceof AccountStatusException && !$authenticationException instanceof CustomUserMessageAccountStatusException))) {
274+
if ($this->isFilteredException($authenticationException)) {
273275
$authenticationException = new BadCredentialsException('Bad credentials.', 0, $authenticationException);
274276
}
275277

@@ -283,4 +285,21 @@ private function handleAuthenticationFailure(AuthenticationException $authentica
283285
// returning null is ok, it means they want the request to continue
284286
return $loginFailureEvent->getResponse();
285287
}
288+
289+
private function isFilteredException(AuthenticationException $exception): bool
290+
{
291+
if (!$this->hideUserNotFoundExceptions) {
292+
return false;
293+
}
294+
295+
if ($exception instanceof UserNotFoundException) {
296+
return true;
297+
}
298+
299+
if ($this->showAccountStatusExceptions) {
300+
return false;
301+
}
302+
303+
return $exception instanceof AccountStatusException && !$exception instanceof CustomUserMessageAccountStatusException;
304+
}
286305
}

0 commit comments

Comments
 (0)
0