summaryrefslogtreecommitdiff
path: root/modules/user/user.test
diff options
context:
space:
mode:
authorAngie Byron <webchick@24967.no-reply.drupal.org>2009-08-08 20:52:33 +0000
committerAngie Byron <webchick@24967.no-reply.drupal.org>2009-08-08 20:52:33 +0000
commit1da6ef52c44fd38785391d3a94af8e969344bc12 (patch)
tree074f1da5dae5e9d1877e601b097cf7e3dc7a957f /modules/user/user.test
parent78e3681cde7f4115b7f65cc3ebb0a2e61a27595a (diff)
downloadbrdo-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.test117
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(