[SECURITY] Prevent login of restricted users
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Http / RequestHandler.php
1 <?php
2 namespace TYPO3\CMS\Frontend\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 TYPO3\CMS\Backend\FrontendBackendUserAuthentication;
18 use TYPO3\CMS\Core\Core\Bootstrap;
19 use TYPO3\CMS\Core\FrontendEditing\FrontendEditingController;
20 use TYPO3\CMS\Core\Http\RequestHandlerInterface;
21 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
22 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
23 use TYPO3\CMS\Core\Utility\GeneralUtility;
24 use TYPO3\CMS\Core\Utility\MathUtility;
25 use TYPO3\CMS\Core\Utility\MonitorUtility;
26 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
27 use TYPO3\CMS\Frontend\Page\PageGenerator;
28 use TYPO3\CMS\Frontend\Utility\CompressionUtility;
29 use TYPO3\CMS\Frontend\View\AdminPanelView;
30
31 /**
32 * This is the main entry point of the TypoScript driven standard front-end
33 *
34 * Basically put, this is the script which all requests for TYPO3 delivered pages goes to in the
35 * frontend (the website). The script instantiates a $TSFE object, includes libraries and does a little logic here
36 * and there in order to instantiate the right classes to create the webpage.
37 * Previously, this was called index_ts.php and also included the logic for the lightweight "eID" concept,
38 * which is now handled in a separate request handler (EidRequestHandler).
39 */
40 class RequestHandler implements RequestHandlerInterface
41 {
42 /**
43 * Instance of the current TYPO3 bootstrap
44 * @var Bootstrap
45 */
46 protected $bootstrap;
47
48 /**
49 * Instance of the timetracker
50 * @var TimeTracker
51 */
52 protected $timeTracker;
53
54 /**
55 * Instance of the TSFE object
56 * @var TypoScriptFrontendController
57 */
58 protected $controller;
59
60 /**
61 * The request handed over
62 * @var \Psr\Http\Message\ServerRequestInterface
63 */
64 protected $request;
65
66 /**
67 * Constructor handing over the bootstrap and the original request
68 *
69 * @param Bootstrap $bootstrap
70 */
71 public function __construct(Bootstrap $bootstrap)
72 {
73 $this->bootstrap = $bootstrap;
74 }
75
76 /**
77 * Handles a frontend request
78 *
79 * @param \Psr\Http\Message\ServerRequestInterface $request
80 * @return NULL|\Psr\Http\Message\ResponseInterface
81 */
82 public function handleRequest(\Psr\Http\Message\ServerRequestInterface $request)
83 {
84 $response = null;
85 $this->request = $request;
86 $this->initializeTimeTracker();
87
88 // Hook to preprocess the current request:
89 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['preprocessRequest'])) {
90 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['preprocessRequest'] as $hookFunction) {
91 $hookParameters = [];
92 GeneralUtility::callUserFunction($hookFunction, $hookParameters, $hookParameters);
93 }
94 unset($hookFunction);
95 unset($hookParameters);
96 }
97
98 $this->initializeController();
99
100 if ($GLOBALS['TYPO3_CONF_VARS']['FE']['pageUnavailable_force']
101 && !GeneralUtility::cmpIP(
102 GeneralUtility::getIndpEnv('REMOTE_ADDR'),
103 $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'])
104 ) {
105 $this->controller->pageUnavailableAndExit('This page is temporarily unavailable.');
106 }
107
108 $this->controller->connectToDB();
109 $this->controller->sendRedirect();
110
111 // Output compression
112 // Remove any output produced until now
113 $this->bootstrap->endOutputBufferingAndCleanPreviousOutput();
114 $this->initializeOutputCompression();
115
116 $this->bootstrap->loadBaseTca();
117
118 // Initializing the Frontend User
119 $this->timeTracker->push('Front End user initialized', '');
120 $this->controller->initFEuser();
121 $this->timeTracker->pull();
122
123 // Initializing a possible logged-in Backend User
124 /** @var $GLOBALS['BE_USER'] \TYPO3\CMS\Backend\FrontendBackendUserAuthentication */
125 $GLOBALS['BE_USER'] = $this->controller->initializeBackendUser();
126
127 // Process the ID, type and other parameters.
128 // After this point we have an array, $page in TSFE, which is the page-record
129 // of the current page, $id.
130 $this->timeTracker->push('Process ID', '');
131 // Initialize admin panel since simulation settings are required here:
132 if ($this->controller->isBackendUserLoggedIn()) {
133 $GLOBALS['BE_USER']->initializeAdminPanel();
134 $this->bootstrap
135 ->initializeBackendRouter()
136 ->loadExtTables();
137 }
138 $this->controller->checkAlternativeIdMethods();
139 $this->controller->clear_preview();
140 $this->controller->determineId();
141
142 // Now, if there is a backend user logged in and he has NO access to this page,
143 // then re-evaluate the id shown! _GP('ADMCMD_noBeUser') is placed here because
144 // \TYPO3\CMS\Version\Hook\PreviewHook might need to know if a backend user is logged in.
145 if (
146 $this->controller->isBackendUserLoggedIn()
147 && (!$GLOBALS['BE_USER']->extPageReadAccess($this->controller->page) || GeneralUtility::_GP('ADMCMD_noBeUser'))
148 ) {
149 // Remove user
150 unset($GLOBALS['BE_USER']);
151 $this->controller->beUserLogin = false;
152 // Re-evaluate the page-id.
153 $this->controller->checkAlternativeIdMethods();
154 $this->controller->clear_preview();
155 $this->controller->determineId();
156 }
157
158 $this->controller->makeCacheHash();
159 $this->timeTracker->pull();
160
161 // Admin Panel & Frontend editing
162 if ($this->controller->isBackendUserLoggedIn()) {
163 $GLOBALS['BE_USER']->initializeFrontendEdit();
164 if ($GLOBALS['BE_USER']->adminPanel instanceof AdminPanelView) {
165 $this->bootstrap->initializeLanguageObject();
166 }
167 if ($GLOBALS['BE_USER']->frontendEdit instanceof FrontendEditingController) {
168 $GLOBALS['BE_USER']->frontendEdit->initConfigOptions();
169 }
170 }
171
172 // Starts the template
173 $this->timeTracker->push('Start Template', '');
174 $this->controller->initTemplate();
175 $this->timeTracker->pull();
176 // Get from cache
177 $this->timeTracker->push('Get Page from cache', '');
178 $this->controller->getFromCache();
179 $this->timeTracker->pull();
180 // Get config if not already gotten
181 // After this, we should have a valid config-array ready
182 $this->controller->getConfigArray();
183 // Setting language and locale
184 $this->timeTracker->push('Setting language and locale', '');
185 $this->controller->settingLanguage();
186 $this->controller->settingLocale();
187 $this->timeTracker->pull();
188
189 // Convert POST data to utf-8 for internal processing if metaCharset is different
190 $this->controller->convPOSTCharset();
191
192 $this->controller->initializeRedirectUrlHandlers();
193
194 $this->controller->handleDataSubmission();
195
196 // Check for shortcut page and redirect
197 $this->controller->checkPageForShortcutRedirect();
198 $this->controller->checkPageForMountpointRedirect();
199
200 // Generate page
201 $this->controller->setUrlIdToken();
202 $this->timeTracker->push('Page generation', '');
203 if ($this->controller->isGeneratePage()) {
204 $this->controller->generatePage_preProcessing();
205 $temp_theScript = $this->controller->generatePage_whichScript();
206 if ($temp_theScript) {
207 include $temp_theScript;
208 } else {
209 $this->controller->preparePageContentGeneration();
210 // Content generation
211 if (!$this->controller->isINTincScript()) {
212 PageGenerator::renderContent();
213 $this->controller->setAbsRefPrefix();
214 }
215 }
216 $this->controller->generatePage_postProcessing();
217 } elseif ($this->controller->isINTincScript()) {
218 $this->controller->preparePageContentGeneration();
219 }
220 $this->controller->releaseLocks();
221 $this->timeTracker->pull();
222
223 // Render non-cached parts
224 if ($this->controller->isINTincScript()) {
225 $this->timeTracker->push('Non-cached objects', '');
226 $this->controller->INTincScript();
227 $this->timeTracker->pull();
228 }
229
230 // Output content
231 $sendTSFEContent = false;
232 if ($this->controller->isOutputting()) {
233 $this->timeTracker->push('Print Content', '');
234 $this->controller->processOutput();
235 $sendTSFEContent = true;
236 $this->timeTracker->pull();
237 }
238 // Store session data for fe_users
239 $this->controller->storeSessionData();
240 // Statistics
241 $GLOBALS['TYPO3_MISC']['microtime_end'] = microtime(true);
242 if ($this->controller->isOutputting()) {
243 if (isset($this->controller->config['config']['debug'])) {
244 $debugParseTime = (bool)$this->controller->config['config']['debug'];
245 } else {
246 $debugParseTime = !empty($GLOBALS['TYPO3_CONF_VARS']['FE']['debug']);
247 }
248 if ($debugParseTime) {
249 $this->controller->content .= LF . '<!-- Parsetime: ' . $this->getParseTime() . 'ms -->';
250 }
251 }
252 $this->controller->redirectToExternalUrl();
253 // Preview info
254 $this->controller->previewInfo();
255 // Hook for end-of-frontend
256 $this->controller->hook_eofe();
257 // Finish timetracking
258 $this->timeTracker->pull();
259 // Check memory usage
260 MonitorUtility::peakMemoryUsage();
261 // beLoginLinkIPList
262 echo $this->controller->beLoginLinkIPList();
263
264 // Admin panel
265 if ($this->controller->isBackendUserLoggedIn() && $GLOBALS['BE_USER'] instanceof FrontendBackendUserAuthentication) {
266 if ($GLOBALS['BE_USER']->isAdminPanelVisible()) {
267 $this->controller->content = str_ireplace('</body>', $GLOBALS['BE_USER']->displayAdminPanel() . '</body>', $this->controller->content);
268 }
269 }
270
271 if ($sendTSFEContent) {
272 /** @var \TYPO3\CMS\Core\Http\Response $response */
273 $response = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Http\Response::class);
274 $response->getBody()->write($this->controller->content);
275 }
276 // Debugging Output
277 if (isset($GLOBALS['error']) && is_object($GLOBALS['error']) && @is_callable([$GLOBALS['error'], 'debugOutput'])) {
278 $GLOBALS['error']->debugOutput();
279 }
280 if (TYPO3_DLOG) {
281 GeneralUtility::devLog('END of FRONTEND session', 'cms', 0, ['_FLUSH' => true]);
282 }
283 return $response;
284 }
285
286 /**
287 * This request handler can handle any frontend request.
288 *
289 * @param \Psr\Http\Message\ServerRequestInterface $request
290 * @return bool If the request is not an eID request, TRUE otherwise FALSE
291 */
292 public function canHandleRequest(\Psr\Http\Message\ServerRequestInterface $request)
293 {
294 return $request->getQueryParams()['eID'] || $request->getParsedBody()['eID'] ? false : true;
295 }
296
297 /**
298 * Returns the priority - how eager the handler is to actually handle the
299 * request.
300 *
301 * @return int The priority of the request handler.
302 */
303 public function getPriority()
304 {
305 return 50;
306 }
307
308 /**
309 * Initializes output compression when enabled, could be split up and put into Bootstrap
310 * at a later point
311 */
312 protected function initializeOutputCompression()
313 {
314 if ($GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'] && extension_loaded('zlib')) {
315 if (MathUtility::canBeInterpretedAsInteger($GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'])) {
316 @ini_set('zlib.output_compression_level', $GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel']);
317 }
318 ob_start([GeneralUtility::makeInstance(CompressionUtility::class), 'compressionOutputHandler']);
319 }
320 }
321
322 /**
323 * Timetracking started depending if a Backend User is logged in
324 *
325 * @return void
326 */
327 protected function initializeTimeTracker()
328 {
329 $configuredCookieName = trim($GLOBALS['TYPO3_CONF_VARS']['BE']['cookieName']) ?: 'be_typo_user';
330
331 /** @var TimeTracker timeTracker */
332 $this->timeTracker = GeneralUtility::makeInstance(TimeTracker::class, ($this->request->getCookieParams()[$configuredCookieName] ? true : false));
333 $this->timeTracker->start();
334 }
335
336 /**
337 * Creates an instance of TSFE and sets it as a global variable
338 *
339 * @return void
340 */
341 protected function initializeController()
342 {
343 $this->controller = GeneralUtility::makeInstance(
344 TypoScriptFrontendController::class,
345 null,
346 GeneralUtility::_GP('id'),
347 GeneralUtility::_GP('type'),
348 GeneralUtility::_GP('no_cache'),
349 GeneralUtility::_GP('cHash'),
350 null,
351 GeneralUtility::_GP('MP'),
352 GeneralUtility::_GP('RDCT')
353 );
354 // setting the global variable for the controller
355 // We have to define this as reference here, because there is code around
356 // which exchanges the TSFE object in the global variable. The reference ensures
357 // that the $controller member always works on the same object as the global variable.
358 // This is a dirty workaround and bypasses the protected access modifier of the controller member.
359 $GLOBALS['TSFE'] = &$this->controller;
360 }
361
362 /**
363 * Calculates the parsetime of the page and returns it.
364 *
365 * @return int the parse time of the page
366 */
367 protected function getParseTime()
368 {
369 // Compensates for the time consumed with Back end user initialization.
370 $processStart = isset($GLOBALS['TYPO3_MISC']['microtime_start']) ? $GLOBALS['TYPO3_MISC']['microtime_start'] : null;
371 $processEnd = isset($GLOBALS['TYPO3_MISC']['microtime_end']) ? $GLOBALS['TYPO3_MISC']['microtime_end'] : null;
372 $beUserInitializationStart = isset($GLOBALS['TYPO3_MISC']['microtime_BE_USER_start']) ? $GLOBALS['TYPO3_MISC']['microtime_BE_USER_start'] : null;
373 $beUserInitializationEnd = isset($GLOBALS['TYPO3_MISC']['microtime_BE_USER_end']) ? $GLOBALS['TYPO3_MISC']['microtime_BE_USER_end'] : null;
374 return $this->timeTracker->getMilliseconds($processStart)
375 - $this->timeTracker->getMilliseconds($processEnd)
376 - ($this->timeTracker->getMilliseconds($beUserInitializationStart)
377 - $this->timeTracker->getMilliseconds($beUserInitializationEnd));
378 }
379 }