diff --git a/core/modules/user/lib/Drupal/user/ProfileFormController.php b/core/modules/user/lib/Drupal/user/ProfileFormController.php index d32cf4a..897583a 100644 --- a/core/modules/user/lib/Drupal/user/ProfileFormController.php +++ b/core/modules/user/lib/Drupal/user/ProfileFormController.php @@ -53,6 +53,14 @@ class ProfileFormController extends AccountFormController { $account->save(); $form_state['values']['uid'] = $account->id(); + // Clear the flood table. Since we don't know the IP address for this user + // we can't use flood_clear_event because we need to use the LIKE operator. + $identifier = $account->uid . '-%'; + db_delete('flood') + ->condition('event', 'failed_login_attempt_user') + ->condition('identifier', $identifier, 'LIKE') + ->execute(); + // Clear the page cache because pages can contain usernames and/or profile // information: cache_invalidate(array('content' => TRUE)); diff --git a/core/modules/user/lib/Drupal/user/Tests/UserLoginTest.php b/core/modules/user/lib/Drupal/user/Tests/UserLoginTest.php index b5e8882..9220f0e 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserLoginTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserLoginTest.php @@ -97,6 +97,67 @@ class UserLoginTest extends WebTestBase { } /** + * Test that flood events are removed after an account has been updated. + */ + function testUpdatedUserFloodControl() { + // Set a high global limit out so that it is not relevant in the test. + variable_set('user_failed_login_ip_limit', 4000); + // Set the per-user login limit. + variable_set('user_failed_login_user_limit', 3); + + $user1 = $this->drupalCreateUser(array('administer users')); + $user2 = $this->drupalCreateUser(array()); + $user2->pass_raw .= 'incorrect'; + + // Try 3 failed logins. + for ($i = 0; $i < 3; $i++) { + $this->assertFailedLogin($user2); + } + + // The next login trial should result in an user-based flood error message. + $this->assertFailedLogin($user2, 'user'); + + // Update the account and assert the user can login again. + $this->drupalLogin($user1); + $user2->pass_raw = 'goodpass'; + $edit = array( + 'pass[pass1]' => $user2->pass_raw, + 'pass[pass2]' => $user2->pass_raw, + ); + $this->drupalPost('user/' . $user2->uid . '/edit', $edit, t('Save')); + $this->drupalLogout(); + $this->drupalLogin($user2); + } + + /** + * Test that flood events are removed after password reset. + */ + function testResetPasswordFloodControl() { + // Set a high global limit out so that it is not relevant in the test. + variable_set('user_failed_login_ip_limit', 4000); + // Set the per-user login limit. + variable_set('user_failed_login_user_limit', 3); + + $user1 = $this->drupalCreateUser(); + $correct = $user1->pass_raw; + $user1->pass_raw .= 'incorrect'; + + // Try 3 failed logins. + for ($i = 0; $i < 3; $i++) { + $this->assertFailedLogin($user1); + } + + // The next login trial should result in an user-based flood error message. + $this->assertFailedLogin($user1, 'user'); + + // Request new password, logout and login. + $this->drupalPost(user_pass_reset_url($user1), array(), t('Log in')); + $this->drupalLogout(); + $user1->pass_raw = $correct; + $this->drupalLogin($user1); + } + + /** * Test that user password is re-hashed upon login after changing $count_log2. */ function testPasswordRehashOnLogin() { diff --git a/core/modules/user/user.pages.inc b/core/modules/user/user.pages.inc index 9121c44..b286fb5 100644 --- a/core/modules/user/user.pages.inc +++ b/core/modules/user/user.pages.inc @@ -143,6 +143,14 @@ function user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $a // Let the user's password be changed without the current password check. $token = drupal_hash_base64(drupal_random_bytes(55)); $_SESSION['pass_reset_' . $user->uid] = $token; + // Clear out flood event for user trying to log in too many times. + if (variable_get('user_failed_login_identifier_uid_only', FALSE)) { + $identifier = $account->uid; + } + else { + $identifier = $account->uid . '-' . ip_address(); + } + flood_clear_event('failed_login_attempt_user', $identifier); drupal_goto('user/' . $user->uid . '/edit', array('query' => array('pass-reset-token' => $token))); } else {