From 09190cd3a4286d5af8f2a4af5c5f07900e90bf8a Mon Sep 17 00:00:00 2001 From: Alex Pott <alex.a.pott@googlemail.com> Date: Thu, 2 Dec 2021 06:57:58 +0000 Subject: [PATCH] Issue #3198010 by mcdruid, acbramley, mstrelan, quietone, manojithape, vikashsoni, chetanbharambe, sarguna raj M, larowlan, alexpott: User login page broken when there are more than 5 failed login attempts for an account --- core/modules/user/src/Form/UserLoginForm.php | 21 +++++++++++++++---- .../tests/src/Functional/UserLoginTest.php | 2 ++ .../src/Kernel/Form/UserLoginFormTest.php | 5 ++++- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/core/modules/user/src/Form/UserLoginForm.php b/core/modules/user/src/Form/UserLoginForm.php index b2fb91a290ee..1fb58ddbde92 100644 --- a/core/modules/user/src/Form/UserLoginForm.php +++ b/core/modules/user/src/Form/UserLoginForm.php @@ -5,13 +5,13 @@ use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\RendererInterface; +use Drupal\Core\Render\BareHtmlPageRendererInterface; use Drupal\Core\Url; use Drupal\user\UserAuthInterface; use Drupal\user\UserInterface; use Drupal\user\UserStorageInterface; use Drupal\user\UserFloodControlInterface; use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\HttpFoundation\Response; /** * Provides a user login form. @@ -48,6 +48,13 @@ class UserLoginForm extends FormBase { */ protected $renderer; + /** + * The bare HTML renderer. + * + * @var \Drupal\Core\Render\BareHtmlPageRendererInterface + */ + protected $bareHtmlPageRenderer; + /** * Constructs a new UserLoginForm. * @@ -59,8 +66,10 @@ class UserLoginForm extends FormBase { * The user authentication object. * @param \Drupal\Core\Render\RendererInterface $renderer * The renderer. + * @param \Drupal\Core\Render\BareHtmlPageRendererInterface $bare_html_renderer + * The renderer. */ - public function __construct($user_flood_control, UserStorageInterface $user_storage, UserAuthInterface $user_auth, RendererInterface $renderer) { + public function __construct($user_flood_control, UserStorageInterface $user_storage, UserAuthInterface $user_auth, RendererInterface $renderer, BareHtmlPageRendererInterface $bare_html_renderer) { if (!$user_flood_control instanceof UserFloodControlInterface) { @trigger_error('Passing the flood service to ' . __METHOD__ . ' is deprecated in drupal:9.1.0 and is replaced by user.flood_control in drupal:10.0.0. See https://www.drupal.org/node/3067148', E_USER_DEPRECATED); $user_flood_control = \Drupal::service('user.flood_control'); @@ -69,6 +78,7 @@ public function __construct($user_flood_control, UserStorageInterface $user_stor $this->userStorage = $user_storage; $this->userAuth = $user_auth; $this->renderer = $renderer; + $this->bareHtmlPageRenderer = $bare_html_renderer; } /** @@ -79,7 +89,8 @@ public static function create(ContainerInterface $container) { $container->get('user.flood_control'), $container->get('entity_type.manager')->getStorage('user'), $container->get('user.auth'), - $container->get('renderer') + $container->get('renderer'), + $container->get('bare_html_page_renderer') ); } @@ -237,7 +248,9 @@ public function validateFinal(array &$form, FormStateInterface $form_state) { // We did not find a uid, so the limit is IP-based. $message = $this->t('Too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or <a href=":url">request a new password</a>.', [':url' => Url::fromRoute('user.pass')->toString()]); } - $form_state->setResponse(new Response($message, 403)); + $response = $this->bareHtmlPageRenderer->renderBarePage(['#markup' => $message], $this->t('Login failed'), 'maintenance_page'); + $response->setStatusCode(403); + $form_state->setResponse($response); } else { // Use $form_state->getUserInput() in the error message to guarantee diff --git a/core/modules/user/tests/src/Functional/UserLoginTest.php b/core/modules/user/tests/src/Functional/UserLoginTest.php index cbc3b29cbeb4..1f8250408b6d 100644 --- a/core/modules/user/tests/src/Functional/UserLoginTest.php +++ b/core/modules/user/tests/src/Functional/UserLoginTest.php @@ -217,6 +217,7 @@ public function assertFailedLogin(User $account, string $flood_trigger = NULL): ->fetchField(); if ($flood_trigger == 'user') { $this->assertSession()->pageTextMatches("/There (has|have) been more than \w+ failed login attempt.* for this account. It is temporarily blocked. Try again later or request a new password./"); + $this->assertSession()->elementExists('css', 'body.maintenance-page'); $this->assertSession()->linkExists("request a new password"); $this->assertSession()->linkByHrefExists(Url::fromRoute('user.pass')->toString()); $this->assertEquals('Flood control blocked login attempt for uid %uid from %ip', $last_log, 'A watchdog message was logged for the login attempt blocked by flood control per user.'); @@ -224,6 +225,7 @@ public function assertFailedLogin(User $account, string $flood_trigger = NULL): else { // No uid, so the limit is IP-based. $this->assertSession()->pageTextContains("Too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or request a new password."); + $this->assertSession()->elementExists('css', 'body.maintenance-page'); $this->assertSession()->linkExists("request a new password"); $this->assertSession()->linkByHrefExists(Url::fromRoute('user.pass')->toString()); $this->assertEquals('Flood control blocked login attempt from %ip', $last_log, 'A watchdog message was logged for the login attempt blocked by flood control per IP.'); diff --git a/core/modules/user/tests/src/Kernel/Form/UserLoginFormTest.php b/core/modules/user/tests/src/Kernel/Form/UserLoginFormTest.php index 4bb50b8a82ac..103fea2d9e9a 100644 --- a/core/modules/user/tests/src/Kernel/Form/UserLoginFormTest.php +++ b/core/modules/user/tests/src/Kernel/Form/UserLoginFormTest.php @@ -4,6 +4,7 @@ use Drupal\Core\Flood\FloodInterface; use Drupal\Core\Render\RendererInterface; +use Drupal\Core\Render\BareHtmlPageRendererInterface; use Drupal\KernelTests\KernelTestBase; use Drupal\user\Form\UserLoginForm; use Drupal\user\UserAuthInterface; @@ -29,11 +30,13 @@ public function testConstructorDeprecations() { $user_storage = $this->prophesize(UserStorageInterface::class); $user_auth = $this->prophesize(UserAuthInterface::class); $renderer = $this->prophesize(RendererInterface::class); + $bare_html_renderer = $this->prophesize(BareHtmlPageRendererInterface::class); $form = new UserLoginForm( $flood->reveal(), $user_storage->reveal(), $user_auth->reveal(), - $renderer->reveal() + $renderer->reveal(), + $bare_html_renderer->reveal() ); $this->assertNotNull($form); } -- GitLab