[FEATURE] Adjust trustedHostsPattern during installation
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Controller / StepController.php
1 <?php
2 namespace TYPO3\CMS\Install\Controller;
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\Core\Utility\GeneralUtility;
18 use TYPO3\CMS\Install\Service\SilentConfigurationUpgradeService;
19
20 /**
21 * Install step controller, dispatcher class of step actions.
22 */
23 class StepController extends AbstractController {
24
25 /**
26 * @var array List of valid action names that need authentication. Order is important!
27 */
28 protected $authenticationActions = array(
29 'environmentAndFolders',
30 'databaseConnect',
31 'databaseSelect',
32 'databaseData',
33 'defaultConfiguration',
34 );
35
36 /**
37 * Index action acts a a dispatcher to different steps
38 *
39 * Warning: Order of these methods is security relevant and interferes with different access
40 * conditions (new/existing installation). See the single method comments for details.
41 *
42 * @throws Exception
43 * @return void
44 */
45 public function execute() {
46 $this->loadBaseExtensions();
47 $this->initializeObjectManager();
48
49 $this->outputInstallToolNotEnabledMessageIfNeeded();
50 $this->outputInstallToolPasswordNotSetMessageIfNeeded();
51 $this->recreatePackageStatesFileIfNotExisting();
52 $this->executeOrOutputFirstInstallStepIfNeeded();
53 $this->adjustTrustedHostsPatternIfNeeded();
54 $this->executeSilentConfigurationUpgradesIfNeeded();
55 $this->initializeSession();
56 $this->checkSessionToken();
57 $this->checkSessionLifetime();
58 $this->loginIfRequested();
59 $this->outputLoginFormIfNotAuthorized();
60 $this->executeSpecificStep();
61 $this->outputSpecificStep();
62 $this->redirectToTool();
63 }
64
65 /**
66 * Execute a step action if requested. If executed, a redirect is done, so
67 * the next request will render step one again if needed or initiate a
68 * request to test the next step.
69 *
70 * @throws Exception
71 * @return void
72 */
73 protected function executeSpecificStep() {
74 $action = $this->getAction();
75 $postValues = $this->getPostValues();
76 if ($action && isset($postValues['set']) && $postValues['set'] === 'execute') {
77 $stepAction = $this->getActionInstance($action);
78 $stepAction->setAction($action);
79 $stepAction->setToken($this->generateTokenForAction($action));
80 $stepAction->setPostValues($this->getPostValues());
81 $messages = $stepAction->execute();
82 $this->addSessionMessages($messages);
83 $this->redirect();
84 }
85 }
86
87 /**
88 * Render a specific step. Fallback to first step if none is given.
89 * The according step is instantiated and 'needsExecution' is called. If
90 * it needs execution, the step will be rendered, otherwise a redirect
91 * to test the next step is initiated.
92 *
93 * @return void
94 */
95 protected function outputSpecificStep() {
96 $action = $this->getAction();
97 if ($action === '') {
98 // First step action
99 list($action) = $this->authenticationActions;
100 }
101 $stepAction = $this->getActionInstance($action);
102 $stepAction->setAction($action);
103 $stepAction->setController('step');
104 $stepAction->setToken($this->generateTokenForAction($action));
105 $stepAction->setPostValues($this->getPostValues());
106
107 $needsExecution = TRUE;
108 try {
109 // needsExecution() may throw a RedirectException to communicate that it changed
110 // configuration parameters and need an application reload.
111 $needsExecution = $stepAction->needsExecution();
112 } catch (Exception\RedirectException $e) {
113 $this->redirect();
114 }
115
116 if ($needsExecution) {
117 if ($this->isInitialInstallationInProgress()) {
118 $currentStep = (array_search($action, $this->authenticationActions) + 1);
119 $totalSteps = count($this->authenticationActions);
120 $stepAction->setStepsCounter($currentStep, $totalSteps);
121 }
122 $stepAction->setMessages($this->session->getMessagesAndFlush());
123 $this->output($stepAction->handle());
124 } else {
125 // Redirect to next step if there are any
126 $currentPosition = array_keys($this->authenticationActions, $action, TRUE);
127 $nextAction = array_slice($this->authenticationActions, $currentPosition[0] + 1, 1);
128 if (!empty($nextAction)) {
129 $this->redirect('', $nextAction[0]);
130 }
131 }
132 }
133
134 /**
135 * Instantiate a specific action class
136 *
137 * @param string $action Action to instantiate
138 * @throws Exception
139 * @return \TYPO3\CMS\Install\Controller\Action\Step\StepInterface
140 */
141 protected function getActionInstance($action) {
142 $this->validateAuthenticationAction($action);
143 $actionClass = ucfirst($action);
144 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $stepAction */
145 $stepAction = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Step\\' . $actionClass);
146 if (!($stepAction instanceof Action\Step\StepInterface)) {
147 throw new Exception(
148 $action . ' does non implement StepInterface',
149 1371303903
150 );
151 }
152 return $stepAction;
153 }
154
155 /**
156 * If the last step was reached and none needs execution, a redirect
157 * to call the tool controller is initiated.
158 *
159 * @return void
160 */
161 protected function redirectToTool() {
162 $this->redirect('tool');
163 }
164
165 /**
166 * Create PackageStates.php if missing and LocalConfiguration exists.
167 *
168 * It is fired if PackageStates.php is deleted on a running instance,
169 * all packages marked as "part of minimal system" are activated in this case.
170 *
171 * The step installer creates typo3conf/, LocalConfiguration and PackageStates in
172 * one call, so an "installation in progress" does not trigger creation of
173 * PackageStates here.
174 *
175 * @throws \Exception
176 * @return void
177 */
178 protected function recreatePackageStatesFileIfNotExisting() {
179 /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
180 $configurationManager = $this->objectManager->get(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class);
181 $localConfigurationFileLocation = $configurationManager->getLocalConfigurationFileLocation();
182 $localConfigurationFileExists = is_file($localConfigurationFileLocation);
183 $packageStatesFilePath = PATH_typo3conf . 'PackageStates.php';
184 $localConfigurationBackupFilePath = preg_replace(
185 '/\\.php$/',
186 '.beforePackageStatesMigration.php',
187 $configurationManager->getLocalConfigurationFileLocation()
188 );
189
190 if (file_exists($packageStatesFilePath)
191 || (is_dir(PATH_typo3conf) && !$localConfigurationFileExists)
192 || !is_dir(PATH_typo3conf)
193 ) {
194 return;
195 }
196
197 try {
198 /** @var \TYPO3\CMS\Core\Package\FailsafePackageManager $packageManager */
199 $packageManager = \TYPO3\CMS\Core\Core\Bootstrap::getInstance()->getEarlyInstance(\TYPO3\CMS\Core\Package\PackageManager::class);
200
201 // Activate all packages required for a minimal usable system
202 $packages = $packageManager->getAvailablePackages();
203 foreach ($packages as $package) {
204 /** @var $package \TYPO3\CMS\Core\Package\PackageInterface */
205 if ($package instanceof \TYPO3\CMS\Core\Package\PackageInterface
206 && $package->isPartOfMinimalUsableSystem()
207 ) {
208 $packageManager->activatePackage($package->getPackageKey());
209 }
210 }
211
212 // Backup LocalConfiguration.php
213 copy(
214 $configurationManager->getLocalConfigurationFileLocation(),
215 $localConfigurationBackupFilePath
216 );
217
218 $packageManager->forceSortAndSavePackageStates();
219
220 // Perform a reload to self, so bootstrap now uses new PackageStates.php
221 $this->redirect();
222 } catch (\Exception $exception) {
223 if (file_exists($packageStatesFilePath)) {
224 unlink($packageStatesFilePath);
225 }
226 if (file_exists($localConfigurationBackupFilePath)) {
227 unlink($localConfigurationBackupFilePath);
228 }
229 throw $exception;
230 }
231 }
232
233 /**
234 * The first install step has a special standing and needs separate handling:
235 * At this point no directory exists (no typo3conf, no typo3temp), so we can
236 * not start the session handling (that stores the install tool session within typo3temp).
237 * This also means, we can not start the token handling for CSRF protection. This
238 * is no real problem, since no local configuration or other security relevant
239 * information was created yet.
240 *
241 * So, if no typo3conf directory exists yet, the first step is just rendered, or
242 * executed if called so. After that, a redirect is initiated to proceed with
243 * other tasks.
244 *
245 * @return void
246 */
247 protected function executeOrOutputFirstInstallStepIfNeeded() {
248 $postValues = $this->getPostValues();
249
250 $wasExecuted = FALSE;
251 $errorMessagesFromExecute = array();
252 if (isset($postValues['action'])
253 && $postValues['action'] === 'environmentAndFolders'
254 ) {
255 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $action */
256 $action = $this->objectManager->get(\TYPO3\CMS\Install\Controller\Action\Step\EnvironmentAndFolders::class);
257 $errorMessagesFromExecute = $action->execute();
258 $wasExecuted = TRUE;
259 }
260
261 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $action */
262 $action = $this->objectManager->get(\TYPO3\CMS\Install\Controller\Action\Step\EnvironmentAndFolders::class);
263
264 $needsExecution = TRUE;
265 try {
266 // needsExecution() may throw a RedirectException to communicate that it changed
267 // configuration parameters and need an application reload.
268 $needsExecution = $action->needsExecution();
269 } catch (Exception\RedirectException $e) {
270 $this->redirect();
271 }
272
273 $testReflection = new \ReflectionMethod(get_class($this), __FUNCTION__);
274 if (!@is_dir(PATH_typo3conf)
275 || $needsExecution
276 || $testReflection->getDocComment() === FALSE
277 ) {
278 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $action */
279 $action = $this->objectManager->get(\TYPO3\CMS\Install\Controller\Action\Step\EnvironmentAndFolders::class);
280 if ($this->isInitialInstallationInProgress()) {
281 $currentStep = (array_search('environmentAndFolders', $this->authenticationActions) + 1);
282 $totalSteps = count($this->authenticationActions);
283 $action->setStepsCounter($currentStep, $totalSteps);
284 }
285 $action->setController('step');
286 $action->setAction('environmentAndFolders');
287 if (!empty($errorMessagesFromExecute)) {
288 $action->setMessages($errorMessagesFromExecute);
289 }
290 $this->output($action->handle());
291 }
292
293 if ($wasExecuted) {
294 $this->redirect();
295 }
296 }
297
298 /**
299 * Checks the trusted hosts pattern setting
300 */
301 protected function adjustTrustedHostsPatternIfNeeded() {
302 if (GeneralUtility::hostHeaderValueMatchesTrustedHostsPattern($_SERVER['HTTP_HOST'])) {
303 return;
304 }
305
306 /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
307 $configurationManager = $this->objectManager->get(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class);
308 $configurationManager->setLocalConfigurationValueByPath('SYS/trustedHostsPattern', '.*');
309 $this->redirect();
310 }
311
312 /**
313 * Call silent upgrade class, redirect to self if configuration was changed.
314 *
315 * @return void
316 */
317 protected function executeSilentConfigurationUpgradesIfNeeded() {
318 /** @var SilentConfigurationUpgradeService $upgradeService */
319 $upgradeService = $this->objectManager->get(SilentConfigurationUpgradeService::class);
320 try {
321 $upgradeService->execute();
322 } catch (Exception\RedirectException $e) {
323 $this->redirect();
324 }
325 }
326
327 }