[TASK] Move BE-related bootstrap code to PSR-15 middleware 38/55538/8
authorBenni Mack <benni@typo3.org>
Sat, 3 Feb 2018 21:08:49 +0000 (22:08 +0100)
committerSusanne Moog <susanne.moog@typo3.org>
Thu, 8 Feb 2018 21:04:31 +0000 (22:04 +0100)
Various functionality used by both backend request handlers is moved
into PSR-15 compatible middleware, making the following Bootstrap
methods obsolete:

- checkLockedBackendAndRedirectOrDie()
- checkBackendIpOrDie()
- checkSslBackendAndRedirectIfNeeded()
- initializeBackendTemplate()
- initializeOutputCompression()
- sendHttpHeaders()

The following methods are still needed in other contexts however
- initializeBackendRouter()
- loadExtTables()
- endOutputBufferingAndCleanPreviousOutput()
- initializeBackendAuthentication()
- initializeBackendUser()
- initializeLanguageObject()

Resolves: #83792
Releases: master
Change-Id: I6772bbb73f17100dfc5116f58f1202f52fee1e8d
Reviewed-on: https://review.typo3.org/55538
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: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
typo3/sysext/backend/Classes/Http/AjaxRequestHandler.php
typo3/sysext/backend/Classes/Http/RequestHandler.php
typo3/sysext/backend/Classes/Middleware/AdditionalResponseHeaders.php [new file with mode: 0644]
typo3/sysext/backend/Classes/Middleware/BackendRouteInitialization.php [new file with mode: 0644]
typo3/sysext/backend/Classes/Middleware/BackendUserAuthenticator.php [new file with mode: 0644]
typo3/sysext/backend/Classes/Middleware/ForcedHttpsBackendRedirector.php [new file with mode: 0644]
typo3/sysext/backend/Classes/Middleware/LegacyBackendTemplateInitialization.php [new file with mode: 0644]
typo3/sysext/backend/Classes/Middleware/LockedBackendGuard.php [new file with mode: 0644]
typo3/sysext/backend/Classes/Middleware/OutputCompression.php [new file with mode: 0644]
typo3/sysext/backend/Configuration/RequestMiddlewares.php [new file with mode: 0644]

index 075e4f4..8f34cfc 100644 (file)
@@ -20,7 +20,6 @@ use Psr\Http\Message\ServerRequestInterface;
 use Psr\Http\Server\RequestHandlerInterface as PsrRequestHandlerInterface;
 use TYPO3\CMS\Backend\Routing\Exception\InvalidRequestTokenException;
 use TYPO3\CMS\Backend\Routing\Exception\ResourceNotFoundException;
