Completed
Push — master ( e28c49...c020e9 )
by
unknown
19:48
created

Installer::throwIfInstallerIsNotAvailable()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 6
rs 10
c 0
b 0
f 0
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
18
namespace TYPO3\CMS\Install\Middleware;
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;
26
use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
27
use TYPO3\CMS\Core\FormProtection\InstallToolFormProtection;
28
use TYPO3\CMS\Core\Http\JsonResponse;
29
use TYPO3\CMS\Install\Controller\InstallerController;
30
use TYPO3\CMS\Install\Service\EnableFileService;
31
use TYPO3\CMS\Install\Service\SessionService;
32
33
/**
34
 * Middleware to walk through the web installation process of TYPO3
35
 * @internal This class is only meant to be used within EXT:install and is not part of the TYPO3 Core API.
36
 */
37
class Installer implements MiddlewareInterface
38
{
39
    /**
40
     * @var ContainerInterface
41
     */
42
    private $container;
43
44
    public function __construct(ContainerInterface $container)
45
    {
46
        $this->container = $container;
47
    }
48
49
    /**
50
     * Handles an Install Tool request when nothing is there
51
     *
52
     * @param ServerRequestInterface $request
53
     * @param RequestHandlerInterface $handler
54
     * @return ResponseInterface
55
     * @throws \RuntimeException
56
     */
57
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
58
    {
59
        if (!$this->canHandleRequest($request)) {
60
            return $handler->handle($request);
61
        }
62
63
        // Lazy load InstallerController, to instantiate the class and the dependencies only if we handle an install request.
64
        $controller = $this->container->get(InstallerController::class);
65
        $actionName = $request->getParsedBody()['install']['action'] ?? $request->getQueryParams()['install']['action'] ?? 'init';
66
        $action = $actionName . 'Action';
67
68
        if ($actionName === 'init' || $actionName === 'mainLayout') {
69
            $response = $controller->$action();
70
        } elseif ($actionName === 'checkInstallerAvailable') {
71
            $response = new JsonResponse([
72
                'success' => $this->isInstallerAvailable(),
73
            ]);
74
        } elseif ($actionName === 'showInstallerNotAvailable') {
75
            $response = $controller->showInstallerNotAvailableAction();
76
        } elseif ($actionName === 'checkEnvironmentAndFolders'
77
            || $actionName === 'showEnvironmentAndFolders'
78
            || $actionName === 'executeEnvironmentAndFolders'
79
        ) {
80
            $this->throwIfInstallerIsNotAvailable();
81
            $response = $controller->$action($request);
82
        } else {
83
            $this->throwIfInstallerIsNotAvailable();
84
            // With main folder layout available, sessions can be handled
85
            $session = new SessionService();
86
            if (!$session->hasSession()) {
87
                $session->startSession();
88
            }
89
            if ($session->isExpired()) {
90
                $session->refreshSession();
91
            }
92
            $postValues = $request->getParsedBody()['install'];
93
            $sessionTokenOk = false;
94
            if (empty($postValues)) {
95
                // No post data is there, no token check necessary
96
                $sessionTokenOk = true;
97
            }
98
            if (isset($postValues['token'])) {
99
                // A token must be given as soon as there is POST data
100
                $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
101
                if ($actionName === '') {
102
                    throw new \RuntimeException('No POST action given for token check', 1505647681);
103
                }
104
                $sessionTokenOk = $formProtection->validateToken($postValues['token'], 'installTool', $actionName);
105
            }
106
            if (!$sessionTokenOk) {
107
                $session->resetSession();
108
                $session->startSession();
109
                throw new \RuntimeException('Invalid session token', 1505647737);
110
            }
111
112
            if (!method_exists($controller, $action)) {
113
                // Sanitize action method, preventing injecting whatever method name
114
                throw new \RuntimeException(
115
                    'Unknown action method ' . $action . ' in controller InstallerController',
116
                    1505687700
117
                );
118
            }
119
120
            $response = $controller->$action($request);
121
122
            if ($actionName === 'executeDefaultConfiguration') {
123
                // Executing last step cleans session
124
                $session->destroySession();
125
            }
126
        }
127
128
        return $response;
129
    }
130
131
    /**
132
     * First installation is in progress, if LocalConfiguration does not exist,
133
     * or if FIRST_INSTALL file exists.
134
     *
135
     * @param ServerRequestInterface $request
136
     * @return bool Returns always TRUE
137
     */
138
    protected function canHandleRequest(ServerRequestInterface $request): bool
139
    {
140
        $localConfigurationFileLocation = (new ConfigurationManager())->getLocalConfigurationFileLocation();
141
        return !@is_file($localConfigurationFileLocation) || EnableFileService::isFirstInstallAllowed();
142
    }
143
144
    /**
145
     * @throws \RuntimeException If installer is not available due to missing FIRST_INSTALL
146
     */
147
    protected function throwIfInstallerIsNotAvailable()
148
    {
149
        if (!$this->isInstallerAvailable()) {
150
            throw new \RuntimeException(
151
                'Installer not available',
152
                1505637427
153
            );
154
        }
155
    }
156
157
    /**
158
     * @return bool TRUE if FIRST_INSTALL file exists
159
     */
160
    protected function isInstallerAvailable(): bool
161
    {
162
        if (EnableFileService::isFirstInstallAllowed()) {
163
            return true;
164
        }
165
        return false;
166
    }
167
}
168