diff --git a/core/modules/user/src/Entity/User.php b/core/modules/user/src/Entity/User.php index 75a35a9ac305e4806d49d6f330b5bdc404e8c153..2af4c9b846d0344528ca667366f7d30a04344b64 100644 --- a/core/modules/user/src/Entity/User.php +++ b/core/modules/user/src/Entity/User.php @@ -7,6 +7,7 @@ use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; +use Drupal\Core\Flood\PrefixFloodInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\user\RoleInterface; use Drupal\user\StatusItem; @@ -125,6 +126,18 @@ public function postSave(EntityStorageInterface $storage, $update = TRUE) { if ($this->id() == \Drupal::currentUser()->id()) { \Drupal::service('session')->migrate(); } + + $flood_config = \Drupal::config('user.flood'); + $flood_service = \Drupal::flood(); + $identifier = $this->id(); + if ($flood_config->get('uid_only')) { + // Clear flood events based on the uid only if configured. + $flood_service->clear('user.failed_login_user', $identifier); + } + elseif ($flood_service instanceof PrefixFloodInterface) { + $flood_service->clearByPrefix('user.failed_login_user', $identifier); + } + } // If the user was blocked, delete the user's sessions to force a logout. diff --git a/core/modules/user/tests/src/Functional/UserPasswordResetTest.php b/core/modules/user/tests/src/Functional/UserPasswordResetTest.php index 19f97cba42f05cd0b8862290049e38c61a51dbf1..ca40e9a6621075ea98fd5bfc795bebbce9b2ff46 100644 --- a/core/modules/user/tests/src/Functional/UserPasswordResetTest.php +++ b/core/modules/user/tests/src/Functional/UserPasswordResetTest.php @@ -494,6 +494,57 @@ public function testUserResetPasswordUserFloodControlIsCleared() { $this->assertCount($before + 4, $this->drupalGetMails(['id' => 'user_password_reset']), 'Another email was sent after clearing flood control.'); } + /** + * Tests user password reset flood control is cleared on admin reset. + */ + public function testUserResetPasswordUserFloodControlAdmin() { + $admin_user = $this->drupalCreateUser([ + 'administer account settings', + 'administer users', + ]); + \Drupal::configFactory()->getEditable('user.flood') + ->set('user_limit', 3) + ->save(); + + $edit = [ + 'name' => $this->account->getAccountName(), + 'pass' => 'wrong_password', + ]; + + // Try 3 requests that should not trigger flood control. + for ($i = 0; $i < 3; $i++) { + $this->drupalGet('user/login'); + $this->submitForm($edit, 'Log in'); + $this->assertSession()->pageTextNotContains('There have been more than 3 failed login attempts for this account. It is temporarily blocked.'); + } + $this->drupalGet('user/login'); + $this->submitForm($edit, 'Log in'); + $this->assertSession()->pageTextContains('There have been more than 3 failed login attempts for this account. It is temporarily blocked.'); + + $password = $this->randomMachineName(); + $edit = [ + 'pass[pass1]' => $password, + 'pass[pass2]' => $password, + ]; + // Log in as admin and change the user password. + $this->drupalLogin($admin_user); + $this->drupalGet('user/' . $this->account->id() . '/edit'); + $this->submitForm($edit, 'Save'); + $this->drupalLogout(); + + $edit = [ + 'name' => $this->account->getAccountName(), + 'pass' => $password, + ]; + + // The next request should *not* trigger flood control, since the + // password change should have cleared flood events for this user. + $this->account->passRaw = $password; + $this->drupalLogin($this->account); + + $this->assertSession()->pageTextNotContains('There have been more than 3 failed login attempts for this account. It is temporarily blocked.'); + } + /** * Helper function to make assertions about a valid password reset. *