diff options
author | Angie Byron <webchick@24967.no-reply.drupal.org> | 2009-08-08 20:52:33 +0000 |
---|---|---|
committer | Angie Byron <webchick@24967.no-reply.drupal.org> | 2009-08-08 20:52:33 +0000 |
commit | 1da6ef52c44fd38785391d3a94af8e969344bc12 (patch) | |
tree | 074f1da5dae5e9d1877e601b097cf7e3dc7a957f /modules/user/user.test | |
parent | 78e3681cde7f4115b7f65cc3ebb0a2e61a27595a (diff) | |
download | brdo-1da6ef52c44fd38785391d3a94af8e969344bc12.tar.gz brdo-1da6ef52c44fd38785391d3a94af8e969344bc12.tar.bz2 |
#485974 by pwolanin, Damien Tournoud, mr.baileys: Improved security by limiting the number of allowed login attempts.
Diffstat (limited to 'modules/user/user.test')
-rw-r--r-- | modules/user/user.test | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/modules/user/user.test b/modules/user/user.test index 82d0bcf5f..42fe6bf42 100644 --- a/modules/user/user.test +++ b/modules/user/user.test @@ -159,6 +159,123 @@ class UserValidationTestCase extends DrupalWebTestCase { } } +/** + * Functional tests for user logins, including rate limiting of login attempts. + */ +class UserLoginTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'User login', + 'description' => 'Ensure that login works as expected.', + 'group' => 'User', + ); + } + + /** + * Test the global login flood control. + */ + function testGlobalLoginFloodControl() { + // Set the global login limit. + variable_set('user_failed_login_ip_limit', 10); + // Set a high per-user limit out so that it is not relevant in the test. + variable_set('user_failed_login_user_limit', 4000); + + $user1 = $this->drupalCreateUser(array()); + $incorrect_user1 = clone $user1; + $incorrect_user1->pass_raw .= 'incorrect'; + + // Try 2 failed logins. + for ($i = 0; $i < 2; $i++) { + $this->assertFailedLogin($incorrect_user1); + } + + // A successful login will not reset the IP-based flood control count. + $this->drupalLogin($user1); + $this->drupalLogout(); + + // Try 8 more failed logins, they should not trigger the flood control + // mechanism. + for ($i = 0; $i < 8; $i++) { + $this->assertFailedLogin($incorrect_user1); + } + + // The next login trial should result in an IP-based flood error message. + $this->assertFailedLogin($incorrect_user1, 'ip'); + + // A login with the correct password should also result in a flood error + // message. + $this->assertFailedLogin($user1, 'ip'); + } + + /** + * Test the per-user login flood control. + */ + function testPerUserLoginFloodControl() { + // 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()); + $incorrect_user1 = clone $user1; + $incorrect_user1->pass_raw .= 'incorrect'; + + $user2 = $this->drupalCreateUser(array()); + + // Try 2 failed logins. + for ($i = 0; $i < 2; $i++) { + $this->assertFailedLogin($incorrect_user1); + } + + // A successful login will reset the per-user flood control count. + $this->drupalLogin($user1); + $this->drupalLogout(); + + // Try 3 failed logins for user 1, they will not trigger flood control. + for ($i = 0; $i < 3; $i++) { + $this->assertFailedLogin($incorrect_user1); + } + + // Try one successful attempt for user 2, it should not trigger any + // flood control. + $this->drupalLogin($user2); + $this->drupalLogout(); + + // Try one more attempt for user 1, it should be rejected, even if the + // correct password has been used. + $this->assertFailedLogin($user1, 'user'); + } + + /** + * Make an unsuccessful login attempt. + * + * @param $account + * A user object with name and pass_raw attributes for the login attempt. + * @param $flood_trigger + * Whether or not to expect that the flood control mechanism will be + * triggered. + */ + function assertFailedLogin($account, $flood_trigger = NULL) { + $edit = array( + 'name' => $account->name, + 'pass' => $account->pass_raw, + ); + $this->drupalPost('user', $edit, t('Log in')); + if (isset($flood_trigger)) { + if ($flood_trigger == 'user') { + $this->assertRaw(format_plural(variable_get('user_failed_login_user_limit', 5), 'Sorry, there has been more than one failed login attempt for this account. It is temporarily blocked. Please try again later, or <a href="@url">request a new password</a>.', 'Sorry, there have been more than @count failed login attempts for this account. It is temporarily blocked. Please try again later, or <a href="@url">request a new password</a>.', array('@url' => url('user/password')))); + } + else { + // No uid, so the limit is IP-based. + $this->assertRaw(t('Sorry, too many failed login attempts from your IP address. This IP address is temporarily blocked. Please try again later, or <a href="@url">request a new password</a>.', array('@url' => url('user/password')))); + } + } + else { + $this->assertText(t('Sorry, unrecognized username or password. Have you forgotten your password?')); + } + } +} + class UserCancelTestCase extends DrupalWebTestCase { public static function getInfo() { return array( |