[TASK] Bootstrap: Load TCA configuration on every request
[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 NULL|\Psr\Http\Message\ResponseInterface
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 ->loadExtTables()
102 ->initializeBackendUser()
103 ->initializeBackendAuthentication()
104 ->initializeLanguageObject()
105 ->initializeBackendTemplate()
106 ->endOutputBufferingAndCleanPreviousOutput()
107 ->initializeOutputCompression()
108 ->sendHttpHeaders();
109 }
110
111 /**
112 * This request handler can handle any backend request coming from index.php
113 *
114 * @param ServerRequestInterface $request
115 * @return bool
116 */
117 public function canHandleRequest(ServerRequestInterface $request)
118 {
119 return $request->getAttribute('isModuleRequest', false);
120 }
121
122 /**
123 * Checks if all parameters are met.
124 *
125 * @return bool
126 */
127 protected function isValidModuleRequest()
128 {
129 return $this->getFormProtection() instanceof BackendFormProtection
130 && $this->getFormProtection()->validateToken((string)$this->request->getQueryParams()['moduleToken'], 'moduleCall', (string)$this->request->getQueryParams()['M']);
131 }
132
133 /**
134 * Executes the modules configured via Extbase
135 *
136 * @param string $moduleName
137 * @return Response A PSR-7 response object
138 * @throws \RuntimeException
139 */
140 protected function dispatchModule($moduleName)
141 {
142 $moduleConfiguration = $this->getModuleConfiguration($moduleName);
143
144 /** @var Response $response */
145 $response = GeneralUtility::makeInstance(Response::class);
146
147 // Check permissions and exit if the user has no permission for entry
148 $this->backendUserAuthentication->modAccess($moduleConfiguration, true);
149 $id = isset($this->request->getQueryParams()['id']) ? $this->request->getQueryParams()['id'] : $this->request->getParsedBody()['id'];
150 if ($id && MathUtility::canBeInterpretedAsInteger($id)) {
151 $permClause = $this->backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
152 // Check page access
153 $access = is_array(BackendUtility::readPageAccess((int)$id, $permClause));
154 if (!$access) {
155 // Check if page has been deleted
156 $deleteField = $GLOBALS['TCA']['pages']['ctrl']['delete'];
157 $pageInfo = BackendUtility::getRecord('pages', (int)$id, $deleteField, $permClause ? ' AND ' . $permClause : '', false);
158 if (!$pageInfo[$deleteField]) {
159 throw new \RuntimeException('You don\'t have access to this page', 1289917924);
160 }
161 }
162 }
163
164 // Use Core Dispatching
165 if (isset($moduleConfiguration['routeTarget'])) {
166 $dispatcher = GeneralUtility::makeInstance(Dispatcher::class);
167 $this->request = $this->request->withAttribute('target', $moduleConfiguration['routeTarget']);
168 $response = $dispatcher->dispatch($this->request, $response);
169 } else {
170 // extbase module
171 $configuration = [
172 'extensionName' => $moduleConfiguration['extensionName'],
173 'pluginName' => $moduleName
174 ];
175 if (isset($moduleConfiguration['vendorName'])) {
176 $configuration['vendorName'] = $moduleConfiguration['vendorName'];
177 }
178
179 // Run Extbase
180 $bootstrap = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Core\Bootstrap::class);
181 $content = $bootstrap->run('', $configuration);
182
183 $response->getBody()->write($content);
184 }
185
186 return $response;
187 }
188
189 /**
190 * Returns the module configuration which is provided during module registration
191 *
192 * @param string $moduleName
193 * @return array
194 * @throws \RuntimeException
195 */
196 protected function getModuleConfiguration($moduleName)
197 {
198 if (!isset($this->moduleRegistry['_configuration'][$moduleName])) {
199 throw new \RuntimeException('Module ' . $moduleName . ' is not configured.', 1289918325);
200 }
201 return $this->moduleRegistry['_configuration'][$moduleName];
202 }
203
204 /**
205 * Returns the priority - how eager the handler is to actually handle the request.
206 *
207 * @return int The priority of the request handler.
208 */
209 public function getPriority()
210 {
211 return 90;
212 }
213
214 /**
215 * Wrapper method for static form protection utility
216 *
217 * @return \TYPO3\CMS\Core\FormProtection\AbstractFormProtection
218 */
219 protected function getFormProtection()
220 {
221 return FormProtectionFactory::get();
222 }
223 }