‪TYPO3CMS  ‪main
Maintenance.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\Container\ContainerInterface;
21 use Psr\Http\Message\ResponseInterface;
22 use Psr\Http\Message\ServerRequestInterface;
23 use Psr\Http\Server\MiddlewareInterface;
24 use Psr\Http\Server\RequestHandlerInterface;
25 use TYPO3\CMS\Core\Configuration\ConfigurationManager;
47 use TYPO3\CMS\Install\Service\SessionService;
48 
54 class ‪Maintenance implements MiddlewareInterface
55 {
59  protected array ‪$controllers = [
60  'icon' => IconController::class,
61  'layout' => LayoutController::class,
62  'login' => LoginController::class,
63  'maintenance' => MaintenanceController::class,
64  'settings' => SettingsController::class,
65  'upgrade' => UpgradeController::class,
66  'environment' => EnvironmentController::class,
67  ];
68 
69  public function ‪__construct(
70  protected readonly ‪FailsafePackageManager $packageManager,
71  protected readonly ConfigurationManager $configurationManager,
72  protected readonly ‪PasswordHashFactory $passwordHashFactory,
73  protected readonly ContainerInterface $container,
74  protected readonly ‪FormProtectionFactory $formProtectionFactory,
75  protected readonly SessionService $sessionService
76  ) {}
77 
81  public function ‪process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
82  {
83  if (!$this->‪canHandleRequest()) {
84  return $handler->handle($request);
85  }
86 
87  if ((‪$GLOBALS['TYPO3_CONF_VARS']['BE']['installToolPassword'] ?? '') === '') {
88  return new ‪HtmlResponse('$GLOBALS[\'TYPO3_CONF_VARS\'][\'BE\'][\'installToolPassword\'] must not be empty.', 500);
89  }
90 
91  $controllerName = $request->getQueryParams()['install']['controller'] ?? 'layout';
92  $actionName = $request->getParsedBody()['install']['action'] ?? $request->getQueryParams()['install']['action'] ?? 'init';
93 
94  if ($actionName === 'showEnableInstallToolFile' && ‪EnableFileService::isInstallToolEnableFilePermanent()) {
95  $actionName = 'showLogin';
96  }
97 
98  $action = $actionName . 'Action';
99 
100  // not session related actions
101  if ($actionName === 'init') {
102  $controller = $this->container->get(LayoutController::class);
103  return $controller->initAction($request);
104  }
105  if ($actionName === 'checkEnableInstallToolFile') {
106  return new ‪JsonResponse([
107  'success' => $this->‪checkEnableInstallToolFile(),
108  ]);
109  }
110  if ($actionName === 'showEnableInstallToolFile') {
111  $controller = $this->container->get(LoginController::class);
112  return $controller->showEnableInstallToolFileAction($request);
113  }
114  if ($actionName === 'showLogin') {
115  if (!$this->‪checkEnableInstallToolFile()) {
116  throw new \RuntimeException('Not authorized', 1505564888);
117  }
118  $controller = $this->container->get(LoginController::class);
119  return $controller->showLoginAction($request);
120  }
121 
122  $this->sessionService->installSessionHandler();
123  // the backend user has an active session but the admin / maintainer
124  // rights have been revoked or the user was disabled or deleted in the meantime
125  if ($this->sessionService->isAuthorizedBackendUserSession($request) && !$this->sessionService->hasActiveBackendUserRoleAndSession()) {
126  // log out the user and destroy the session
127  $this->sessionService->resetSession();
128  $this->sessionService->destroySession($request);
129  $formProtection = $this->formProtectionFactory->createFromRequest($request);
130  $formProtection->clean();
131 
132  return new ‪HtmlResponse('', 403);
133  }
134 
135  if ($actionName === 'preAccessCheck') {
136  $response = new ‪JsonResponse([
137  'installToolLocked' => !$this->‪checkEnableInstallToolFile(),
138  'isAuthorized' => $this->sessionService->isAuthorized($request),
139  ]);
140  } elseif ($actionName === 'checkLogin') {
141  if (!$this->‪checkEnableInstallToolFile() && !$this->sessionService->isAuthorizedBackendUserSession($request)) {
142  throw new \RuntimeException('Not authorized', 1505563556);
143  }
144  if ($this->sessionService->isAuthorized($request)) {
145  $this->sessionService->refreshSession();
146  $response = new ‪JsonResponse([
147  'success' => true,
148  ]);
149  } else {
150  // Session expired, log out user, start new session
151  $this->sessionService->resetSession();
152  $this->sessionService->startSession();
153  $response = new ‪JsonResponse([
154  'success' => false,
155  ]);
156  }
157  } elseif ($actionName === 'login') {
158  $this->sessionService->initializeSession();
159  if (!$this->‪checkEnableInstallToolFile()) {
160  throw new \RuntimeException('Not authorized', 1505567462);
161  }
162  $this->‪checkSessionToken($request);
163  $this->‪checkSessionLifetime($request);
164  $password = $request->getParsedBody()['install']['password'] ?? null;
165  $authService = $this->container->get(AuthenticationService::class);
166  if ($authService->loginWithPassword($password, $request, $this->sessionService)) {
167  $response = new ‪JsonResponse([
168  'success' => true,
169  ]);
170  } else {
171  if ($password === null || empty($password)) {
172  $messageQueue = new ‪FlashMessageQueue('install');
173  $messageQueue->enqueue(
174  new ‪FlashMessage('Please enter the install tool password', '', ContextualFeedbackSeverity::ERROR)
175  );
176  } else {
177  $hashInstance = $this->passwordHashFactory->getDefaultHashInstance('BE');
178  $hashedPassword = $hashInstance->getHashedPassword($password);
179  $messageQueue = new ‪FlashMessageQueue('install');
180  $messageQueue->enqueue(
181  new ‪FlashMessage(
182  'Given password does not match the install tool login password. Calculated hash: ' . $hashedPassword,
183  '',
184  ContextualFeedbackSeverity::ERROR
185  )
186  );
187  }
188  $response = new ‪JsonResponse([
189  'success' => false,
190  'status' => $messageQueue,
191  ]);
192  }
193  } elseif ($actionName === 'logout') {
196  }
197  $formProtection = $this->formProtectionFactory->createFromRequest($request);
198  $formProtection->clean();
199  $this->sessionService->destroySession($request);
200  $response = new ‪JsonResponse([
201  'success' => true,
202  ]);
203  } else {
204  $enforceReferrerResponse = $this->‪enforceReferrer($request);
205  if ($enforceReferrerResponse !== null) {
206  return $enforceReferrerResponse;
207  }
208  $this->sessionService->initializeSession();
209  if (
210  !$this->‪checkSessionToken($request)
211  || !$this->‪checkSessionLifetime($request)
212  || !$this->sessionService->isAuthorized($request)
213  ) {
214  return new ‪HtmlResponse('', 403);
215  }
216  $this->sessionService->refreshSession();
217  if (!array_key_exists($controllerName, $this->controllers)) {
218  throw new \RuntimeException(
219  'Unknown controller ' . $controllerName,
220  1505215756
221  );
222  }
223  $this->packageManager->recreatePackageStatesFileIfMissing();
224  $className = $this->controllers[$controllerName];
226  $controller = $this->container->get($className);
227  if (!method_exists($controller, $action)) {
228  throw new \RuntimeException(
229  'Unknown action method ' . $action . ' in controller ' . $controllerName,
230  1505216027
231  );
232  }
233  $response = $controller->$action($request);
234  }
235 
236  return $response;
237  }
238 
242  protected function ‪canHandleRequest(): bool
243  {
245  if (!$basicIntegrity) {
246  return false;
247  }
248  return true;
249  }
250 
254  protected function ‪checkEnableInstallToolFile(): bool
255  {
257  }
258 
262  protected function ‪checkSessionToken(ServerRequestInterface $request): bool
263  {
264  $postValues = $request->getParsedBody()['install'] ?? null;
265  // no post data is there, so no token check necessary
266  if (empty($postValues)) {
267  return true;
268  }
269  $tokenOk = false;
270  // A token must be given as soon as there is POST data
271  if (isset($postValues['token'])) {
272  $formProtection = $this->formProtectionFactory->createFromRequest($request);
273  $action = (string)$postValues['action'];
274  if ($action === '') {
275  throw new \RuntimeException(
276  'No POST action given for token check',
277  1369326593
278  );
279  }
280  $tokenOk = $formProtection->validateToken($postValues['token'], 'installTool', $action);
281  }
282  if (!$tokenOk) {
283  $this->sessionService->resetSession();
284  $this->sessionService->startSession();
285  }
286  return $tokenOk;
287  }
288 
295  protected function ‪checkSessionLifetime(ServerRequestInterface $request): bool
296  {
297  $isExpired = $this->sessionService->isExpired($request);
298  if ($isExpired) {
299  // Session expired, log out user, start new session
300  $this->sessionService->resetSession();
301  $this->sessionService->startSession();
302  }
303  return !$isExpired;
304  }
305 
311  protected function ‪checkIfEssentialConfigurationExists(): bool
312  {
313  if (file_exists($this->configurationManager->getSystemConfigurationFileLocation())) {
314  return true;
315  }
316  // Check can be removed with TYPO3 v14.0
317  if (file_exists($this->configurationManager->getLocalConfigurationFileLocation())) {
318  mkdir(dirname($this->configurationManager->getSystemConfigurationFileLocation()), 02775, true);
319  rename($this->configurationManager->getLocalConfigurationFileLocation(), $this->configurationManager->getSystemConfigurationFileLocation());
320  if (file_exists(‪Environment::getLegacyConfigPath() . '/AdditionalConfiguration.php')) {
321  rename(‪Environment::getLegacyConfigPath() . '/AdditionalConfiguration.php', $this->configurationManager->getAdditionalConfigurationFileLocation());
322  }
323 
324  return file_exists($this->configurationManager->getSystemConfigurationFileLocation());
325  }
326  return false;
327  }
328 
334  protected function ‪enforceReferrer(ServerRequestInterface $request): ?ResponseInterface
335  {
336  if (!(new ‪Features())->isFeatureEnabled('security.backend.enforceReferrer')) {
337  return null;
338  }
339  return (new ‪ReferrerEnforcer($request))->handle([
340  'flags' => ['refresh-always'],
341  'subject' => 'Install Tool',
342  ]);
343  }
344 }
‪TYPO3\CMS\Core\Http\Security\ReferrerEnforcer
Definition: ReferrerEnforcer.php:32
‪TYPO3\CMS\Install\Middleware\Maintenance\enforceReferrer
‪enforceReferrer(ServerRequestInterface $request)
Definition: Maintenance.php:334
‪TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashFactory
Definition: PasswordHashFactory.php:27
‪TYPO3\CMS\Install\Service\EnableFileService\checkInstallToolEnableFile
‪static checkInstallToolEnableFile()
Definition: EnableFileService.php:103
‪TYPO3\CMS\Core\Package\FailsafePackageManager
Definition: FailsafePackageManager.php:27
‪TYPO3\CMS\Install\Middleware\Maintenance
Definition: Maintenance.php:55
‪TYPO3\CMS\Install\Controller\UpgradeController
Definition: UpgradeController.php:84
‪TYPO3\CMS\Install\Middleware\Maintenance\checkSessionLifetime
‪bool checkSessionLifetime(ServerRequestInterface $request)
Definition: Maintenance.php:295
‪TYPO3\CMS\Install\Controller\EnvironmentController
Definition: EnvironmentController.php:55
‪TYPO3\CMS\Install\Service\EnableFileService
Definition: EnableFileService.php:26
‪TYPO3\CMS\Install\Middleware\Maintenance\__construct
‪__construct(protected readonly FailsafePackageManager $packageManager, protected readonly ConfigurationManager $configurationManager, protected readonly PasswordHashFactory $passwordHashFactory, protected readonly ContainerInterface $container, protected readonly FormProtectionFactory $formProtectionFactory, protected readonly SessionService $sessionService)
Definition: Maintenance.php:69
‪TYPO3\CMS\Core\Core\Environment\getLegacyConfigPath
‪static getLegacyConfigPath()
Definition: Environment.php:268
‪TYPO3\CMS\Install\Controller\IconController
Definition: IconController.php:32
‪TYPO3\CMS\Install\Middleware\Maintenance\checkSessionToken
‪checkSessionToken(ServerRequestInterface $request)
Definition: Maintenance.php:262
‪TYPO3\CMS\Core\Type\ContextualFeedbackSeverity
‪ContextualFeedbackSeverity
Definition: ContextualFeedbackSeverity.php:25
‪TYPO3\CMS\Install\Service\EnableFileService\installToolEnableFileExists
‪static installToolEnableFileExists()
Definition: EnableFileService.php:95
‪TYPO3\CMS\Install\Controller\LoginController
Definition: LoginController.php:32
‪TYPO3\CMS\Install\Middleware\Maintenance\process
‪process(ServerRequestInterface $request, RequestHandlerInterface $handler)
Definition: Maintenance.php:81
‪TYPO3\CMS\Core\Configuration\Features
Definition: Features.php:56
‪TYPO3\CMS\Install\Middleware\Maintenance\$controllers
‪array $controllers
Definition: Maintenance.php:59
‪TYPO3\CMS\Install\Service\EnableFileService\isInstallToolEnableFilePermanent
‪static isInstallToolEnableFilePermanent()
Definition: EnableFileService.php:121
‪TYPO3\CMS\Install\Controller\LayoutController
Definition: LayoutController.php:48
‪TYPO3\CMS\Install\Middleware\Maintenance\checkEnableInstallToolFile
‪checkEnableInstallToolFile()
Definition: Maintenance.php:254
‪TYPO3\CMS\Install\Service\EnableFileService\removeInstallToolEnableFile
‪static removeInstallToolEnableFile()
Definition: EnableFileService.php:70
‪TYPO3\CMS\Core\Messaging\FlashMessage
Definition: FlashMessage.php:27
‪TYPO3\CMS\Core\FormProtection\FormProtectionFactory
Definition: FormProtectionFactory.php:43
‪TYPO3\CMS\Install\Middleware\Maintenance\canHandleRequest
‪canHandleRequest()
Definition: Maintenance.php:242
‪TYPO3\CMS\Install\Service\EnableFileService\isFirstInstallAllowed
‪static isFirstInstallAllowed()
Definition: EnableFileService.php:42
‪TYPO3\CMS\Core\Http\JsonResponse
Definition: JsonResponse.php:28
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Install\Controller\MaintenanceController
Definition: MaintenanceController.php:54
‪TYPO3\CMS\Install\Controller\AbstractController
Definition: AbstractController.php:35
‪TYPO3\CMS\Install\Middleware\Maintenance\checkIfEssentialConfigurationExists
‪bool checkIfEssentialConfigurationExists()
Definition: Maintenance.php:311
‪TYPO3\CMS\Install\Authentication\AuthenticationService
Definition: AuthenticationService.php:39
‪TYPO3\CMS\Install\Controller\SettingsController
Definition: SettingsController.php:53
‪TYPO3\CMS\Install\Middleware
Definition: Installer.php:18
‪TYPO3\CMS\Core\Messaging\FlashMessageQueue
Definition: FlashMessageQueue.php:29
‪TYPO3\CMS\Core\Http\HtmlResponse
Definition: HtmlResponse.php:28