From fee6f4e0821415d52f8f0db13dca57baec5fb5dd Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Wed, 10 Jul 2024 08:41:14 +0100
Subject: [PATCH] Issue #3456738 by cmlara, Anybody, andrewbelcher, Berdir,
 catch: BC break in login auth changes from #3444978

(cherry picked from commit e015f80827e2a92da1a1cd7f13bd7e47b453b956)
---
 .../UserAuthenticationController.php          |  2 +-
 core/modules/user/src/Form/UserLoginForm.php  |  7 ++++
 .../src/UserAuthDecorator.php                 | 28 ++++++++++++++++
 .../user_auth_decorator_test.info.yml         |  5 +++
 .../user_auth_decorator_test.services.yml     |  6 ++++
 .../Rest/UserJsonBasicAuthDecoratedTest.php   | 31 +++++++++++++++++
 .../src/Functional/UserLoginDecoratedTest.php | 33 +++++++++++++++++++
 .../Functional/UserLoginHttpDecoratedTest.php | 33 +++++++++++++++++++
 8 files changed, 144 insertions(+), 1 deletion(-)
 create mode 100644 core/modules/user/tests/modules/user_auth_decorator_test/src/UserAuthDecorator.php
 create mode 100644 core/modules/user/tests/modules/user_auth_decorator_test/user_auth_decorator_test.info.yml
 create mode 100644 core/modules/user/tests/modules/user_auth_decorator_test/user_auth_decorator_test.services.yml
 create mode 100644 core/modules/user/tests/src/Functional/Rest/UserJsonBasicAuthDecoratedTest.php
 create mode 100644 core/modules/user/tests/src/Functional/UserLoginDecoratedTest.php
 create mode 100644 core/modules/user/tests/src/Functional/UserLoginHttpDecoratedTest.php

