From 0308595bc544e9ceab05106b248a04ab3cef0d48 Mon Sep 17 00:00:00 2001 From: Torben Hansen <derhansen@gmail.com> Date: Wed, 8 Mar 2023 19:38:33 +0100 Subject: [PATCH] [FEATURE] Make PSR-7 request accessible for authentication services Custom TYPO3 authentication services can now directly access the PSR-7 Request object via the $authInfo array, handed over to the initAuth() method of those services. This therefore allows to further reduce usages of PHP super globals and `GeneralUtility::getIndpEnv()`. Resolves: #100116 Releases: main Signed-off-by: Torben Hansen <derhansen@gmail.com> Change-Id: I12a3484b49862886e7013dc2106a0705ef39c91f Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/78077 Tested-by: Benni Mack <benni@typo3.org> Reviewed-by: Benni Mack <benni@typo3.org> Tested-by: core-ci <typo3@b13.com> Tested-by: Oliver Bartsch <bo@cedev.de> Reviewed-by: Oliver Bartsch <bo@cedev.de> --- .../AbstractUserAuthentication.php | 24 ++++++----- ...estAccessibleForAuthenticationServices.rst | 43 +++++++++++++++++++ .../AbstractUserAuthenticationTest.php | 3 +- .../Controller/BackendModuleController.php | 4 +- 4 files changed, 60 insertions(+), 14 deletions(-) create mode 100644 typo3/sysext/core/Documentation/Changelog/12.3/Feature-100116-MakePSR-7RequestAccessibleForAuthenticationServices.rst diff --git a/typo3/sysext/core/Classes/Authentication/AbstractUserAuthentication.php b/typo3/sysext/core/Classes/Authentication/AbstractUserAuthentication.php index 2c4859ddddae..ba7602c4a3af 100644 --- a/typo3/sysext/core/Classes/Authentication/AbstractUserAuthentication.php +++ b/typo3/sysext/core/Classes/Authentication/AbstractUserAuthentication.php @@ -254,6 +254,7 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface public function start(ServerRequestInterface $request) { $this->logger->debug('## Beginning of auth logging.'); + // Make certain that NO user is set initially $this->user = null; @@ -502,7 +503,7 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface } // Fetch users from the database (or somewhere else) - $possibleUsers = $this->fetchPossibleUsers($loginData, $activeLogin, $isExistingSession, $authenticatedUserFromSession); + $possibleUsers = $this->fetchPossibleUsers($loginData, $activeLogin, $isExistingSession, $authenticatedUserFromSession, $request); // If no new user was set we use the already found user session if (empty($possibleUsers) && $isExistingSession && !$anonymousSession) { @@ -534,7 +535,7 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface $subType = 'authUser' . $this->loginType; /** @var AuthenticationService $serviceObj */ - foreach ($this->getAuthServices($subType, $loginData, $authenticatedUserFromSession) as $serviceObj) { + foreach ($this->getAuthServices($subType, $loginData, $authenticatedUserFromSession, $request) as $serviceObj) { if (($ret = (int)$serviceObj->authUser($userRecordCandidate)) > 0) { // If the service returns >=200 then no more checking is needed - useful for IP checking without password if ($ret >= 200) { @@ -561,7 +562,7 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface // @link https://cwe.mitre.org/data/definitions/208.html } elseif ($activeLogin) { $subType = 'authUser' . $this->loginType; - foreach ($this->getAuthServices($subType, $loginData, $authenticatedUserFromSession) as $serviceObj) { + foreach ($this->getAuthServices($subType, $loginData, $authenticatedUserFromSession, $request) as $serviceObj) { if ($serviceObj instanceof MimicServiceInterface && $serviceObj->mimicAuthUser() === false) { break; } @@ -647,7 +648,7 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface * * @param array|null $authenticatedUserFromSession if we have a user from an existing session, this is set here, otherwise null */ - protected function fetchPossibleUsers(array $loginData, bool $activeLogin, bool $isExistingSession, ?array $authenticatedUserFromSession): array + protected function fetchPossibleUsers(array $loginData, bool $activeLogin, bool $isExistingSession, ?array $authenticatedUserFromSession, ServerRequestInterface $request): array { $possibleUsers = []; $authConfiguration = $this->getAuthServiceConfiguration(); @@ -662,7 +663,7 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface // First found user will be used $subType = 'getUser' . $this->loginType; /** @var AuthenticationService $serviceObj */ - foreach ($this->getAuthServices($subType, $loginData, $authenticatedUserFromSession) as $serviceObj) { + foreach ($this->getAuthServices($subType, $loginData, $authenticatedUserFromSession, $request) as $serviceObj) { $row = $serviceObj->getUser(); if (is_array($row)) { $possibleUsers[] = $row; @@ -742,11 +743,11 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface * @param array|null $authenticatedUserFromSession the user which was loaded from the session, or null if none was found * @return \Traversable A generator of service objects */ - protected function getAuthServices(string $subType, array $loginData, ?array $authenticatedUserFromSession): \Traversable + protected function getAuthServices(string $subType, array $loginData, ?array $authenticatedUserFromSession, ServerRequestInterface $request): \Traversable { $serviceChain = []; // The info array provide additional information for auth services - $authInfo = $this->getAuthInfoArray(); + $authInfo = $this->getAuthInfoArray($request); if ($authenticatedUserFromSession !== null) { $authInfo['user'] = $authenticatedUserFromSession; } @@ -1118,7 +1119,7 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface ]; // Only process the login data if a login is requested if ($loginData['status'] === LoginType::LOGIN) { - $loginData = $this->processLoginData($loginData); + $loginData = $this->processLoginData($loginData, $request); } return $loginData; } @@ -1136,14 +1137,14 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface * @return array * @internal */ - public function processLoginData($loginData) + public function processLoginData(array $loginData, ServerRequestInterface $request): array { $this->logger->debug('Login data before processing', $this->removeSensitiveLoginDataForLoggingInfo($loginData)); $subType = 'processLoginData' . $this->loginType; $isLoginDataProcessed = false; $processedLoginData = $loginData; /** @var AuthenticationService $serviceObject */ - foreach ($this->getAuthServices($subType, $loginData, null) as $serviceObject) { + foreach ($this->getAuthServices($subType, $loginData, null, $request) as $serviceObject) { $serviceResult = $serviceObject->processLoginData($processedLoginData, 'normal'); if (!empty($serviceResult)) { $isLoginDataProcessed = true; @@ -1194,12 +1195,13 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface * @return array * @internal */ - public function getAuthInfoArray() + public function getAuthInfoArray(ServerRequestInterface $request) { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->user_table); $expressionBuilder = $queryBuilder->expr(); $authInfo = []; $authInfo['loginType'] = $this->loginType; + $authInfo['request'] = $request; $authInfo['refInfo'] = parse_url(GeneralUtility::getIndpEnv('HTTP_REFERER')); $authInfo['HTTP_HOST'] = GeneralUtility::getIndpEnv('HTTP_HOST'); $authInfo['REMOTE_ADDR'] = GeneralUtility::getIndpEnv('REMOTE_ADDR'); diff --git a/typo3/sysext/core/Documentation/Changelog/12.3/Feature-100116-MakePSR-7RequestAccessibleForAuthenticationServices.rst b/typo3/sysext/core/Documentation/Changelog/12.3/Feature-100116-MakePSR-7RequestAccessibleForAuthenticationServices.rst new file mode 100644 index 000000000000..b5257fa80a80 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/12.3/Feature-100116-MakePSR-7RequestAccessibleForAuthenticationServices.rst @@ -0,0 +1,43 @@ +.. include:: /Includes.rst.txt + +.. _feature-100116-1678299307: + +============================================================================ +Feature: #100116 - Make PSR-7 Request accessible for authentication services +============================================================================ + +See :issue:`100116` + +Description +=========== + +Authentication services can now access the PSR-7 Request object via the +:php:`$authInfo` array. Previously, custom TYPO3 authentication services +did not have direct access to the object and therefore had to either +use PHP super globals or TYPO3's `GeneralUtility::getIndpEnv()` method. + +The following example shows how to retrieve the PSR-7 Request in the +`initAuth()` method of a custom authentication service: + +.. code-block:: php + + public function initAuth($mode, $loginData, $authInfo, $pObj) + { + /** @var ServerRequestInterface $request */ + $request = $authInfo['request']; + + /** @var NormalizedParams $normalizedParams */ + $normalizedParams = $request->getAttribute('normalizedParams'); + $isHttps = $normalizedParams->isHttps(); + } + + +Impact +====== + +Custom TYPO3 authentication services can now directly access the PSR-7 +Request object from the authentication process. It is available via the +:php:`request` key of the :php:`$authInfo` array, which is handed over +to the :php:`initAuth()` method. + +.. index:: ext:core diff --git a/typo3/sysext/core/Tests/Functional/Authentication/AbstractUserAuthenticationTest.php b/typo3/sysext/core/Tests/Functional/Authentication/AbstractUserAuthenticationTest.php index 233c30e1b509..7e534c573b16 100644 --- a/typo3/sysext/core/Tests/Functional/Authentication/AbstractUserAuthenticationTest.php +++ b/typo3/sysext/core/Tests/Functional/Authentication/AbstractUserAuthenticationTest.php @@ -18,6 +18,7 @@ declare(strict_types=1); namespace TYPO3\CMS\Core\Tests\Functional\Authentication; use TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression; +use TYPO3\CMS\Core\Http\ServerRequest; use TYPO3\CMS\Core\Session\UserSession; use TYPO3\CMS\Core\Tests\Functional\Authentication\Fixtures\AnyUserAuthentication; use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; @@ -80,7 +81,7 @@ class AbstractUserAuthenticationTest extends FunctionalTestCase $this->subject->user_table = 'be_users'; $this->subject->checkPid_value = null; - $authInfoArray = $this->subject->getAuthInfoArray(); + $authInfoArray = $this->subject->getAuthInfoArray(new ServerRequest('https://example.com')); $enableClause = $authInfoArray['db_user']['enable_clause']; self::assertInstanceOf(CompositeExpression::class, $enableClause); diff --git a/typo3/sysext/install/Classes/Controller/BackendModuleController.php b/typo3/sysext/install/Classes/Controller/BackendModuleController.php index 2556d097470d..974227edf4ae 100644 --- a/typo3/sysext/install/Classes/Controller/BackendModuleController.php +++ b/typo3/sysext/install/Classes/Controller/BackendModuleController.php @@ -226,8 +226,8 @@ class BackendModuleController ]; // currently there is no dedicated API to perform authentication // that's why this process partially has to be simulated here - $loginData = $backendUser->processLoginData($loginData); - $authInfo = $backendUser->getAuthInfoArray(); + $loginData = $backendUser->processLoginData($loginData, $request); + $authInfo = $backendUser->getAuthInfoArray($request); $authenticated = false; /** @var AbstractAuthenticationService $service or any other service (sic!) */ -- GitLab