[!!!][TASK] Remove path-based backend module registration
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Http / BackendModuleRequestHandler.php
1 <?php
2 namespace TYPO3\CMS\Backend\Http;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use Psr\Http\Message\ServerRequestInterface;
18 use TYPO3\CMS\Backend\Utility\BackendUtility;
19 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
20 use TYPO3\CMS\Core\Core\Bootstrap;
21 use TYPO3\CMS\Core\Exception;
22 use TYPO3\CMS\Core\FormProtection\BackendFormProtection;
23 use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
24 use TYPO3\CMS\Core\Http\Dispatcher;
25 use TYPO3\CMS\Core\Http\RequestHandlerInterface;
26 use TYPO3\CMS\Core\Http\Response;
27 use TYPO3\CMS\Core\Utility\GeneralUtility;
28 use TYPO3\CMS\Core\Utility\MathUtility;
29
30 /**
31 * Handles the request for backend modules and wizards
32 * Juggles with $GLOBALS['TBE_MODULES']
33 */
34 class BackendModuleRequestHandler implements RequestHandlerInterface
35 {
36 /**
37 * @var Bootstrap
38 */
39 protected $bootstrap;
40
41 /**
42 * @var array
43 */
44 protected $moduleRegistry = array();
45
46 /**
47 * @var BackendUserAuthentication
48 */
49 protected $backendUserAuthentication;
50
51 /**
52 * Instance of the current Http Request
53 * @var ServerRequestInterface
54 */
55 protected $request;
56
57 /**
58 * Constructor handing over the bootstrap and the original request
59 *
60 * @param Bootstrap $bootstrap
61 */
62 public function __construct(Bootstrap $bootstrap)
63 {
64 $this->bootstrap = $bootstrap;
65 }
66
67 /**
68 * Handles the request, evaluating the configuration and executes the module accordingly
69 *
70 * @param ServerRequestInterface $request
71 * @return NULL|\Psr\Http\Message\ResponseInterface
72 * @throws Exception
73 */
74 public function handleRequest(ServerRequestInterface $request)
75 {
76 $this->request = $request;
77 $this->boot();
78
79 $this->moduleRegistry = $GLOBALS['TBE_MODULES'];
80
81 if (!$this->isValidModuleRequest()) {
82 throw new Exception('The CSRF protection token for the requested module is missing or invalid', 1417988921);
83 }
84
85 // Set to empty as it is not needed / always coming from typo3/index.php
86 $GLOBALS['BACK_PATH'] = '';
87
88 $this->backendUserAuthentication = $GLOBALS['BE_USER'];
89
90 $moduleName = (string)$this->request->getQueryParams()['M'];
91 return $this->dispatchModule($moduleName);
92 }
93
94 /**
95 * Execute TYPO3 bootstrap
96 *
97 * @return void
98 */
99 protected function boot()
100 {
101 // Evaluate the constant for skipping the BE user check for the bootstrap, will be done without the constant at a later point
102 if (defined('TYPO3_PROCEED_IF_NO_USER') && TYPO3_PROCEED_IF_NO_USER) {
103 $proceedIfNoUserIsLoggedIn = true;
104 } else {
105 $proceedIfNoUserIsLoggedIn = false;
106 }
107
108 $this->bootstrap->checkLockedBackendAndRedirectOrDie()
109 ->checkBackendIpOrDie()
110 ->checkSslBackendAndRedirectIfNeeded()
111 ->initializeBackendRouter()
112 ->loadExtensionTables(true)
113 ->initializeSpriteManager()
114 ->initializeBackendUser()
115 ->initializeBackendAuthentication($proceedIfNoUserIsLoggedIn)
116 ->initializeLanguageObject()
117 ->initializeBackendTemplate()
118 ->endOutputBufferingAndCleanPreviousOutput()
119 ->initializeOutputCompression()
120 ->sendHttpHeaders();
121 }
122
123 /**
124 * This request handler can handle any backend request coming from index.php
125 *
126 * @param ServerRequestInterface $request
127 * @return bool
128 */
129 public function canHandleRequest(ServerRequestInterface $request)
130 {
131 return $request->getAttribute('isModuleRequest', false);
132 }
133
134 /**
135 * Checks if all parameters are met.
136 *
137 * @return bool
138 */
139 protected function isValidModuleRequest()
140 {
141 return $this->getFormProtection() instanceof BackendFormProtection
142 && $this->getFormProtection()->validateToken((string)$this->request->getQueryParams()['moduleToken'], 'moduleCall', (string)$this->request->getQueryParams()['M']);
143 }
144
145 /**
146 * Executes the modules configured via Extbase
147 *
148 * @param string $moduleName
149 * @return Response A PSR-7 response object
150 * @throws \RuntimeException
151 */
152 protected function dispatchModule($moduleName)
153 {
154 $moduleConfiguration = $this->getModuleConfiguration($moduleName);
155
156 // Check permissions and exit if the user has no permission for entry
157 $this->backendUserAuthentication->modAccess($moduleConfiguration, true);
158 $id = isset($this->request->getQueryParams()['id']) ? $this->request->getQueryParams()['id'] : $this->request->getParsedBody()['id'];
159 if ($id && MathUtility::canBeInterpretedAsInteger($id)) {
160 // Check page access
161 $permClause = $this->backendUserAuthentication->getPagePermsClause(true);
162 $access = is_array(BackendUtility::readPageAccess((int)$id, $permClause));
163 if (!$access) {
164 throw new \RuntimeException('You don\'t have access to this page', 1289917924);
165 }
166 }
167
168 /** @var Response $response */
169 $response = GeneralUtility::makeInstance(Response::class);
170
171 // Use Core Dispatching
172 if (isset($moduleConfiguration['routeTarget'])) {
173 $dispatcher = GeneralUtility::makeInstance(Dispatcher::class);
174 $this->request = $this->request->withAttribute('target', $moduleConfiguration['routeTarget']);
175 $response = $dispatcher->dispatch($this->request, $response);
176 } else {
177 // extbase module
178 $configuration = array(
179 'extensionName' => $moduleConfiguration['extensionName'],
180 'pluginName' => $moduleName
181 );
182 if (isset($moduleConfiguration['vendorName'])) {
183 $configuration['vendorName'] = $moduleConfiguration['vendorName'];
184 }
185
186 // Run Extbase
187 $bootstrap = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Core\Bootstrap::class);
188 $content = $bootstrap->run('', $configuration);
189
190 $response->getBody()->write($content);
191 }
192
193 return $response;
194 }
195
196 /**
197 * Returns the module configuration which is provided during module registration
198 *
199 * @param string $moduleName
200 * @return array
201 * @throws \RuntimeException
202 */
203 protected function getModuleConfiguration($moduleName)
204 {
205 if (!isset($this->moduleRegistry['_configuration'][$moduleName])) {
206 throw new \RuntimeException('Module ' . $moduleName . ' is not configured.', 1289918325);
207 }
208 return $this->moduleRegistry['_configuration'][$moduleName];
209 }
210
211 /**
212 * Returns the priority - how eager the handler is to actually handle the request.
213 *
214 * @return int The priority of the request handler.
215 */
216 public function getPriority()
217 {
218 return 90;
219 }
220
221 /**
222 * Wrapper method for static form protection utility
223 *
224 * @return \TYPO3\CMS\Core\FormProtection\AbstractFormProtection
225 */
226 protected function getFormProtection()
227 {
228 return FormProtectionFactory::get();
229 }
230 }