Issue #992540 by valthebald, ndobromirov, jec006, kid_icarus, rickmanelius, Matt V., aerozeppelin, Munavijayalakshmi, gaurav.kapoor, swentel, klidifia, Yogesh Pawar, Everett Zufelt, grendzy, cashwilliams, lachezar.valchev, alexpott, tstoeckler, catch, Heine: Nothing Clears the "5 Failed Login Attempts" Security message
parent
b9c288ba3e
commit
cade2c61d1
|
@ -6,6 +6,7 @@ use Drupal\Component\Utility\Crypt;
|
|||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Datetime\DateFormatterInterface;
|
||||
use Drupal\Core\Flood\FloodInterface;
|
||||
use Drupal\user\Form\UserPasswordResetForm;
|
||||
use Drupal\user\UserDataInterface;
|
||||
use Drupal\user\UserInterface;
|
||||
|
@ -48,6 +49,13 @@ class UserController extends ControllerBase {
|
|||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* The flood service.
|
||||
*
|
||||
* @var \Drupal\Core\Flood\FloodInterface
|
||||
*/
|
||||
protected $flood;
|
||||
|
||||
/**
|
||||
* Constructs a UserController object.
|
||||
*
|
||||
|
@ -59,12 +67,15 @@ class UserController extends ControllerBase {
|
|||
* The user data service.
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
* A logger instance.
|
||||
* @param \Drupal\Core\Flood\FloodInterface $flood
|
||||
* The flood service.
|
||||
*/
|
||||
public function __construct(DateFormatterInterface $date_formatter, UserStorageInterface $user_storage, UserDataInterface $user_data, LoggerInterface $logger) {
|
||||
public function __construct(DateFormatterInterface $date_formatter, UserStorageInterface $user_storage, UserDataInterface $user_data, LoggerInterface $logger, FloodInterface $flood) {
|
||||
$this->dateFormatter = $date_formatter;
|
||||
$this->userStorage = $user_storage;
|
||||
$this->userData = $user_data;
|
||||
$this->logger = $logger;
|
||||
$this->flood = $flood;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,7 +86,8 @@ class UserController extends ControllerBase {
|
|||
$container->get('date.formatter'),
|
||||
$container->get('entity.manager')->getStorage('user'),
|
||||
$container->get('user.data'),
|
||||
$container->get('logger.factory')->get('user')
|
||||
$container->get('logger.factory')->get('user'),
|
||||
$container->get('flood')
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -186,6 +198,8 @@ class UserController extends ControllerBase {
|
|||
/**
|
||||
* Validates user, hash, and timestamp; logs the user in if correct.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The request.
|
||||
* @param int $uid
|
||||
* User ID of the user requesting reset.
|
||||
* @param int $timestamp
|
||||
|
@ -201,7 +215,7 @@ class UserController extends ControllerBase {
|
|||
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
|
||||
* If $uid is for a blocked user or invalid user ID.
|
||||
*/
|
||||
public function resetPassLogin($uid, $timestamp, $hash) {
|
||||
public function resetPassLogin(Request $request, $uid, $timestamp, $hash) {
|
||||
// The current user is not logged in, so check the parameters.
|
||||
$current = REQUEST_TIME;
|
||||
/** @var \Drupal\user\UserInterface $user */
|
||||
|
@ -222,6 +236,18 @@ class UserController extends ControllerBase {
|
|||
return $this->redirect('user.pass');
|
||||
}
|
||||
elseif ($user->isAuthenticated() && ($timestamp >= $user->getLastLoginTime()) && ($timestamp <= $current) && Crypt::hashEquals($hash, user_pass_rehash($user, $timestamp))) {
|
||||
$flood_config = $this->config('user.flood');
|
||||
if ($flood_config->get('uid_only')) {
|
||||
// Clear flood events based on the uid only if configured.
|
||||
$identifier = $user->id();
|
||||
}
|
||||
else {
|
||||
// The default identifier is a combination of uid and IP address.
|
||||
$identifier = $user->id() . '-' . $request->getClientIP();
|
||||
}
|
||||
$this->flood->clear('user.failed_login_ip');
|
||||
$this->flood->clear('user.failed_login_user', $identifier);
|
||||
|
||||
user_login_finalize($user);
|
||||
$this->logger->notice('User %name used one-time login link at time %timestamp.', ['%name' => $user->getDisplayName(), '%timestamp' => $timestamp]);
|
||||
drupal_set_message($this->t('You have just used your one-time login link. It is no longer necessary to use this link to log in. Please change your password.'));
|
||||
|
|
|
@ -63,16 +63,23 @@ class UserLoginTest extends WebTestBase {
|
|||
// A login with the correct password should also result in a flood error
|
||||
// message.
|
||||
$this->assertFailedLogin($user1, 'ip');
|
||||
$this->resetUserPassword($user1);
|
||||
$this->drupalLogout();
|
||||
// Try to login as user 1, it should be successful.
|
||||
$this->drupalLogin($user1);
|
||||
$this->assertNoRaw('Too many failed login attempts from your IP address.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the per-user login flood control.
|
||||
*/
|
||||
public function testPerUserLoginFloodControl() {
|
||||
$user_limit = 3;
|
||||
|
||||
$this->config('user.flood')
|
||||
// Set a high global limit out so that it is not relevant in the test.
|
||||
->set('ip_limit', 4000)
|
||||
->set('user_limit', 3)
|
||||
->set('user_limit', $user_limit)
|
||||
->save();
|
||||
|
||||
$user1 = $this->drupalCreateUser([]);
|
||||
|
@ -103,6 +110,12 @@ class UserLoginTest extends WebTestBase {
|
|||
// Try one more attempt for user 1, it should be rejected, even if the
|
||||
// correct password has been used.
|
||||
$this->assertFailedLogin($user1, 'user');
|
||||
$this->resetUserPassword($user1);
|
||||
$this->drupalLogout();
|
||||
|
||||
// Try to login as user 1, it should be successful.
|
||||
$this->drupalLogin($user1);
|
||||
$this->assertNoRaw('There have been more than ' . $user_limit . ' failed login attempts for this account.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -175,4 +188,23 @@ class UserLoginTest extends WebTestBase {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset user password.
|
||||
*
|
||||
* @param object $user
|
||||
* A user object.
|
||||
*/
|
||||
public function resetUserPassword($user) {
|
||||
$this->drupalGet('user/password');
|
||||
$edit['name'] = $user->getUsername();
|
||||
$this->drupalPostForm(NULL, $edit, 'Submit');
|
||||
$_emails = $this->drupalGetMails();
|
||||
$email = end($_emails);
|
||||
$urls = [];
|
||||
preg_match('#.+user/reset/.+#', $email['body'], $urls);
|
||||
$resetURL = $urls[0];
|
||||
$this->drupalGet($resetURL);
|
||||
$this->drupalPostForm(NULL, NULL, 'Log in');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue