View source
<?php
namespace Drupal\login_security\Tests;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Logger\RfcLogLevel;
class LoginSecurityUserBlockingTest extends LoginSecurityTestBase {
public static $modules = [
'user',
'login_security',
'dblog',
];
protected $badUsers = [];
public function setUp() {
parent::setUp();
$this->badUsers[] = $this
->drupalCreateUser();
$this->badUsers[] = $this
->drupalCreateUser();
}
protected function getAttemptsAvailableMessage($attempt, $attempts_limit) {
$variables = [
'@attempt' => $attempt,
'@login_attempts_limit' => $attempts_limit,
];
return SafeMarkup::format('You have used @attempt out of @login_attempts_limit login attempts. After all @login_attempts_limit have been used, you will be unable to login.', $variables);
}
protected function getDefaultDrupalLoginErrorMessage() {
return 'Unrecognized username or password.';
}
protected function getDefaultDrupalBlockedUserErrorMessage($user_name) {
return SafeMarkup::format('The username %name has not been activated or is blocked.', [
'%name' => $user_name,
]);
}
protected function assertTextLastLoginMessage() {
$this
->assertText('Your last login was', 'Last login message found.');
}
protected function assertNoTextLastLoginMessage() {
$this
->assertNoText('Your last login was', 'Last login message not found.');
}
protected function assertTextLastPageAccess() {
$this
->assertText('Your last page access (site activity) was ', 'Last page access message found.');
}
protected function assertNoTextLastPageAccess() {
$this
->assertNoText('Your last page access (site activity) was ', 'Last page access message not found.');
}
protected function assertBlockedUser($log, $username) {
$variables = [
'@username' => $username,
];
$expected = SafeMarkup::format('Blocked user @username due to security configuration.', $variables);
$this
->assertEqual(SafeMarkup::format($log->message, unserialize($log->variables)), $expected, 'User blocked log was set.');
$this
->assertEqual($log->severity, RfcLogLevel::NOTICE, 'User blocked log was of severity "Notice".');
}
protected function getLogMessages() {
return \Drupal::database()
->select('watchdog', 'w')
->fields('w', [
'wid',
'message',
'variables',
'severity',
])
->condition('w.type', 'login_security')
->execute()
->fetchAllAssoc('wid');
}
public function testThresholdNotify() {
\Drupal::configFactory()
->getEditable('login_security.settings')
->set('user_wrong_count', 5)
->set('activity_threshold', 5)
->save();
for ($i = 0; $i < 10; $i++) {
$login = [
'name' => $this->badUsers[0]
->getAccountName(),
'pass' => 'bad_password_' . $i,
];
$this
->drupalPostForm('user', $login, t('Log in'));
}
$logs = $this
->getLogMessages();
$this
->assertEqual(count($logs), 1, '1 event was logged.');
$log = array_pop($logs);
$this
->assertBlockedUser($log, $this->badUsers[0]
->getAccountName());
db_truncate('watchdog')
->execute();
for ($i = 0; $i < 10; $i++) {
$login = [
'name' => $this->badUsers[1]
->getAccountName(),
'pass' => 'bad_password_' . $i,
];
$this
->drupalPostForm('user', $login, t('Log in'));
}
$logs = $this
->getLogMessages();
$this
->assertEqual(count($logs), 2, '2 events were logged.');
$log = array_shift($logs);
$variables = [
'@activity_threshold' => 5,
'@tracking_current_count' => 6,
];
$expected = SafeMarkup::format('Ongoing attack detected: Suspicious activity detected in login form submissions. Too many invalid login attempts threshold reached: currently @tracking_current_count events are tracked, and threshold is configured for @activity_threshold attempts.', $variables);
$this
->assertEqual(SafeMarkup::format($log->message, unserialize($log->variables)), $expected);
$this
->assertEqual($log->severity, RfcLogLevel::WARNING, 'The logged alert was of severity "Warning".');
$log = array_shift($logs);
$this
->assertBlockedUser($log, $this->badUsers[1]
->getAccountName());
}
public function testUserBlocking() {
$config = \Drupal::configFactory()
->getEditable('login_security.settings');
$login_attempts_limit = 2;
$config
->set('user_wrong_count', $login_attempts_limit)
->save();
$config
->set('user_blocked_notification_emails', '')
->save();
$normal_user = $this
->drupalCreateUser();
$new_pass = user_password();
$normal_user
->setPassword($new_pass);
$config
->set('notice_attempts_available', 1)
->save();
$this
->drupalLoginLite($normal_user);
$this
->assertText($this
->getAttemptsAvailableMessage(1, $login_attempts_limit), 'Attempts available message displayed.');
$this
->assertFieldByName('form_id', 'user_login_form', 'Login form found.');
$config
->set('notice_attempts_available', 0)
->save();
$this
->drupalLoginLite($normal_user);
$this
->assertNoText($this
->getAttemptsAvailableMessage(2, $login_attempts_limit), 'Attempts available message NOT displayed.');
$this
->assertFieldByName('form_id', 'user_login_form', 'Login form found.');
$this
->assertText(SafeMarkup::format('The user @user_name has been blocked due to failed login attempts.', [
'@user_name' => $normal_user
->getUsername(),
]), 'Blocked message displayed.');
$this
->assertFieldByName('form_id', 'user_login_form', 'Login form found.');
}
public function testDrupalErrorToggle() {
$config = \Drupal::configFactory()
->getEditable('login_security.settings');
$normal_user = $this
->drupalCreateUser();
$new_pass = user_password();
$normal_user
->setPassword($new_pass);
$config
->set('disable_core_login_error', 0)
->save();
$this
->drupalLoginLite($normal_user);
$this
->assertRaw($this
->getDefaultDrupalLoginErrorMessage(), 'Drupal "...Have you forgotten your password?" login error message found.');
$normal_user->status
->setValue(0);
$normal_user
->save();
$this
->drupalLoginLite($normal_user);
$this
->assertRaw($this
->getDefaultDrupalBlockedUserErrorMessage($normal_user
->getAccountName()), 'Drupal "...has not been activated or is blocked." login error message found.');
$config
->set('disable_core_login_error', 1)
->save();
$normal_user->status
->setValue(1);
$normal_user
->save();
$this
->drupalLoginLite($normal_user);
$this
->assertNoRaw($this
->getDefaultDrupalLoginErrorMessage(), 'Drupal "...Have you forgotten your password?" login error message NOT found.');
$normal_user->status
->setValue(0);
$normal_user
->save();
$this
->drupalLoginLite($normal_user);
$this
->assertNoRaw($this
->getDefaultDrupalBlockedUserErrorMessage($normal_user
->getAccountName()), 'Drupal "...has not been activated or is blocked." login error message NOT found.');
}
public function testLoginMessage() {
$config = \Drupal::configFactory()
->getEditable('login_security.settings');
$normal_user = $this
->drupalCreateUser();
$config
->set('last_login_timestamp', 1)
->save();
$config
->set('last_access_timestamp', 1)
->save();
$this
->drupalLogin($normal_user);
$this
->assertNoTextLastLoginMessage();
$config
->set('last_login_timestamp', 0)
->save();
$config
->set('last_access_timestamp', 0)
->save();
$this
->drupalLogin($normal_user);
$this
->assertNoTextLastLoginMessage();
$this
->assertNoTextLastPageAccess();
$config
->set('last_login_timestamp', 1)
->save();
$this
->drupalLogin($normal_user);
$this
->assertTextLastLoginMessage();
$this
->assertNoTextLastPageAccess();
$config
->set('last_login_timestamp', 0)
->save();
$config
->set('last_access_timestamp', 1)
->save();
$this
->drupalLogin($normal_user);
$this
->assertNoTextLastLoginMessage();
$this
->assertTextLastPageAccess();
$config
->set('last_login_timestamp', 1)
->save();
$this
->drupalLogin($normal_user);
$this
->assertTextLastLoginMessage();
$this
->assertTextLastPageAccess();
}
}