-use TYPO3\CMS\Core\Core\Bootstrap;
 use TYPO3\CMS\Core\Http\RequestHandlerInterface;
 use TYPO3\CMS\Core\Http\Response;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -29,42 +28,12 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
  * AJAX dispatcher
  *
  * Main entry point for AJAX calls in the TYPO3 Backend. Based on ?route=/ajax/* of the outside application.
- * Before doing the basic BE-related set up of this request (see the additional calls on $this->bootstrap inside
- * handleRequest()), some AJAX-calls can be made without a valid user, which is determined here.
  *
  * AJAX Requests are typically registered within EXT:myext/Configuration/Backend/AjaxRoutes.php
  */
 class AjaxRequestHandler implements RequestHandlerInterface, PsrRequestHandlerInterface
 {
     /**
-     * Instance of the current TYPO3 bootstrap
-     * @var Bootstrap
-     */
-    protected $bootstrap;
-
-    /**
-     * List of requests that don't need a valid BE user
-     * @var array
-     */
-    protected $publicAjaxRoutes = [
-        '/ajax/login',
-        '/ajax/logout',
-        '/ajax/login/refresh',
-        '/ajax/login/timedout',
-        '/ajax/rsa/publickey'
-    ];
-
-    /**
-     * Constructor handing over the bootstrap and the original request
-     *
-     * @param Bootstrap $bootstrap
-     */
-    public function __construct(Bootstrap $bootstrap)
-    {
-        $this->bootstrap = $bootstrap;
-    }
-
-    /**
      * Handles any AJAX request in the TYPO3 Backend
      *
      * @param ServerRequestInterface $request
@@ -78,20 +47,24 @@ class AjaxRequestHandler implements RequestHandlerInterface, PsrRequestHandlerIn
     /**
      * Handles any AJAX request in the TYPO3 Backend, after finishing running middlewares
      *
+     * Creates a response object with JSON headers automatically, and then dispatches to the correct route
+     *
      * @param ServerRequestInterface $request
      * @return ResponseInterface
+     * @throws ResourceNotFoundException if no valid route was found
+     * @throws InvalidRequestTokenException if the request could not be verified
      */
     public function handle(ServerRequestInterface $request): ResponseInterface
     {
-        // First get the name of the route
-        $routePath = $request->getParsedBody()['route'] ?? $request->getQueryParams()['route'] ?? '';
-        $request = $request->withAttribute('routePath', $routePath);
-
-        $proceedIfNoUserIsLoggedIn = $this->isLoggedInBackendUserRequired($routePath);
-        $this->boot($proceedIfNoUserIsLoggedIn);
+        /** @var Response $response */
+        $response = GeneralUtility::makeInstance(Response::class, 'php://temp', 200, [
+            'Content-Type' => 'application/json; charset=utf-8',
+            'X-JSON' => 'true'
+        ]);
 
-        // Backend Routing - check if a valid route is there, and dispatch
-        return $this->dispatch($request);
+        /** @var RouteDispatcher $dispatcher */
+        $dispatcher = GeneralUtility::makeInstance(RouteDispatcher::class);
+        return $dispatcher->dispatch($request, $response);
     }
 
     /**
@@ -116,59 +89,4 @@ class AjaxRequestHandler implements RequestHandlerInterface, PsrRequestHandlerIn
     {
         return 80;
     }
-
-    /**
-     * Check if the user is required for the request
-     * If we're trying to do an ajax login, don't require a user
-     *
-     * @param string $routePath the Route path to check against, something like '
-     * @return bool whether the request can proceed without a login required
-     */
-    protected function isLoggedInBackendUserRequired(string $routePath): bool
-    {
-        return in_array($routePath, $this->publicAjaxRoutes, true);
-    }
-
-    /**
-     * Start the Backend bootstrap part
-     *
-     * @param bool $proceedIfNoUserIsLoggedIn a flag if a backend user is required
-     */
-    protected function boot(bool $proceedIfNoUserIsLoggedIn)
-    {
-        $this->bootstrap
-            ->checkLockedBackendAndRedirectOrDie($proceedIfNoUserIsLoggedIn)
-            ->checkBackendIpOrDie()
-            ->checkSslBackendAndRedirectIfNeeded()
-            ->initializeBackendRouter()
-            ->loadExtTables()
-            ->initializeBackendUser()
-            ->initializeBackendAuthentication($proceedIfNoUserIsLoggedIn)
-            ->initializeLanguageObject()
-            ->initializeBackendTemplate()
-            ->endOutputBufferingAndCleanPreviousOutput()
-            ->initializeOutputCompression()
-            ->sendHttpHeaders();
-    }
-
-    /**
-     * Creates a response object with JSON headers automatically, and then dispatches to the correct route
-     *
-     * @param ServerRequestInterface $request
-     * @return ResponseInterface $response
-     * @throws ResourceNotFoundException if no valid route was found
-     * @throws InvalidRequestTokenException if the request could not be verified
-     */
-    protected function dispatch(ServerRequestInterface $request): ResponseInterface
-    {
-        /** @var Response $response */
-        $response = GeneralUtility::makeInstance(Response::class, 'php://temp', 200, [
-            'Content-Type' => 'application/json; charset=utf-8',
-            'X-JSON' => 'true'
-        ]);
-
-        /** @var RouteDispatcher $dispatcher */
-        $dispatcher = GeneralUtility::makeInstance(RouteDispatcher::class);
-        return $dispatcher->dispatch($request, $response);
-    }
 }
