[FEATURE] Add support for PSR-15 HTTP middlewares
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Http / RequestHandler.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Backend\Http;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use Psr\Http\Message\ResponseInterface;
19 use Psr\Http\Message\ServerRequestInterface;
20 use Psr\Http\Server\RequestHandlerInterface as PsrRequestHandlerInterface;
21 use TYPO3\CMS\Backend\Routing\Exception\InvalidRequestTokenException;
22 use TYPO3\CMS\Core\Core\Bootstrap;
23 use TYPO3\CMS\Core\Http\RedirectResponse;
24 use TYPO3\CMS\Core\Http\RequestHandlerInterface;
25 use TYPO3\CMS\Core\Http\Response;
26 use TYPO3\CMS\Core\Utility\GeneralUtility;
27
28 /**
29 * General RequestHandler for the TYPO3 Backend. This is used for all Backend requests except for CLI
30 * or AJAX calls.
31 *
32 * If a get/post parameter "route" is set, the Backend Routing is called and searches for a
33 * matching route inside the Router. The corresponding controller / action is called then which returns the response.
34 *
35 * The following get/post parameters are evaluated here:
36 * - route
37 * - token
38 */
39 class RequestHandler implements RequestHandlerInterface, PsrRequestHandlerInterface
40 {
41 /**
42 * Instance of the current TYPO3 bootstrap
43 * @var Bootstrap
44 */
45 protected $bootstrap;
46
47 /**
48 * Constructor handing over the bootstrap and the original request
49 *
50 * @param Bootstrap $bootstrap
51 */
52 public function __construct(Bootstrap $bootstrap)
53 {
54 $this->bootstrap = $bootstrap;
55 }
56
57 /**
58 * Handles any backend request
59 *
60 * @param ServerRequestInterface $request
61 * @return ResponseInterface
62 */
63 public function handleRequest(ServerRequestInterface $request): ResponseInterface
64 {
65 return $this->handle($request);
66 }
67
68 /**
69 * Handles a backend request, after finishing running middlewares
70 *
71 * @param ServerRequestInterface $request
72 * @return ResponseInterface
73 */
74 public function handle(ServerRequestInterface $request): ResponseInterface
75 {
76 // Check if a module URL is requested and deprecate this call
77 $moduleName = $request->getQueryParams()['M'] ?? $request->getParsedBody()['M'] ?? null;
78 // Allow the login page to be displayed if routing is not used and on index.php
79 $pathToRoute = $request->getQueryParams()['route'] ?? $request->getParsedBody()['route'] ?? $moduleName ?? '/login';
80 $request = $request->withAttribute('routePath', $pathToRoute);
81
82 // skip the BE user check on the login page
83 // should be handled differently in the future by checking the Bootstrap directly
84 $this->boot($pathToRoute === '/login');
85
86 if ($moduleName !== null) {
87 trigger_error('Calling the TYPO3 Backend with "M" GET parameter will be removed in TYPO3 v10,'
88 . ' the calling code calls this script with "&M=' . $moduleName . '" and needs to be adapted'
89 . ' to use the TYPO3 API.', E_USER_DEPRECATED);
90 }
91
92 // Check if the router has the available route and dispatch.
93 try {
94 return $this->dispatch($request);
95 } catch (InvalidRequestTokenException $e) {
96 // When token was invalid redirect to login
97 $url = GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . TYPO3_mainDir;
98 return new RedirectResponse($url);
99 }
100 }
101
102 /**
103 * Does the main work for setting up the backend environment for any Backend request
104 *
105 * @param bool $proceedIfNoUserIsLoggedIn option to allow to render the request even if no user is logged in
106 */
107 protected function boot(bool $proceedIfNoUserIsLoggedIn)
108 {
109 $this->bootstrap
110 ->checkLockedBackendAndRedirectOrDie()
111 ->checkBackendIpOrDie()
112 ->checkSslBackendAndRedirectIfNeeded()
113 ->initializeBackendRouter()
114 ->loadExtTables()
115 ->initializeBackendUser()
116 ->initializeBackendAuthentication($proceedIfNoUserIsLoggedIn)
117 ->initializeLanguageObject()
118 ->initializeBackendTemplate()
119 ->endOutputBufferingAndCleanPreviousOutput()
120 ->initializeOutputCompression()
121 ->sendHttpHeaders();
122 }
123
124 /**
125 * This request handler can handle any backend request (but not CLI).
126 *
127 * @param ServerRequestInterface $request
128 * @return bool If the request is not a CLI script, TRUE otherwise FALSE
129 */
130 public function canHandleRequest(ServerRequestInterface $request): bool
131 {
132 return TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_BE && !(TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI);
133 }
134
135 /**
136 * Returns the priority - how eager the handler is to actually handle the
137 * request.
138 *
139 * @return int The priority of the request handler.
140 */
141 public function getPriority(): int
142 {
143 return 50;
144 }
145
146 /**
147 * Dispatch the request to the appropriate controller through the Backend Dispatcher which resolves the routing
148 *
149 * @param ServerRequestInterface $request
150 * @return ResponseInterface
151 * @throws InvalidRequestTokenException if the request could not be verified
152 * @throws \InvalidArgumentException when a route is found but the target of the route cannot be called
153 */
154 protected function dispatch(ServerRequestInterface $request): ResponseInterface
155 {
156 /** @var Response $response */
157 $response = GeneralUtility::makeInstance(Response::class);
158 /** @var RouteDispatcher $dispatcher */
159 $dispatcher = GeneralUtility::makeInstance(RouteDispatcher::class);
160 return $dispatcher->dispatch($request, $response);
161 }
162 }