f88dd4b5755ed48d009cf07ffec0dc43767dbf33
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Middleware / PageArgumentValidator.php
1 <?php
2 declare(strict_types = 1);
3
4 namespace TYPO3\CMS\Frontend\Middleware;
5
6 /*
7 * This file is part of the TYPO3 CMS project.
8 *
9 * It is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License, either version 2
11 * of the License, or any later version.
12 *
13 * For the full copyright and license information, please read the
14 * LICENSE.txt file that was distributed with this source code.
15 *
16 * The TYPO3 project - inspiring people to share!
17 */
18
19 use Psr\Http\Message\ResponseInterface;
20 use Psr\Http\Message\ServerRequestInterface;
21 use Psr\Http\Server\MiddlewareInterface;
22 use Psr\Http\Server\RequestHandlerInterface;
23 use TYPO3\CMS\Core\Routing\PageArguments;
24 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
25 use TYPO3\CMS\Core\Utility\GeneralUtility;
26 use TYPO3\CMS\Frontend\Controller\ErrorController;
27 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
28 use TYPO3\CMS\Frontend\Page\CacheHashCalculator;
29 use TYPO3\CMS\Frontend\Page\PageAccessFailureReasons;
30
31 /**
32 * This middleware validates given request parameters against the common "cHash" functionality.
33 */
34 class PageArgumentValidator implements MiddlewareInterface
35 {
36
37 /**
38 * The cHash Service class used for cHash related functionality
39 *
40 * @var CacheHashCalculator
41 */
42 protected $cacheHashCalculator;
43
44 /**
45 * @var TypoScriptFrontendController
46 */
47 protected $controller;
48
49 /**
50 * @param TypoScriptFrontendController|null $controller
51 */
52 public function __construct(TypoScriptFrontendController $controller = null)
53 {
54 $this->controller = $controller ?? $GLOBALS['TSFE'];
55 $this->cacheHashCalculator = GeneralUtility::makeInstance(CacheHashCalculator::class);
56 }
57
58 /**
59 * Validates the &cHash parameter against the other $queryParameters / GET parameters
60 *
61 * @param ServerRequestInterface $request
62 * @param RequestHandlerInterface $handler
63 * @return ResponseInterface
64 */
65 public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
66 {
67 $pageNotFoundOnValidationError = (bool)($GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFoundOnCHashError'] ?? true);
68 $pageArguments = $request->getAttribute('routing', null);
69 if ($pageArguments instanceof PageArguments) {
70 $this->controller->setPageArguments($pageArguments);
71 }
72 if ($this->controller->no_cache && !$pageNotFoundOnValidationError) {
73 // No need to test anything if caching was already disabled.
74 } else {
75 // Evaluate the cache hash parameter or dynamic arguments when coming from a Site-based routing
76 if ($pageArguments instanceof PageArguments) {
77 $queryParams = $pageArguments->getDynamicArguments();
78 } else {
79 $queryParams = $request->getQueryParams();
80 }
81 if (!empty($queryParams) && !$this->evaluateCacheHashParameter($queryParams, $pageNotFoundOnValidationError)) {
82 return GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
83 $request,
84 'Request parameters could not be validated (&cHash comparison failed)',
85 ['code' => PageAccessFailureReasons::CACHEHASH_COMPARISON_FAILED]
86 );
87 }
88 }
89 return $handler->handle($request);
90 }
91
92 /**
93 * Calculates a hash string based on additional parameters in the url.
94 *
95 * Calculated hash is stored in $this->controller->cHash_array.
96 * This is used to cache pages with more parameters than just id and type.
97 *
98 * @see TypoScriptFrontendController::reqCHash()
99 * @param array $queryParams GET parameters
100 * @param bool $pageNotFoundOnCacheHashError see $GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFoundOnCHashError']
101 * @return bool if false, then a PageNotFound response is triggered
102 */
103 protected function evaluateCacheHashParameter(array $queryParams, bool $pageNotFoundOnCacheHashError): bool
104 {
105 if ($this->controller->cHash) {
106 // Make sure we use the page uid and not the page alias
107 $queryParams['id'] = $this->controller->id;
108 $this->controller->cHash_array = $this->cacheHashCalculator->getRelevantParameters(GeneralUtility::implodeArrayForUrl('', $queryParams));
109 $cHash_calc = $this->cacheHashCalculator->calculateCacheHash($this->controller->cHash_array);
110 if (!hash_equals($cHash_calc, $this->controller->cHash)) {
111 // Early return to trigger the error controller
112 if ($pageNotFoundOnCacheHashError) {
113 return false;
114 }
115 $this->controller->no_cache = true;
116 $this->getTimeTracker()->setTSlogMessage('The incoming cHash "' . $this->controller->cHash . '" and calculated cHash "' . $cHash_calc . '" did not match, so caching was disabled. The fieldlist used was "' . implode(',', array_keys($this->controller->cHash_array)) . '"', 2);
117 }
118 // No cHash is set, check if that is correct
119 } elseif ($this->cacheHashCalculator->doParametersRequireCacheHash(GeneralUtility::implodeArrayForUrl('', $queryParams))) {
120 // Will disable caching
121 $this->controller->reqCHash();
122 }
123 return true;
124 }
125
126 /**
127 * @return TimeTracker
128 */
129 protected function getTimeTracker(): TimeTracker
130 {
131 return GeneralUtility::makeInstance(TimeTracker::class);
132 }
133 }