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