[TASK] Move FE User initialization into PSR-15 middleware 53/55753/4
authorBenni Mack <benni@typo3.org>
Fri, 16 Feb 2018 12:13:12 +0000 (13:13 +0100)
committerGeorg Ringer <georg.ringer@gmail.com>
Mon, 19 Feb 2018 08:55:48 +0000 (09:55 +0100)
The initialization of a frontend user is currently bound to TSFE,
however, the only parts where TSFE is actually needed is to
set the frontenduserauthentication object to $TSFE->fe_user
and to call a post-processing hook, which could be dealt with
through a middleware for extensions.

Further steps are to deprecate "initFEuser()" method, and to move
the transferring of the user session into the FrontendUser object
itself, once this is not using superglobals instead anymore.

Additional cleanups on the Authentication object will follow separately
once all Middlewares and the Request object are in place.

For now, this patch aims to extract the logic and keep maximum backwards-
compatibility.

Resolves: #83936
Releases: master
Change-Id: Id51b49f41336717fde4606a71ec6b2c805662b88
Reviewed-on: https://review.typo3.org/55753
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Mathias Schreiber <mathias.schreiber@typo3.com>
Tested-by: Mathias Schreiber <mathias.schreiber@typo3.com>
Reviewed-by: Georg Ringer <georg.ringer@gmail.com>
Tested-by: Georg Ringer <georg.ringer@gmail.com>
typo3/sysext/frontend/Classes/Http/RequestHandler.php
typo3/sysext/frontend/Classes/Middleware/FrontendUserAuthenticator.php [new file with mode: 0644]
typo3/sysext/frontend/Configuration/RequestMiddlewares.php

index dd76da5..78faf1a 100644 (file)
@@ -87,11 +87,6 @@ class RequestHandler implements RequestHandlerInterface, PsrRequestHandlerInterf
         /** @var TypoScriptFrontendController $controller */
         $controller = $GLOBALS['TSFE'];
 
-        // Initializing the Frontend User
-        $this->timeTracker->push('Front End user initialized', '');
-        $controller->initFEuser();
-        $this->timeTracker->pull();
-
         // Initializing a possible logged-in Backend User
         /** @var $GLOBALS['BE_USER'] \TYPO3\CMS\Backend\FrontendBackendUserAuthentication */
         $GLOBALS['BE_USER'] = $controller->initializeBackendUser();
diff --git a/typo3/sysext/frontend/Classes/Middleware/FrontendUserAuthenticator.php b/typo3/sysext/frontend/Classes/Middleware/FrontendUserAuthenticator.php
new file mode 100644 (file)
index 0000000..e2df00e
--- /dev/null
@@ -0,0 +1,110 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Frontend\Middleware;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\MiddlewareInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
+
+/**
+ * This middleware authenticates a Frontend User (fe_users).
+ * A valid $GLOBALS['TSFE'] object is needed for the time being, being fully backwards-compatible.
+ */
+class FrontendUserAuthenticator implements MiddlewareInterface
+{
+    /**
+     * Creates a frontend user authentication object, tries to authenticate a user
+     * and stores the object in $GLOBALS['TSFE']->fe_user.
+     *
+     * @param ServerRequestInterface $request
+     * @param RequestHandlerInterface $handler
+     * @return ResponseInterface
+     */
+    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
+    {
+        $frontendUser = GeneralUtility::makeInstance(FrontendUserAuthentication::class);
+
+        // List of page IDs where to look for frontend user records
+        $pid = $request->getParsedBody()['pid'] ?? $request->getQueryParams()['pid'] ?? 0;
+        if ($pid) {
+            $frontendUser->checkPid_value = implode(',', GeneralUtility::intExplode(',', $pid));
+        }
+
+        // Check if a session is transferred, and update the cookie parameters
+        $frontendSessionKey = $request->getParsedBody()['FE_SESSION_KEY'] ?? $request->getQueryParams()['FE_SESSION_KEY'] ?? '';
+        if ($frontendSessionKey) {
+            $request = $this->transferFrontendUserSession($frontendUser, $request, $frontendSessionKey);
+        }
+
+        // Authenticate now
+        $frontendUser->start();
+        $frontendUser->unpack_uc();
+
+        // Keep the backwards-compatibility for TYPO3 v9, to have the fe_user within the global TSFE object
+        $GLOBALS['TSFE']->fe_user = $frontendUser;
+
+        // Call hook for possible manipulation of frontend user object
+        // This hook is kept for compatibility reasons, however, it should be fairly simple to add a custom middleware
+        // for this purpose
+        $_params = ['pObj' => &$GLOBALS['TSFE']];
+        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['initFEuser'] ?? [] as $_funcRef) {
+            GeneralUtility::callUserFunction($_funcRef, $_params, $GLOBALS['TSFE']);
+        }
+        return $handler->handle($request);
+    }
+
+    /**
+     * It's possible to transfer a frontend user session via a GET/POST parameter 'FE_SESSION_KEY'.
+     * In the future, this logic should be moved into the FrontendUserAuthentication object directly,
+     * but only if FrontendUserAuthentication does not request superglobals (like $_COOKIE) anymore.
+     *
+     * @param FrontendUserAuthentication $frontendUser
+     * @param ServerRequestInterface $request
+     * @param string $frontendSessionKey
+     * @return ServerRequestInterface
+     */
+    protected function transferFrontendUserSession(
+        FrontendUserAuthentication $frontendUser,
+        ServerRequestInterface $request,
+        string $frontendSessionKey
+    ): ServerRequestInterface {
+        list($sessionId, $hash) = explode('-', $frontendSessionKey);
+        // If the session key hash check is OK, set the cookie
+        if (md5(($sessionId . '/' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) === (string)$hash) {
+            $cookieName = FrontendUserAuthentication::getCookieName();
+
+            // keep the global cookie overwriting for now, as long as FrontendUserAuthentication does not
+            // use the request object for fetching the cookie information.
+            $_COOKIE[$cookieName] = $sessionId;
+            if (isset($_SERVER['HTTP_COOKIE'])) {
+                // See http://forge.typo3.org/issues/27740
+                $_SERVER['HTTP_COOKIE'] .= ';' . $cookieName . '=' . $sessionId;
+            }
+            // Add the cookie to the Server Request object
+            $cookieParams = $request->getCookieParams();
+            $cookieParams[$cookieName] = $sessionId;
+            $request = $request->withCookieParams($cookieParams);
+            // @deprecated: we override the current request because it was enriched by cookie information here.
+            $GLOBALS['TYPO3_REQUEST'] = $request;
+            $frontendUser->forceSetCookie = true;
+            $frontendUser->dontSetCookie = false;
+        }
+        return $request;
+    }
+}
index 69c26f5..83c386b 100644 (file)
@@ -63,5 +63,11 @@ return [
                 'typo3/cms-frontend/tsfe',
             ]
         ],
+        'typo3/cms-frontend/authentication' => [
+            'target' => \TYPO3\CMS\Frontend\Middleware\FrontendUserAuthenticator::class,
+            'after' => [
+                'typo3/cms-frontend/tsfe',
+            ]
+        ],
     ]
 ];