[CLEANUP] Alwas put null at the last position
[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\Type\Bitmask\Permission;
28 use TYPO3\CMS\Core\Utility\GeneralUtility;
29 use TYPO3\CMS\Core\Utility\MathUtility;
30
31 /**
32 * Handles the request for backend modules and wizards
33 * Juggles with $GLOBALS['TBE_MODULES']
34 */
35 class BackendModuleRequestHandler implements RequestHandlerInterface
36 {
37 /**
38 * @var Bootstrap
39 */
40 protected $bootstrap;
41
42 /**
43 * @var array
44 */
45 protected $moduleRegistry = [];
46
47 /**
48 * @var BackendUserAuthentication
49 */
50 protected $backendUserAuthentication;
51
52 /**
53 * Instance of the current Http Request
54 * @var ServerRequestInterface
55 */
56 protected $request;
57
58 /**
59 * Constructor handing over the bootstrap and the original request
60 *
61 * @param Bootstrap $bootstrap
62 */
63 public function __construct(Bootstrap $bootstrap)
64 {
65 $this->bootstrap = $bootstrap;
66 }
67
68 /**
69 * Handles the request, evaluating the configuration and executes the module accordingly
70 *
71 * @param ServerRequestInterface $request
72 * @return \Psr\Http\Message\ResponseInterface|null
73 * @throws Exception
74 */
75 public function handleRequest(ServerRequestInterface $request)
76 {
77 $this->request = $request;
78 $this->boot();
79
80 $this->moduleRegistry = $GLOBALS['TBE_MODULES'];
81
82 if (!$this->isValidModuleRequest()) {
83 throw new Exception('The CSRF protection token for the requested module is missing or invalid', 1417988921);
84 }
85
86 $this->backendUserAuthentication = $GLOBALS['BE_USER'];
87
88 $moduleName = (string)$this->request->getQueryParams()['M'];
89 return $this->dispatchModule($moduleName);
90 }
91
92 /**
93 * Execute TYPO3 bootstrap
94 */
95 protected function boot()
96 {
97 $this->bootstrap->checkLockedBackendAndRedirectOrDie()
98 ->checkBackendIpOrDie()
99 ->checkSslBackendAndRedirectIfNeeded()
100 ->initializeBackendRouter()
101 ->loadBaseTca()
102 ->loadExtTables()
103 ->initializeBackendUser()
104 ->initializeBackendAuthentication()
105 ->initializeLanguageObject()
106 ->initializeBackendTemplate()
107 ->endOutputBufferingAndCleanPreviousOutput()
108 ->initializeOutputCompression()
109 ->sendHttpHeaders();
110 }
111
112 /**
113 * This request handler can handle any backend request coming from index.php
114 *
115 * @param ServerRequestInterface $request
116 * @return bool
117 */
118 public function canHandleRequest(ServerRequestInterface $request)
119 {
120 return $request->getAttribute('isModuleRequest', false);
121 }
122
123 /**
124 * Checks if all parameters are met.
125 *
126 * @return bool
127 */
128 protected function isValidModuleRequest()
129 {
130 return $this->getFormProtection() instanceof BackendFormProtection
131 && $this->getFormProtection()->validateToken((string)$this->request->getQueryParams()['moduleToken'], 'moduleCall', (string)$this->request->getQueryParams()['M']);
132 }
133
134 /**
135 * Executes the modules configured via Extbase
136 *
137 * @param string $moduleName
138 * @return Response A PSR-7 response object
139 * @throws \RuntimeException
140 */
141 protected function dispatchModule($moduleName)
142 {
143 $moduleConfiguration = $this->getModuleConfiguration($moduleName);
144
145 /** @var Response $response */
146 $response = GeneralUtility::makeInstance(Response::class);
147
148 // Check permissions and exit if the user has no permission for entry
149 $this->backendUserAuthentication->modAccess($moduleConfiguration, true);
150 $id = isset($this->request->getQueryParams()['id']) ? $this->request->getQueryParams()['id'] : $this->request->getParsedBody()['id'];
151 if ($id && MathUtility::canBeInterpretedAsInteger($id)) {
152 $permClause = $this->backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
153 // Check page access
154 $access = is_array(BackendUtility::readPageAccess((int)$id, $permClause));
155 if (!$access) {
156 // Check if page has been deleted
157 $deleteField = $GLOBALS['TCA']['pages']['ctrl']['delete'];
158 $pageInfo = BackendUtility::getRecord('pages', (int)$id, $deleteField, $permClause ? ' AND ' . $permClause : '', false);
159 if (!$pageInfo[$deleteField]) {
160 throw new \RuntimeException('You don\'t have access to this page', 1289917924);
161 }
162 }
163 }
164
165 // Use Core Dispatching
166 if (isset($moduleConfiguration['routeTarget'])) {
167 $dispatcher = GeneralUtility::makeInstance(Dispatcher::class);
168 $this->request = $this->request->withAttribute('target', $moduleConfiguration['routeTarget']);
169 $response = $dispatcher->dispatch($this->request, $response);
170 } else {
171 // extbase module
172 $configuration = [
173 'extensionName' => $moduleConfiguration['extensionName'],
174 'pluginName' => $moduleName
175 ];
176 if (isset($moduleConfiguration['vendorName'])) {
177 $configuration['vendorName'] = $moduleConfiguration['vendorName'];
178 }
179
180 // Run Extbase
181 $bootstrap = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Core\Bootstrap::class);
182 $content = $bootstrap->run('', $configuration);
183
184 $response->getBody()->write($content);
185 }
186
187 return $response;
188 }
189
190 /**
191 * Returns the module configuration which is provided during module registration
192 *
193 * @param string $moduleName
194 * @return array
195 * @throws \RuntimeException
196 */
197 protected function getModuleConfiguration($moduleName)
198 {
199 if (!isset($this->moduleRegistry['_configuration'][$moduleName])) {
200 throw new \RuntimeException('Module ' . $moduleName . ' is not configured.', 1289918325);
201 }
202 return $this->moduleRegistry['_configuration'][$moduleName];
203 }
204
205 /**
206 * Returns the priority - how eager the handler is to actually handle the request.
207 *
208 * @return int The priority of the request handler.
209 */
210 public function getPriority()
211 {
212 return 90;
213 }
214
215 /**
216 * Wrapper method for static form protection utility
217 *
218 * @return \TYPO3\CMS\Core\FormProtection\AbstractFormProtection
219 */
220 protected function getFormProtection()
221 {
222 return FormProtectionFactory::get();
223 }
224 }