diff --git a/core/modules/user/src/Controller/UserAuthenticationController.php b/core/modules/user/src/Controller/UserAuthenticationController.php
index 7758b0d96d2f..af31a878ddcc 100644
--- a/core/modules/user/src/Controller/UserAuthenticationController.php
+++ b/core/modules/user/src/Controller/UserAuthenticationController.php
@@ -201,7 +201,7 @@ public function login(Request $request) {
         $authenticated = $this->userAuth->authenticateAccount($account, $credentials['pass']) ? $account->id() : FALSE;
       }
       else {
-        $authenticated = $this->userAuth->authenticateAccount($credentials['name'], $credentials['pass']);
+        $authenticated = $this->userAuth->authenticate($credentials['name'], $credentials['pass']);
       }
       if ($authenticated) {
         $this->userFloodControl->clear('user.http_login', $this->getLoginFloodIdentifier($request, $credentials['name']));
diff --git a/core/modules/user/src/Form/UserLoginForm.php b/core/modules/user/src/Form/UserLoginForm.php
index 8fe696d15a53..33e6653eb154 100644
--- a/core/modules/user/src/Form/UserLoginForm.php
+++ b/core/modules/user/src/Form/UserLoginForm.php
@@ -246,6 +246,13 @@ public function validateAuthentication(array &$form, FormStateInterface $form_st
         if ($this->userAuth instanceof UserAuthenticationInterface) {
           $form_state->set('uid', $this->userAuth->authenticateAccount($account, $password) ? $account->id() : FALSE);
         }
+        // The userAuth object is decorated by an object that that has not
+        // been upgraded to the new UserAuthenticationInterface. Fallback
+        // to the authenticate() method.
+        else {
+          $uid = $this->userAuth->authenticate($form_state->getValue('name'), $password);
+          $form_state->set('uid', $uid);
+        }
       }
       elseif (!$this->userAuth instanceof UserAuthenticationInterface) {
         $uid = $this->userAuth->authenticate($form_state->getValue('name'), $password);
diff --git a/core/modules/user/tests/modules/user_auth_decorator_test/src/UserAuthDecorator.php b/core/modules/user/tests/modules/user_auth_decorator_test/src/UserAuthDecorator.php
new file mode 100644
index 000000000000..fc2fd1a5c8fb
--- /dev/null
+++ b/core/modules/user/tests/modules/user_auth_decorator_test/src/UserAuthDecorator.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Drupal\user_auth_decorator_test;
+
+use Drupal\user\UserAuthInterface;
+
+/**
+ * Helper to validate UserAuthInterface BC layers are functional.
+ */
+class UserAuthDecorator implements UserAuthInterface {
+
+  /**
+   * Constructs a UserAuthDecorator object.
+   *
+   * @param \Drupal\user\UserAuthInterface $inner
+   *   The inner User.Auth service.
+   */
+  public function __construct(protected UserAuthInterface $inner) {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function authenticate($username, #[\SensitiveParameter] $password) {
+    return $this->inner->authenticate($username, $password);
+  }
+
+}
diff --git a/core/modules/user/tests/modules/user_auth_decorator_test/user_auth_decorator_test.info.yml b/core/modules/user/tests/modules/user_auth_decorator_test/user_auth_decorator_test.info.yml
new file mode 100644
index 000000000000..a05dfd67c2ee
--- /dev/null
+++ b/core/modules/user/tests/modules/user_auth_decorator_test/user_auth_decorator_test.info.yml
@@ -0,0 +1,5 @@
+name: 'User Auth Service decorated only with UserAuthInterface'
+type: module
+description: 'Support module for user authentication testing.'
+package: Testing
+version: VERSION
diff --git a/core/modules/user/tests/modules/user_auth_decorator_test/user_auth_decorator_test.services.yml b/core/modules/user/tests/modules/user_auth_decorator_test/user_auth_decorator_test.services.yml
new file mode 100644
index 000000000000..c274386664cb
--- /dev/null
+++ b/core/modules/user/tests/modules/user_auth_decorator_test/user_auth_decorator_test.services.yml
@@ -0,0 +1,6 @@
+services:
+  user_auth_decorator.user.auth:
+    class: \Drupal\user_auth_decorator_test\UserAuthDecorator
+    decorates: user.auth
+    arguments:
+      - '@user_auth_decorator.user.auth.inner'
diff --git a/core/modules/user/tests/src/Functional/Rest/UserJsonBasicAuthDecoratedTest.php b/core/modules/user/tests/src/Functional/Rest/UserJsonBasicAuthDecoratedTest.php
new file mode 100644
index 000000000000..463852243dec
--- /dev/null
+++ b/core/modules/user/tests/src/Functional/Rest/UserJsonBasicAuthDecoratedTest.php
@@ -0,0 +1,31 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\user\Functional\Rest;
+
+use Drupal\user_auth_decorator_test\UserAuthDecorator;
+
+/**
+ * Run UserJsonBasicAuthTest with a user.auth decorator.
+ *
+ * @group rest
+ * @group #slow
+ */
+class UserJsonBasicAuthDecoratedTest extends UserJsonBasicAuthTest {
+  /**
+   * Modules to install.
+   *
+   * @var array
+   */
+  protected static $modules = ['user_auth_decorator_test'];
+
+  /**
+   * Test that the UserAuthDecorator is providing user.auth.
+   */
+  public function testServiceDecorated(): void {
+    $service = \Drupal::service('user.auth');
+    $this->assertInstanceOf(UserAuthDecorator::class, $service);
+  }
+
+}
diff --git a/core/modules/user/tests/src/Functional/UserLoginDecoratedTest.php b/core/modules/user/tests/src/Functional/UserLoginDecoratedTest.php
new file mode 100644
index 000000000000..2f09b03927d5
--- /dev/null
+++ b/core/modules/user/tests/src/Functional/UserLoginDecoratedTest.php
@@ -0,0 +1,33 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\user\Functional;
+
+use Drupal\user_auth_decorator_test\UserAuthDecorator;
+
+/**
+ * Ensure that login works as expected with a decorator.
+ *
+ * The decorator does not implement UserAuthenticationInterface.
+ *
+ * @group user
+ */
+class UserLoginDecoratedTest extends UserLoginTest {
+
+  /**
+   * Modules to install.
+   *
+   * @var array
+   */
+  protected static $modules = ['user_auth_decorator_test'];
+
+  /**
+   * Test that the UserAuthDecorator is providing user.auth.
+   */
+  public function testServiceDecorated(): void {
+    $service = \Drupal::service('user.auth');
+    $this->assertInstanceOf(UserAuthDecorator::class, $service);
+  }
+
+}
diff --git a/core/modules/user/tests/src/Functional/UserLoginHttpDecoratedTest.php b/core/modules/user/tests/src/Functional/UserLoginHttpDecoratedTest.php
new file mode 100644
index 000000000000..71ab498faf4f
--- /dev/null
+++ b/core/modules/user/tests/src/Functional/UserLoginHttpDecoratedTest.php
@@ -0,0 +1,33 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\user\Functional;
+
+use Drupal\user_auth_decorator_test\UserAuthDecorator;
+
+/**
+ * Tests login and password reset via direct HTTP with a user.auth decorator.
+ *
+ * The decorator does not implement UserAuthenticationInterface.
+ *
+ * @group user
+ */
+class UserLoginHttpDecoratedTest extends UserLoginHttpTest {
+
+  /**
+   * Modules to install.
+   *
+   * @var array
+   */
+  protected static $modules = ['user_auth_decorator_test'];
+
+  /**
+   * Test that the UserAuthDecorator is providing user.auth.
+   */
+  public function testServiceDecorated(): void {
+    $service = \Drupal::service('user.auth');
+    $this->assertInstanceOf(UserAuthDecorator::class, $service);
+  }
+
+}
-- 
GitLab