index b5acbb3..825ac8b 100644 (file)
@@ -19,8 +19,6 @@ use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Psr\Http\Server\RequestHandlerInterface as PsrRequestHandlerInterface;
 use TYPO3\CMS\Backend\Routing\Exception\InvalidRequestTokenException;
-use TYPO3\CMS\Backend\Routing\Router;
-use TYPO3\CMS\Core\Core\Bootstrap;
 use TYPO3\CMS\Core\Http\RedirectResponse;
 use TYPO3\CMS\Core\Http\RequestHandlerInterface;
 use TYPO3\CMS\Core\Http\Response;
@@ -40,22 +38,6 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
 class RequestHandler implements RequestHandlerInterface, PsrRequestHandlerInterface
 {
     /**
-     * Instance of the current TYPO3 bootstrap
-     * @var Bootstrap
-     */
-    protected $bootstrap;
-
-    /**
-     * Constructor handing over the bootstrap and the original request
-     *
-     * @param Bootstrap $bootstrap
-     */
-    public function __construct(Bootstrap $bootstrap)
-    {
-        $this->bootstrap = $bootstrap;
-    }
-
-    /**
      * Handles any backend request
      *
      * @param ServerRequestInterface $request
@@ -68,41 +50,19 @@ class RequestHandler implements RequestHandlerInterface, PsrRequestHandlerInterf
 
     /**
      * Handles a backend request, after finishing running middlewares
+     * Dispatch the request to the appropriate controller through the
+     * Backend Dispatcher which resolves the routing
      *
      * @param ServerRequestInterface $request
      * @return ResponseInterface
      */
     public function handle(ServerRequestInterface $request): ResponseInterface
     {
-        // Check if a module URL is requested and deprecate this call
-        $moduleName = $request->getQueryParams()['M'] ?? $request->getParsedBody()['M'] ?? null;
-        // Allow the login page to be displayed if routing is not used and on index.php
-        $pathToRoute = $request->getQueryParams()['route'] ?? $request->getParsedBody()['route'] ?? $moduleName ?? '/login';
-
-        // skip the BE user check on the login page
-        // should be handled differently in the future by checking the Bootstrap directly
-        $this->boot($pathToRoute === '/login');
-
-        if ($moduleName !== null) {
-            // backwards compatibility for old module names
-            // @deprecated since TYPO3 CMS 9, will be removed in TYPO3 CMS 10.
-            $router = GeneralUtility::makeInstance(Router::class);
-            foreach ($router->getRoutes() as $routeIdentifier => $route) {
-                if ($routeIdentifier === $moduleName) {
-                    $pathToRoute = $route->getPath();
-                    break;
-                }
-            }
-
-            trigger_error('Calling the TYPO3 Backend with "M" GET parameter will be removed in TYPO3 v10,'
-                . ' the calling code calls this script with "&M=' . $moduleName . '" and needs to be adapted'
-                . ' to use the TYPO3 API.', E_USER_DEPRECATED);
-        }
-        $request = $request->withAttribute('routePath', $pathToRoute);
-
         // Check if the router has the available route and dispatch.
         try {
-            return $this->dispatch($request);
+            $response = GeneralUtility::makeInstance(Response::class);
+            $dispatcher = GeneralUtility::makeInstance(RouteDispatcher::class);
+            return $dispatcher->dispatch($request, $response);
         } catch (InvalidRequestTokenException $e) {
             // When token was invalid redirect to login
             $url = GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . TYPO3_mainDir;
@@ -111,28 +71,6 @@ class RequestHandler implements RequestHandlerInterface, PsrRequestHandlerInterf
     }
 
     /**
-     * Does the main work for setting up the backend environment for any Backend request
-     *
-     * @param bool $proceedIfNoUserIsLoggedIn option to allow to render the request even if no user is logged in
-     */
-    protected function boot(bool $proceedIfNoUserIsLoggedIn)
-    {
-        $this->bootstrap
-            ->checkLockedBackendAndRedirectOrDie()
-            ->checkBackendIpOrDie()
-            ->checkSslBackendAndRedirectIfNeeded()
-            ->initializeBackendRouter()
-            ->loadExtTables()
-            ->initializeBackendUser()
-            ->initializeBackendAuthentication($proceedIfNoUserIsLoggedIn)
-            ->initializeLanguageObject()
-            ->initializeBackendTemplate()
-            ->endOutputBufferingAndCleanPreviousOutput()
-            ->initializeOutputCompression()
-            ->sendHttpHeaders();
-    }
-
-    /**
      * This request handler can handle any backend request (but not CLI).
      *
      * @param ServerRequestInterface $request
@@ -153,21 +91,4 @@ class RequestHandler implements RequestHandlerInterface, PsrRequestHandlerInterf
     {
         return 50;
     }
-
-    /**
-     * Dispatch the request to the appropriate controller through the Backend Dispatcher which resolves the routing
-     *
-     * @param ServerRequestInterface $request
-     * @return ResponseInterface
-     * @throws InvalidRequestTokenException if the request could not be verified
-     * @throws \InvalidArgumentException when a route is found but the target of the route cannot be called
-     */
-    protected function dispatch(ServerRequestInterface $request): ResponseInterface
-    {
-        /** @var Response $response */
-        $response = GeneralUtility::makeInstance(Response::class);
-        /** @var RouteDispatcher $dispatcher */
-        $dispatcher = GeneralUtility::makeInstance(RouteDispatcher::class);
-        return $dispatcher->dispatch($request, $response);
-    }
 }
diff --git a/typo3/sysext/backend/Classes/Middleware/AdditionalResponseHeaders.php b/typo3/sysext/backend/Classes/Middleware/AdditionalResponseHeaders.php
new file mode 100644 (file)
index 0000000..e5638a7
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Backend\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;
+
+/**
+ * Sets up click-jacking prevention for HTTP requests by adding HTTP headers for the response
+ *
+ * @internal
+ */
+class AdditionalResponseHeaders implements MiddlewareInterface
+{
+    /**
+     * Adds HTTP headers defined in $GLOBALS['TYPO3_CONF_VARS']['BE']['HTTP']['Response']['Headers']
+     *
+     * @param ServerRequestInterface $request
+     * @param RequestHandlerInterface $handler
+     * @return ResponseInterface
+     */
+    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
+    {
+        $response = $handler->handle($request);
+        foreach ($GLOBALS['TYPO3_CONF_VARS']['BE']['HTTP']['Response']['Headers'] ?? [] as $header => $value) {
+            $response = $response->withAddedHeader($header, $value);
+        }
+        return $response;
+    }
+}
diff --git a/typo3/sysext/backend/Classes/Middleware/BackendRouteInitialization.php b/typo3/sysext/backend/Classes/Middleware/BackendRouteInitialization.php
new file mode 100644 (file)
index 0000000..cccf3ce
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Backend\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\Backend\Routing\Router;
+use TYPO3\CMS\Core\Core\Bootstrap;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+/**
+ * Initializes the Backend Router and also loads ext_tables.php from all extensions, as this is the place
+ * where all modules register their routes to the router afterwards.
+ *
+ * The route path is added to the request as attribute "routePath".
+ *
+ * @internal
+ */
+class BackendRouteInitialization implements MiddlewareInterface
+{
+    /**
+     * Resolve the &route (or &M) GET/POST parameter, and also the Router object.
+     *
+     * @param ServerRequestInterface $request
+     * @param RequestHandlerInterface $handler
+     * @return ResponseInterface
+     */
+    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
+    {
+        // Check if a module URL is requested and deprecate this call
+        $moduleName = $request->getQueryParams()['M'] ?? $request->getParsedBody()['M'] ?? null;
+        // Allow the login page to be displayed if routing is not used and on index.php
+        $pathToRoute = $request->getQueryParams()['route'] ?? $request->getParsedBody()['route'] ?? $moduleName ?? '/login';
+
+        if ($moduleName !== null) {
+            // backwards compatibility for old module names
+            // @deprecated since TYPO3 CMS 9, will be removed in TYPO3 CMS 10.
+            $router = GeneralUtility::makeInstance(Router::class);
+            foreach ($router->getRoutes() as $routeIdentifier => $route) {
+                if ($routeIdentifier === $moduleName) {
+                    $pathToRoute = $route->getPath();
+                    break;
+                }
+            }
+
+            trigger_error('Calling the TYPO3 Backend with "M" GET parameter will be removed in TYPO3 v10,'
+                . ' the calling code calls this script with "&M=' . $moduleName . '" and needs to be adapted'
+                . ' to use the TYPO3 API.', E_USER_DEPRECATED);
+        }
+
+        Bootstrap::getInstance()
+            ->initializeBackendRouter()
+            ->loadExtTables();
+
+        // Add the route path to the request
+        $request = $request->withAttribute('routePath', $pathToRoute);
+
+        return $handler->handle($request);
+    }
+}
diff --git a/typo3/sysext/backend/Classes/Middleware/BackendUserAuthenticator.php b/typo3/sysext/backend/Classes/Middleware/BackendUserAuthenticator.php
new file mode 100644 (file)
index 0000000..926fe0d
--- /dev/null
@@ -0,0 +1,76 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Backend\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\Core\Bootstrap;
+
+/**
+ * Initializes the backend user authentication object (BE_USER) and the global LANG object.
+ *
+ * @internal
+ */
+class BackendUserAuthenticator implements MiddlewareInterface
+{
+    /**
+     * List of requests that don't need a valid BE user
+     *
+     * @var array
+     */
+    protected $publicRoutes = [
+        '/login',
+        '/ajax/login',
+        '/ajax/logout',
+        '/ajax/login/refresh',
+        '/ajax/login/timedout',
+        '/ajax/rsa/publickey'
+    ];
+
+    /**
+     * Calls the bootstrap process to set up $GLOBALS['BE_USER'] AND $GLOBALS['LANG']
+     *
+     * @param ServerRequestInterface $request
+     * @param RequestHandlerInterface $handler
+     * @return ResponseInterface
+     */
+    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
+    {
+        $pathToRoute = $request->getAttribute('routePath', '/login');
+
+        Bootstrap::getInstance()
+            ->initializeBackendUser()
+            // @todo: once this logic is in this method, the redirect URL should be handled as response here
+            ->initializeBackendAuthentication($this->isLoggedInBackendUserRequired($pathToRoute))
+            ->initializeLanguageObject();
+
+        return $handler->handle($request);
+    }
+
+    /**
+     * Check if the user is required for the request
+     * If we're trying to do a login or an ajax login, don't require a user
+     *
+     * @param string $routePath the Route path to check against, something like '
+     * @return bool whether the request can proceed without a login required
+     */
+    protected function isLoggedInBackendUserRequired(string $routePath): bool
+    {
+        return in_array($routePath, $this->publicRoutes, true);
+    }
+}
diff --git a/typo3/sysext/backend/Classes/Middleware/ForcedHttpsBackendRedirector.php b/typo3/sysext/backend/Classes/Middleware/ForcedHttpsBackendRedirector.php
new file mode 100644 (file)
index 0000000..2dd78f5
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Backend\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\Http\RedirectResponse;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+/**
+ * Check lockSSL configuration variable and redirect
+ * to https version of the backend if needed
+ *
+ * @internal
+ */
+class ForcedHttpsBackendRedirector implements MiddlewareInterface
+{
+    /**
+     * @todo Remove getIndpEnv() usage once $request contains all the site parameters (URL etc.)
+     *
+     * @param ServerRequestInterface $request
+     * @param RequestHandlerInterface $handler
+     * @return ResponseInterface
+     */
+    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
+    {
+        if ((bool)$GLOBALS['TYPO3_CONF_VARS']['BE']['lockSSL'] && !GeneralUtility::getIndpEnv('TYPO3_SSL')) {
+            if ((int)$GLOBALS['TYPO3_CONF_VARS']['BE']['lockSSLPort']) {
+                $sslPortSuffix = ':' . (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['lockSSLPort'];
+            } else {
+                $sslPortSuffix = '';
+            }
+            list(, $url) = explode('://', GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . TYPO3_mainDir, 2);
+            list($server, $address) = explode('/', $url, 2);
+            return new RedirectResponse('https://' . $server . $sslPortSuffix . '/' . $address);
+        }
+
+        return $handler->handle($request);
+    }
+}
diff --git a/typo3/sysext/backend/Classes/Middleware/LegacyBackendTemplateInitialization.php b/typo3/sysext/backend/Classes/Middleware/LegacyBackendTemplateInitialization.php
new file mode 100644 (file)
index 0000000..1d483ec
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Backend\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\Backend\Template\DocumentTemplate;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+/**
+ * Initializes the global object $GLOBALS['TBE_TEMPLATE'] for legacy reasons
+ *
+ * @internal
+ */
+class LegacyBackendTemplateInitialization implements MiddlewareInterface
+{
+    /**
+     * $GLOBALS['TBE_TEMPLATE'] is available then
+     *
+     * @param ServerRequestInterface $request
+     * @param RequestHandlerInterface $handler
+     * @return ResponseInterface
+     */
+    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
+    {
+        $GLOBALS['TBE_TEMPLATE'] = GeneralUtility::makeInstance(DocumentTemplate::class);
+        return $handler->handle($request);
+    }
+}
diff --git a/typo3/sysext/backend/Classes/Middleware/LockedBackendGuard.php b/typo3/sysext/backend/Classes/Middleware/LockedBackendGuard.php
new file mode 100644 (file)
index 0000000..aa49337
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Backend\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\Http\RedirectResponse;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+/**
+ * Checks various security options for accessing the TYPO3 backend before proceeding
+ *
+ * @internal
+ */
+class LockedBackendGuard implements MiddlewareInterface
+{
+    /**
+     * Checks the client's IP address and if typo3conf/LOCK_BACKEND is available
+     *
+     * @param ServerRequestInterface $request
+     * @param RequestHandlerInterface $handler
+     * @return ResponseInterface
+     */
+    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
+    {
+        $redirectToUri = $this->checkLockedBackend();
+        if (!empty($redirectToUri)) {
+            return new RedirectResponse($redirectToUri, 302);
+        }
+        $this->validateVisitorsIpAgainstIpMaskList(
+            $request->getServerParams()['REMOTE_ADDR'],
+            trim((string)$GLOBALS['TYPO3_CONF_VARS']['BE']['IPmaskList'])
+        );
+
+        return $handler->handle($request);
+    }
+
+    /**
+     * Check adminOnly configuration variable and redirects to an URL in file typo3conf/LOCK_BACKEND
+     *
+     * @throws \RuntimeException
+     * @return string|null
+     */
+    protected function checkLockedBackend()
+    {
+        if ($GLOBALS['TYPO3_CONF_VARS']['BE']['adminOnly'] < 0) {
+            throw new \RuntimeException('TYPO3 Backend locked: Backend and Install Tool are locked for maintenance. [BE][adminOnly] is set to "' . (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['adminOnly'] . '".', 1517949794);
+        }
+        if (@is_file(PATH_typo3conf . 'LOCK_BACKEND')) {
+            $fileContent = file_get_contents(PATH_typo3conf . 'LOCK_BACKEND');
+            if ($fileContent) {
+                return $fileContent;
+            }
+            throw new \RuntimeException('TYPO3 Backend locked: Browser backend is locked for maintenance. Remove lock by removing the file "typo3conf/LOCK_BACKEND" or use CLI-scripts.', 1517949793);
+        }
+    }
+
+    /**
+     * Compare client IP with IPmaskList and throw an exception
+     *
+     * @param string $ipAddress
+     * @param string $ipMaskList
+     * @throws \RuntimeException
+     */
+    protected function validateVisitorsIpAgainstIpMaskList(string $ipAddress, string $ipMaskList = '')
+    {
+        if ($ipMaskList !== '' && !GeneralUtility::cmpIP($ipAddress, $ipMaskList)) {
+            throw new \RuntimeException('TYPO3 Backend access denied: The IP address of your client does not match the list of allowed IP addresses.', 1517949792);
+        }
+    }
+}
diff --git a/typo3/sysext/backend/Classes/Middleware/OutputCompression.php b/typo3/sysext/backend/Classes/Middleware/OutputCompression.php
new file mode 100644 (file)
index 0000000..6bb93af
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Backend\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\MathUtility;
+
+/**
+ * Sets up output compression
+ *
+ * @internal
+ */
+class OutputCompression implements MiddlewareInterface
+{
+    /**
+     * Clears all output and checks if a compression level is set
+     *
+     * @param ServerRequestInterface $request
+     * @param RequestHandlerInterface $handler
+     * @return ResponseInterface
+     */
+    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
+    {
+        // Throw away all output that may have happened during bootstrapping by weird extensions
+        ob_clean();
+        // Initialize output compression if configured
+        $this->initializeOutputCompression();
+
+        return $handler->handle($request);
+    }
+
+    /**
+     * Initialize output compression if configured
+     */
+    protected function initializeOutputCompression()
+    {
+        if (extension_loaded('zlib') && $GLOBALS['TYPO3_CONF_VARS']['BE']['compressionLevel']) {
+            if (MathUtility::canBeInterpretedAsInteger($GLOBALS['TYPO3_CONF_VARS']['BE']['compressionLevel'])) {
+                @ini_set('zlib.output_compression_level', $GLOBALS['TYPO3_CONF_VARS']['BE']['compressionLevel']);
+            }
+            ob_start('ob_gzhandler');
+        }
+    }
+}
diff --git a/typo3/sysext/backend/Configuration/RequestMiddlewares.php b/typo3/sysext/backend/Configuration/RequestMiddlewares.php
new file mode 100644 (file)
index 0000000..cfa2346
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+/**
+ * An array consisting of implementations of middlewares for a middleware stack to be registered
+ *
+ *  'stackname' => [
+ *      'middleware-identifier' => [
+ *         'target' => classname or callable
+ *         'before/after' => array of dependencies
+ *      ]
+ *   ]
+ */
+return [
+    'backend' => [
+        'typo3/cms-backend/locked-backend' => [
+            'target' => \TYPO3\CMS\Backend\Middleware\LockedBackendGuard::class,
+        ],
+        'typo3/cms-backend/https-redirector' => [
+            'target' => \TYPO3\CMS\Backend\Middleware\ForcedHttpsBackendRedirector::class,
+            'after' => [
+                'typo3/cms-backend/locked-backend'
+            ]
+        ],
+        'typo3/cms-backend/backend-routing' => [
+            'target' => \TYPO3\CMS\Backend\Middleware\BackendRouteInitialization::class,
+            'after' => [
+                'typo3/cms-backend/https-redirector'
+            ]
+        ],
+        'typo3/cms-backend/authentication' => [
+            'target' => \TYPO3\CMS\Backend\Middleware\BackendUserAuthenticator::class,
+            'after' => [
+                'typo3/cms-backend/backend-routing'
+            ]
+        ],
+        'typo3/cms-backend/legacy-document-template' => [
+            'target' => \TYPO3\CMS\Backend\Middleware\LegacyBackendTemplateInitialization::class,
+            'after' => [
+                'typo3/cms-backend/authentication'
+            ]
+        ],
+        'typo3/cms-backend/output-compression' => [
+            'target' => \TYPO3\CMS\Backend\Middleware\OutputCompression::class,
+            'after' => [
+                'typo3/cms-backend/authentication'
+            ]
+        ],
+        'typo3/cms-backend/response-headers' => [
+            'target' => \TYPO3\CMS\Backend\Middleware\AdditionalResponseHeaders::class,
+            'after' => [
+                'typo3/cms-backend/output-compression'
+            ]
+        ],
+    ]
+];