changes rebased
This commit is contained in:
parent
252f85c2c2
commit
cc7409502a
@ -5,6 +5,7 @@ CHANGELOG
|
|||||||
---
|
---
|
||||||
|
|
||||||
* [BC break] Add `login_throttling.lock_factory` setting defaulting to `null` (instead of `lock.factory`)
|
* [BC break] Add `login_throttling.lock_factory` setting defaulting to `null` (instead of `lock.factory`)
|
||||||
|
* Add a `login_throttling.interval` (in `security.firewalls`) option to change the default throttling interval.
|
||||||
* Add the `debug:firewall` command.
|
* Add the `debug:firewall` command.
|
||||||
* Deprecate `UserPasswordEncoderCommand` class and the corresponding `user:encode-password` command,
|
* Deprecate `UserPasswordEncoderCommand` class and the corresponding `user:encode-password` command,
|
||||||
use `UserPasswordHashCommand` and `user:hash-password` instead
|
use `UserPasswordHashCommand` and `user:hash-password` instead
|
||||||
|
@ -54,6 +54,7 @@ class LoginThrottlingFactory implements AuthenticatorFactoryInterface, SecurityF
|
|||||||
->children()
|
->children()
|
||||||
->scalarNode('limiter')->info(sprintf('A service id implementing "%s".', RequestRateLimiterInterface::class))->end()
|
->scalarNode('limiter')->info(sprintf('A service id implementing "%s".', RequestRateLimiterInterface::class))->end()
|
||||||
->integerNode('max_attempts')->defaultValue(5)->end()
|
->integerNode('max_attempts')->defaultValue(5)->end()
|
||||||
|
->scalarNode('interval')->defaultValue('1 minute')->end()
|
||||||
->scalarNode('lock_factory')->info('The service ID of the lock factory used by the login rate limiter (or null to disable locking)')->defaultNull()->end()
|
->scalarNode('lock_factory')->info('The service ID of the lock factory used by the login rate limiter (or null to disable locking)')->defaultNull()->end()
|
||||||
->end();
|
->end();
|
||||||
}
|
}
|
||||||
@ -76,7 +77,7 @@ class LoginThrottlingFactory implements AuthenticatorFactoryInterface, SecurityF
|
|||||||
$limiterOptions = [
|
$limiterOptions = [
|
||||||
'policy' => 'fixed_window',
|
'policy' => 'fixed_window',
|
||||||
'limit' => $config['max_attempts'],
|
'limit' => $config['max_attempts'],
|
||||||
'interval' => '1 minute',
|
'interval' => $config['interval'],
|
||||||
'lock_factory' => $config['lock_factory'],
|
'lock_factory' => $config['lock_factory'],
|
||||||
];
|
];
|
||||||
FrameworkExtension::registerRateLimiter($container, $localId = '_login_local_'.$firewallName, $limiterOptions);
|
FrameworkExtension::registerRateLimiter($container, $localId = '_login_local_'.$firewallName, $limiterOptions);
|
||||||
|
@ -112,7 +112,7 @@ class FormLoginTest extends AbstractWebTestCase
|
|||||||
* @dataProvider provideInvalidCredentials
|
* @dataProvider provideInvalidCredentials
|
||||||
* @group time-sensitive
|
* @group time-sensitive
|
||||||
*/
|
*/
|
||||||
public function testLoginThrottling($username, $password)
|
public function testLoginThrottling(string $username, string $password, int $attemptIndex)
|
||||||
{
|
{
|
||||||
if (!class_exists(LoginThrottlingListener::class)) {
|
if (!class_exists(LoginThrottlingListener::class)) {
|
||||||
$this->markTestSkipped('Login throttling requires symfony/security-http:^5.2');
|
$this->markTestSkipped('Login throttling requires symfony/security-http:^5.2');
|
||||||
@ -125,19 +125,28 @@ class FormLoginTest extends AbstractWebTestCase
|
|||||||
$form['_password'] = $password;
|
$form['_password'] = $password;
|
||||||
$client->submit($form);
|
$client->submit($form);
|
||||||
|
|
||||||
$client->followRedirect()->selectButton('login')->form();
|
|
||||||
$form['_username'] = $username;
|
|
||||||
$form['_password'] = $password;
|
|
||||||
$client->submit($form);
|
|
||||||
|
|
||||||
$text = $client->followRedirect()->text(null, true);
|
$text = $client->followRedirect()->text(null, true);
|
||||||
$this->assertStringMatchesFormat('%sToo many failed login attempts, please try again in %d minute%s', $text);
|
if (1 === $attemptIndex) {
|
||||||
|
// First attempt : Invalid credentials (OK)
|
||||||
|
$this->assertStringMatchesFormat('%sInvalid credentials%s', $text);
|
||||||
|
} elseif (2 === $attemptIndex) {
|
||||||
|
// Second attempt : login throttling !
|
||||||
|
$this->assertStringMatchesFormat('%sToo many failed login attempts, please try again in 8 minutes%s', $text);
|
||||||
|
} elseif (3 === $attemptIndex) {
|
||||||
|
// Third attempt with unexisting username
|
||||||
|
$this->assertStringMatchesFormat('%sUsername could not be found.%s', $text);
|
||||||
|
} elseif (4 === $attemptIndex) {
|
||||||
|
// Fourth attempt : still login throttling !
|
||||||
|
$this->assertStringMatchesFormat('%sToo many failed login attempts, please try again in 8 minutes%s', $text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideInvalidCredentials()
|
public function provideInvalidCredentials()
|
||||||
{
|
{
|
||||||
yield 'invalid_password' => ['johannes', 'wrong'];
|
yield 'invalid_password' => ['johannes', 'wrong', 1];
|
||||||
yield 'invalid_username' => ['wrong', 'wrong'];
|
yield 'invalid_password_again' => ['johannes', 'also_wrong', 2];
|
||||||
|
yield 'invalid_username' => ['wrong', 'wrong', 3];
|
||||||
|
yield 'invalid_password_again_bis' => ['johannes', 'wrong_again', 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideClientOptions()
|
public function provideClientOptions()
|
||||||
|
Reference in New Issue
Block a user