[TASK] Install Tool: Split up Checks into RequestHandlers 00/53800/21
authorBenni Mack <benni@typo3.org>
Thu, 24 Aug 2017 19:20:09 +0000 (21:20 +0200)
committerChristian Kuhn <lolli@schwarzbu.ch>
Wed, 30 Aug 2017 10:05:00 +0000 (12:05 +0200)
In order to get the controllers free of security checks, the logic of
authentication/session handling is moved from various controllers into
the RequestHandler and the Application.

Additionally, a second RequestHandler (RecoveryRequestHandler) is introduced
which acts as a fallback if TYPO3 is not installed yet, or the installation is
broken (e.g. missing PackageStates.php).

This brings a cleaner dispatching mechanism, having the RecoveryRequestHandler
(which can handle any request) dealing with the StepController, and the
regular RequestHandler (with higher priority) for handling the maintenance
functionality for running installations.

Resolves: #82238
Releases: master
Change-Id: I906ab3f15479bbe70202608f6bd7838cc22d9e3a
Reviewed-on: https://review.typo3.org/53800
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
typo3/sysext/install/Classes/Controller/AbstractController.php
typo3/sysext/install/Classes/Controller/AjaxController.php
typo3/sysext/install/Classes/Controller/StepController.php
typo3/sysext/install/Classes/Controller/ToolController.php
typo3/sysext/install/Classes/Exception/AuthenticationRequiredException.php [new file with mode: 0644]
typo3/sysext/install/Classes/Http/Application.php
typo3/sysext/install/Classes/Http/RecoveryRequestHandler.php [new file with mode: 0644]
typo3/sysext/install/Classes/Http/RequestHandler.php

index 1834c15..c449a81 100644 (file)
@@ -14,9 +14,10 @@ namespace TYPO3\CMS\Install\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Core\Cache\Backend\NullBackend;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Install\Service\EnableFileService;
+use TYPO3\CMS\Install\Controller\Exception\RedirectException;
+use TYPO3\CMS\Install\Exception\AuthenticationRequiredException;
+use TYPO3\CMS\Install\Service\SessionService;
 
 /**
  * Controller abstract for shared parts of Tool, Step and Ajax controller
@@ -24,7 +25,7 @@ use TYPO3\CMS\Install\Service\EnableFileService;
 class AbstractController
 {
     /**
-     * @var \TYPO3\CMS\Install\Service\SessionService
+     * @var SessionService
      */
     protected $session = null;
 
@@ -34,6 +35,14 @@ class AbstractController
     protected $authenticationActions = [];
 
     /**
+     * @param SessionService $session
+     */
+    public function setSessionService(SessionService $session)
+    {
+        $this->session = $session;
+    }
+
+    /**
      * @return bool
      */
     protected function isInstallToolAvailable()
@@ -47,147 +56,12 @@ class AbstractController
     }
 
     /**
-     * Guard method checking typo3conf/ENABLE_INSTALL_TOOL
-     *
-     * Checking ENABLE_INSTALL_TOOL validity is simple:
-     * As soon as there is a typo3conf directory at all (not step 1 of "first install"),
-     * the file must be there and valid in order to proceed.
-     */
-    protected function outputInstallToolNotEnabledMessageIfNeeded()
-    {
-        if (!$this->isInstallToolAvailable()) {
-            if (!EnableFileService::isFirstInstallAllowed() && !\TYPO3\CMS\Core\Core\Bootstrap::getInstance()->checkIfEssentialConfigurationExists()) {
-                /** @var \TYPO3\CMS\Install\Controller\Action\ActionInterface $action */
-                $action = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Controller\Action\Common\FirstInstallAction::class);
-                $action->setAction('firstInstall');
-            } else {
-                /** @var \TYPO3\CMS\Install\Controller\Action\ActionInterface $action */
-                $action = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Controller\Action\Common\InstallToolDisabledAction::class);
-                $action->setAction('installToolDisabled');
-            }
-            $action->setController('common');
-            $this->output($action->handle());
-        }
-    }
-
-    /**
-     * Guard method checking for valid install tool password
-     *
-     * If installation is completed - LocalConfiguration exists and
-     * installProcess is not running, and installToolPassword must be set
-     */
-    protected function outputInstallToolPasswordNotSetMessageIfNeeded()
-    {
-        if (!$this->isInitialInstallationInProgress()
-            && (empty($GLOBALS['TYPO3_CONF_VARS']['BE']['installToolPassword']))
-        ) {
-            /** @var \TYPO3\CMS\Install\Controller\Action\ActionInterface $action */
-            $action = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Controller\Action\Common\InstallToolPasswordNotSetAction::class);
-            $action->setController('common');
-            $action->setAction('installToolPasswordNotSet');
-            $this->output($action->handle());
-        }
-    }
-
-    /**
-     * Use form protection API to find out if protected POST forms are ok.
-     *
-     * @throws Exception
-     */
-    protected function checkSessionToken()
-    {
-        $postValues = $this->getPostValues();
-        $tokenOk = false;
-        if (!empty($postValues)) {
-            // A token must be given as soon as there is POST data
-            if (isset($postValues['token'])) {
-                /** @var $formProtection \TYPO3\CMS\Core\FormProtection\InstallToolFormProtection */
-                $formProtection = \TYPO3\CMS\Core\FormProtection\FormProtectionFactory::get(
-                    \TYPO3\CMS\Core\FormProtection\InstallToolFormProtection::class
-                );
-                $action = $this->getAction();
-                if ($action === '') {
-                    throw new Exception(
-                        'No POST action given for token check',
-                        1369326593
-                    );
-                }
-                $tokenOk = $formProtection->validateToken($postValues['token'], 'installTool', $action);
-            }
-        } else {
-            $tokenOk = true;
-        }
-
-        $this->handleSessionTokenCheck($tokenOk);
-    }
-
-    /**
-     * If session token was not ok, the session is reset and either
-     * a redirect is initialized (will load the same step step controller again) or
-     * if in install tool, the login form is displayed.
-     *
-     * @param bool $tokenOk
-     */
-    protected function handleSessionTokenCheck($tokenOk)
-    {
-        if (!$tokenOk) {
-            $this->session->resetSession();
-            $this->session->startSession();
-
-            if ($this->isInitialInstallationInProgress()) {
-                $this->redirect();
-            } else {
-                /** @var $message \TYPO3\CMS\Install\Status\ErrorStatus */
-                $message = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Status\ErrorStatus::class);
-                $message->setTitle('Invalid form token');
-                $message->setMessage(
-                    'The form protection token was invalid. You have been logged out, please log in and try again.'
-                );
-                $this->output($this->loginForm($message));
-            }
-        }
-    }
-
-    /**
-     * Check if session expired.
-     */
-    protected function checkSessionLifetime()
-    {
-        if ($this->session->isExpired()) {
-            // Session expired, log out user, start new session
-            $this->session->resetSession();
-            $this->session->startSession();
-
-            $this->handleSessionLifeTimeExpired();
-        }
-    }
-
-    /**
-     * If session expired, the current step of step controller is reloaded
-     * (if first installation is running) - or the login form is displayed.
-     */
-    protected function handleSessionLifeTimeExpired()
-    {
-        if ($this->isInitialInstallationInProgress()) {
-            $this->redirect();
-        } else {
-            /** @var $message \TYPO3\CMS\Install\Status\ErrorStatus */
-            $message = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Status\ErrorStatus::class);
-            $message->setTitle('Session expired');
-            $message->setMessage(
-                'Your Install Tool session has expired. You have been logged out, please log in and try again.'
-            );
-            $this->output($this->loginForm($message));
-        }
-    }
-
-    /**
      * Show login form
      *
      * @param \TYPO3\CMS\Install\Status\StatusInterface $message Optional status message from controller
      * @return string Rendered HTML
      */
-    protected function loginForm(\TYPO3\CMS\Install\Status\StatusInterface $message = null)
+    public function loginForm(\TYPO3\CMS\Install\Status\StatusInterface $message = null)
     {
         /** @var \TYPO3\CMS\Install\Controller\Action\Common\LoginForm $action */
         $action = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Controller\Action\Common\LoginForm::class);
@@ -204,6 +78,9 @@ class AbstractController
 
     /**
      * Validate install tool password and login user if requested
+     *
+     * @throws RedirectException on successful login
+     * @throws AuthenticationRequiredException when a login is requested but credentials are invalid
      */
     protected function loginIfRequested()
     {
@@ -233,38 +110,22 @@ class AbstractController
             if ($validPassword) {
                 $this->session->setAuthorized();
                 $this->sendLoginSuccessfulMail();
-                $this->redirect();
+                throw new RedirectException('Login', 1504032046);
+            }
+            if (!isset($postValues['values']['password']) || $postValues['values']['password'] === '') {
+                $messageText = 'Please enter the install tool password';
             } else {
-                if (!isset($postValues['values']['password']) || $postValues['values']['password'] === '') {
-                    $messageText = 'Please enter the install tool password';
-                } else {
-                    $saltFactory = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance(null, 'BE');
-                    $hashedPassword = $saltFactory->getHashedPassword($password);
-                    $messageText = 'Given password does not match the install tool login password. ' .
+                $saltFactory = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance(null, 'BE');
+                $hashedPassword = $saltFactory->getHashedPassword($password);
+                $messageText = 'Given password does not match the install tool login password. ' .
                         'Calculated hash: ' . $hashedPassword;
-                }
-                /** @var $message \TYPO3\CMS\Install\Status\ErrorStatus */
-                $message = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Status\ErrorStatus::class);
-                $message->setTitle('Login failed');
-                $message->setMessage($messageText);
-                $this->sendLoginFailedMail();
-                $this->output($this->loginForm($message));
             }
-        }
-    }
-
-    /**
-     * Show login for if user is not authorized yet and if
-     * not in first installation process.
-     */
-    protected function outputLoginFormIfNotAuthorized()
-    {
-        if (!$this->session->isAuthorized()
-            && !$this->isInitialInstallationInProgress()
-        ) {
-            $this->output($this->loginForm());
-        } else {
-            $this->session->refreshSession();
+            /** @var $message \TYPO3\CMS\Install\Status\ErrorStatus */
+            $message = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Status\ErrorStatus::class);
+            $message->setTitle('Login failed');
+            $message->setMessage($messageText);
+            $this->sendLoginFailedMail();
+            throw new AuthenticationRequiredException('Login failed', 1504031979, null, $message);
         }
     }
 
@@ -362,20 +223,6 @@ class AbstractController
     }
 
     /**
-     * Initialize session object.
-     * Subclass will throw exception if session can not be created or if
-     * preconditions like a valid encryption key are not set.
-     */
-    protected function initializeSession()
-    {
-        /** @var \TYPO3\CMS\Install\Service\SessionService $session */
-        $this->session = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Service\SessionService::class);
-        if (!$this->session->hasSession()) {
-            $this->session->startSession();
-        }
-    }
-
-    /**
      * Add status messages to session.
      * Used to output messages between requests, especially in step controller
      *
@@ -389,31 +236,6 @@ class AbstractController
     }
 
     /**
-     * Required extbase ext_localconf
-     * Set caching to NullBackend, install tool must not cache anything
-     */
-    protected function loadBaseExtensions()
-    {
-        // @todo: Find out if this could be left out
-        require(\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('extbase') . 'ext_localconf.php');
-
-        $cacheConfigurations = $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations'];
-
-        $cacheConfigurationsWithCachesSetToNullBackend = [];
-        foreach ($cacheConfigurations as $cacheName => $cacheConfiguration) {
-            // cache_core is handled in bootstrap already
-            if (is_array($cacheConfiguration) && $cacheName !== 'cache_core') {
-                $cacheConfiguration['backend'] = NullBackend::class;
-                $cacheConfiguration['options'] = [];
-            }
-            $cacheConfigurationsWithCachesSetToNullBackend[$cacheName] = $cacheConfiguration;
-        }
-        /** @var $cacheManager \TYPO3\CMS\Core\Cache\CacheManager */
-        $cacheManager = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class);
-        $cacheManager->setCacheConfigurations($cacheConfigurationsWithCachesSetToNullBackend);
-    }
-
-    /**
      * Check given action name is one of the allowed actions.
      *
      * @param string $action Given action to validate
@@ -435,7 +257,7 @@ class AbstractController
      * @throws Exception
      * @return string Empty string if no action is given or sanitized action string
      */
-    protected function getAction()
+    public function getAction()
     {
         $formValues = GeneralUtility::_GP('install');
         $action = '';
@@ -479,7 +301,7 @@ class AbstractController
      * @param string $action Set specific action for next request, used in step controller to specify next step
      * @throws Exception\RedirectLoopException
      */
-    protected function redirect($controller = '', $action = '')
+    public function redirect($controller = '', $action = '')
     {
         $getPostValues = GeneralUtility::_GP('install');
 
@@ -539,7 +361,7 @@ class AbstractController
      *
      * @param string $content Content to output
      */
-    protected function output($content = '')
+    public function output($content = '')
     {
         header('Content-Type: text/html; charset=utf-8');
         header('Cache-Control: no-cache, must-revalidate');
index ccf2b6a..5091aa6 100644 (file)
@@ -87,39 +87,11 @@ class AjaxController extends AbstractController
      */
     public function execute()
     {
-        $this->loadBaseExtensions();
-        // Warning: Order of these methods is security relevant and interferes with different access
-        // conditions (new/existing installation). See the single method comments for details.
-        $this->outputInstallToolNotEnabledMessageIfNeeded();
-        $this->checkInstallToolPasswordNotSet();
-        $this->initializeSession();
-        $this->checkSessionToken();
-        $this->checkSessionLifetime();
         $this->checkLogin();
         $this->dispatchAuthenticationActions();
     }
 
     /**
-     * Check whether the install tool is enabled
-     */
-    protected function outputInstallToolNotEnabledMessageIfNeeded()
-    {
-        if (!$this->isInstallToolAvailable()) {
-            $this->output($this->unauthorized);
-        }
-    }
-
-    /**
-     * Check if the install tool password is set
-     */
-    protected function checkInstallToolPasswordNotSet()
-    {
-        if (empty($GLOBALS['TYPO3_CONF_VARS']['BE']['installToolPassword'])) {
-            $this->output($this->unauthorized);
-        }
-    }
-
-    /**
      * Check login status
      */
     protected function checkLogin()
@@ -132,28 +104,6 @@ class AjaxController extends AbstractController
     }
 
     /**
-     * Overwrites abstract method
-     * In contrast to abstract method, a response "you are not authorized is outputted"
-     *
-     * @param bool $tokenOk
-     */
-    protected function handleSessionTokenCheck($tokenOk)
-    {
-        if (!$tokenOk) {
-            $this->output($this->unauthorized);
-        }
-    }
-
-    /**
-     * Overwrites abstract method
-     * In contrast to abstract method, a response "you are not authorized is outputted"
-     */
-    protected function handleSessionLifeTimeExpired()
-    {
-        $this->output($this->unauthorized);
-    }
-
-    /**
      * Call an action that needs authentication
      *
      * @throws Exception
@@ -188,7 +138,7 @@ class AjaxController extends AbstractController
      *
      * @param string $content JSON encoded content to output
      */
-    protected function output($content = '')
+    public function output($content = '')
     {
         ob_clean();
         header('Content-Type: application/json; charset=utf-8');
index 26e9673..682ec88 100644 (file)
@@ -15,7 +15,7 @@ namespace TYPO3\CMS\Install\Controller;
  */
 
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Install\Service\SilentConfigurationUpgradeService;
+use TYPO3\CMS\Install\Service\EnableFileService;
 
 /**
  * Install step controller, dispatcher class of step actions.
@@ -43,16 +43,6 @@ class StepController extends AbstractController
      */
     public function execute()
     {
-        $this->loadBaseExtensions();
-        $this->outputInstallToolNotEnabledMessageIfNeeded();
-        $this->outputInstallToolPasswordNotSetMessageIfNeeded();
-        $this->recreatePackageStatesFileIfNotExisting();
-        $this->executeOrOutputFirstInstallStepIfNeeded();
-        $this->adjustTrustedHostsPatternIfNeeded();
-        $this->executeSilentConfigurationUpgradesIfNeeded();
-        $this->initializeSession();
-        $this->checkSessionToken();
-        $this->checkSessionLifetime();
         $this->loginIfRequested();
         $this->outputLoginFormIfNotAuthorized();
         $this->executeSpecificStep();
@@ -61,6 +51,58 @@ class StepController extends AbstractController
     }
 
     /**
+     * Guard method checking typo3conf/ENABLE_INSTALL_TOOL
+     *
+     * Checking ENABLE_INSTALL_TOOL validity is simple:
+     * As soon as there is a typo3conf directory at all (not step 1 of "first install"),
+     * the file must be there and valid in order to proceed.
+     */
+    public function outputInstallToolNotEnabledMessage()
+    {
+        if (!EnableFileService::isFirstInstallAllowed() && !\TYPO3\CMS\Core\Core\Bootstrap::getInstance()->checkIfEssentialConfigurationExists()) {
+            /** @var \TYPO3\CMS\Install\Controller\Action\ActionInterface $action */
+            $action = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Controller\Action\Common\FirstInstallAction::class);
+            $action->setAction('firstInstall');
+        } else {
+            /** @var \TYPO3\CMS\Install\Controller\Action\ActionInterface $action */
+            $action = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Controller\Action\Common\InstallToolDisabledAction::class);
+            $action->setAction('installToolDisabled');
+        }
+        $action->setController('common');
+        $this->output($action->handle());
+    }
+
+    /**
+     * Guard method checking for valid install tool password
+     *
+     * If installation is completed - LocalConfiguration exists and
+     * installProcess is not running, and installToolPassword must be set
+     */
+    public function outputInstallToolPasswordNotSetMessage()
+    {
+        /** @var \TYPO3\CMS\Install\Controller\Action\ActionInterface $action */
+        $action = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Controller\Action\Common\InstallToolPasswordNotSetAction::class);
+        $action->setController('common');
+        $action->setAction('installToolPasswordNotSet');
+        $this->output($action->handle());
+    }
+
+    /**
+     * Show login for if user is not authorized yet and if
+     * not in first installation process.
+     */
+    protected function outputLoginFormIfNotAuthorized()
+    {
+        if (!$this->session->isAuthorized()
+            && !$this->isInitialInstallationInProgress()
+        ) {
+            $this->output($this->loginForm());
+        } else {
+            $this->session->refreshSession();
+        }
+    }
+
+    /**
      * Execute a step action if requested. If executed, a redirect is done, so
      * the next request will render step one again if needed or initiate a
      * request to test the next step.
@@ -160,74 +202,6 @@ class StepController extends AbstractController
     }
 
     /**
-     * Create PackageStates.php if missing and LocalConfiguration exists.
-     *
-     * It is fired if PackageStates.php is deleted on a running instance,
-     * all packages marked as "part of minimal system" are activated in this case.
-     *
-     * The step installer creates typo3conf/, LocalConfiguration and PackageStates in
-     * one call, so an "installation in progress" does not trigger creation of
-     * PackageStates here.
-     *
-     * @throws \Exception
-     */
-    protected function recreatePackageStatesFileIfNotExisting()
-    {
-        /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
-        $configurationManager = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class);
-        $localConfigurationFileLocation = $configurationManager->getLocalConfigurationFileLocation();
-        $localConfigurationFileExists = is_file($localConfigurationFileLocation);
-        $packageStatesFilePath = PATH_typo3conf . 'PackageStates.php';
-        $localConfigurationBackupFilePath = preg_replace(
-            '/\\.php$/',
-            '.beforePackageStatesMigration.php',
-            $configurationManager->getLocalConfigurationFileLocation()
-        );
-
-        if (file_exists($packageStatesFilePath)
-            || (is_dir(PATH_typo3conf) && !$localConfigurationFileExists)
-            || !is_dir(PATH_typo3conf)
-        ) {
-            return;
-        }
-
-        try {
-            /** @var \TYPO3\CMS\Core\Package\FailsafePackageManager $packageManager */
-            $packageManager = \TYPO3\CMS\Core\Core\Bootstrap::getInstance()->getEarlyInstance(\TYPO3\CMS\Core\Package\PackageManager::class);
-
-            // Activate all packages required for a minimal usable system
-            $packages = $packageManager->getAvailablePackages();
-            foreach ($packages as $package) {
-                /** @var $package \TYPO3\CMS\Core\Package\PackageInterface */
-                if ($package instanceof \TYPO3\CMS\Core\Package\PackageInterface
-                    && $package->isPartOfMinimalUsableSystem()
-                ) {
-                    $packageManager->activatePackage($package->getPackageKey());
-                }
-            }
-
-            // Backup LocalConfiguration.php
-            copy(
-                $configurationManager->getLocalConfigurationFileLocation(),
-                $localConfigurationBackupFilePath
-            );
-
-            $packageManager->forceSortAndSavePackageStates();
-
-            // Perform a reload to self, so bootstrap now uses new PackageStates.php
-            $this->redirect();
-        } catch (\Exception $exception) {
-            if (file_exists($packageStatesFilePath)) {
-                unlink($packageStatesFilePath);
-            }
-            if (file_exists($localConfigurationBackupFilePath)) {
-                unlink($localConfigurationBackupFilePath);
-            }
-            throw $exception;
-        }
-    }
-
-    /**
      * The first install step has a special standing and needs separate handling:
      * At this point no directory exists (no typo3conf, no typo3temp), so we can
      * not start the session handling (that stores the install tool session within typo3temp).
@@ -239,7 +213,7 @@ class StepController extends AbstractController
      * executed if called so. After that, a redirect is initiated to proceed with
      * other tasks.
      */
-    protected function executeOrOutputFirstInstallStepIfNeeded()
+    public function executeOrOutputFirstInstallStepIfNeeded()
     {
         $postValues = $this->getPostValues();
 
@@ -286,33 +260,4 @@ class StepController extends AbstractController
             $this->redirect();
         }
     }
-
-    /**
-     * Checks the trusted hosts pattern setting
-     */
-    protected function adjustTrustedHostsPatternIfNeeded()
-    {
-        if (GeneralUtility::hostHeaderValueMatchesTrustedHostsPattern($_SERVER['HTTP_HOST'])) {
-            return;
-        }
-
-        /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
-        $configurationManager = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class);
-        $configurationManager->setLocalConfigurationValueByPath('SYS/trustedHostsPattern', '.*');
-        $this->redirect();
-    }
-
-    /**
-     * Call silent upgrade class, redirect to self if configuration was changed.
-     */
-    protected function executeSilentConfigurationUpgradesIfNeeded()
-    {
-        /** @var SilentConfigurationUpgradeService $upgradeService */
-        $upgradeService = GeneralUtility::makeInstance(SilentConfigurationUpgradeService::class);
-        try {
-            $upgradeService->execute();
-        } catch (Exception\RedirectException $e) {
-            $this->redirect();
-        }
-    }
 }
index 0f03dc2..b70ad6f 100644 (file)
@@ -14,6 +14,7 @@ namespace TYPO3\CMS\Install\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Install\Controller\Exception\RedirectException;
 use TYPO3\CMS\Install\Service\EnableFileService;
 
 /**
@@ -39,15 +40,6 @@ class ToolController extends AbstractController
      */
     public function execute()
     {
-        $this->loadBaseExtensions();
-
-        // Warning: Order of these methods is security relevant and interferes with different access
-        // conditions (new/existing installation). See the single method comments for details.
-        $this->outputInstallToolNotEnabledMessageIfNeeded();
-        $this->outputInstallToolPasswordNotSetMessageIfNeeded();
-        $this->initializeSession();
-        $this->checkSessionToken();
-        $this->checkSessionLifetime();
         $this->logoutIfRequested();
         $this->loginIfRequested();
         $this->outputLoginFormIfNotAuthorized();
@@ -71,7 +63,20 @@ class ToolController extends AbstractController
             );
             $formProtection->clean();
             $this->session->destroySession();
-            $this->redirect();
+            throw new RedirectException('Forced logout', 1504032052);
+        }
+    }
+
+    /**
+     * Show login for if user is not authorized yet and if
+     * not in first installation process.
+     */
+    protected function outputLoginFormIfNotAuthorized()
+    {
+        if (!$this->session->isAuthorized()) {
+            $this->output($this->loginForm());
+        } else {
+            $this->session->refreshSession();
         }
     }
 
diff --git a/typo3/sysext/install/Classes/Exception/AuthenticationRequiredException.php b/typo3/sysext/install/Classes/Exception/AuthenticationRequiredException.php
new file mode 100644 (file)
index 0000000..b7e5d47
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+declare(strict_types=1);
+namespace TYPO3\CMS\Install\Exception;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use Throwable;
+use TYPO3\CMS\Install\Status\StatusInterface;
+
+/**
+ * An exception if the authentication is needed
+ */
+class AuthenticationRequiredException extends \TYPO3\CMS\Install\Exception
+{
+    /**
+     * @var StatusInterface
+     */
+    protected $messageObject;
+
+    /**
+     * @param string $message
+     * @param int $code
+     * @param Throwable|null $previous
+     * @param StatusInterface|null $messageObject
+     */
+    public function __construct($message = '', $code = 0, Throwable $previous = null, StatusInterface $messageObject = null)
+    {
+        parent::__construct($message, $code, $previous);
+        $this->messageObject = $messageObject;
+    }
+
+    /**
+     * @return StatusInterface
+     */
+    public function getMessageObject(): StatusInterface
+    {
+        return $this->messageObject;
+    }
+}
index 732c1e0..e31c44a 100644 (file)
@@ -13,6 +13,7 @@ namespace TYPO3\CMS\Install\Http;
  *
  * The TYPO3 project - inspiring people to share!
  */
+use TYPO3\CMS\Core\Cache\Backend\NullBackend;
 use TYPO3\CMS\Core\Core\ApplicationInterface;
 use TYPO3\CMS\Core\Core\Bootstrap;
 
@@ -37,7 +38,8 @@ class Application implements ApplicationInterface
      * @var array
      */
     protected $availableRequestHandlers = [
-        \TYPO3\CMS\Install\Http\RequestHandler::class
+        \TYPO3\CMS\Install\Http\RequestHandler::class,
+        \TYPO3\CMS\Install\Http\RecoveryRequestHandler::class
     ];
 
     /**
@@ -61,6 +63,8 @@ class Application implements ApplicationInterface
         $this->bootstrap
             ->startOutputBuffering()
             ->loadConfigurationAndInitialize(false, \TYPO3\CMS\Core\Package\FailsafePackageManager::class);
+
+        $this->disableCachingFramework();
     }
 
     /**
@@ -82,6 +86,27 @@ class Application implements ApplicationInterface
     }
 
     /**
+     * Set caching to NullBackend, install tool must not cache anything
+     */
+    protected function disableCachingFramework()
+    {
+        $cacheConfigurations = $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations'];
+
+        $cacheConfigurationsWithCachesSetToNullBackend = [];
+        foreach ($cacheConfigurations as $cacheName => $cacheConfiguration) {
+            // cache_core is handled in bootstrap already
+            if (is_array($cacheConfiguration) && $cacheName !== 'cache_core') {
+                $cacheConfiguration['backend'] = NullBackend::class;
+                $cacheConfiguration['options'] = [];
+            }
+            $cacheConfigurationsWithCachesSetToNullBackend[$cacheName] = $cacheConfiguration;
+        }
+        /** @var $cacheManager \TYPO3\CMS\Core\Cache\CacheManager */
+        $cacheManager = $this->bootstrap->getEarlyInstance(\TYPO3\CMS\Core\Cache\CacheManager::class);
+        $cacheManager->setCacheConfigurations($cacheConfigurationsWithCachesSetToNullBackend);
+    }
+
+    /**
      * Define constants
      */
     protected function defineLegacyConstants()
diff --git a/typo3/sysext/install/Classes/Http/RecoveryRequestHandler.php b/typo3/sysext/install/Classes/Http/RecoveryRequestHandler.php
new file mode 100644 (file)
index 0000000..3c19c85
--- /dev/null
@@ -0,0 +1,358 @@
+<?php
+declare(strict_types=1);
+namespace TYPO3\CMS\Install\Http;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Core\Core\Bootstrap;
+use TYPO3\CMS\Core\Http\RequestHandlerInterface;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Install\Controller\Exception;
+use TYPO3\CMS\Install\Controller\Exception\RedirectException;
+use TYPO3\CMS\Install\Controller\StepController;
+use TYPO3\CMS\Install\Exception\AuthenticationRequiredException;
+use TYPO3\CMS\Install\Service\SilentConfigurationUpgradeService;
+
+/**
+ * Request handler if
+ * - an instance is not (fully) installed or basic configuration missing (= fresh installation)
+ * - the install tool is locked (no file exists) etc.
+ * - or the install tool session is invalid
+ */
+class RecoveryRequestHandler implements RequestHandlerInterface
+{
+    /**
+     * Instance of the current TYPO3 bootstrap
+     * @var Bootstrap
+     */
+    protected $bootstrap;
+
+    /**
+     * @var ServerRequestInterface
+     */
+    protected $request;
+
+    /**
+     * @var \TYPO3\CMS\Install\Service\SessionService
+     */
+    protected $session = null;
+
+    /**
+     * Constructor handing over the bootstrap
+     *
+     * @param Bootstrap $bootstrap
+     */
+    public function __construct(Bootstrap $bootstrap)
+    {
+        $this->bootstrap = $bootstrap;
+    }
+
+    /**
+     * Handles an install tool request when nothing is there
+     *
+     * @param ServerRequestInterface $request
+     */
+    public function handleRequest(ServerRequestInterface $request)
+    {
+        $this->request = $request;
+        $controller = GeneralUtility::makeInstance(StepController::class);
+
+        try {
+            // Warning: Order of these methods is security relevant and interferes with different access
+            // conditions (new/existing installation). See the single method comments for details.
+            if (!$this->isInstallToolAvailable()) {
+                $controller->outputInstallToolNotEnabledMessage();
+            }
+            if (!$this->isInitialInstallationInProgress()
+                && !$this->isInstallToolPasswordSet()
+            ) {
+                $controller->outputInstallToolPasswordNotSetMessage();
+            }
+            $this->recreatePackageStatesFileIfNotExisting();
+
+            // todo: this would be nice, if this is detected by the Request workflow and not the controller
+            // controller should just execute this
+            $controller->executeOrOutputFirstInstallStepIfNeeded();
+            $this->adjustTrustedHostsPatternIfNeeded();
+            $this->executeSilentConfigurationUpgradesIfNeeded();
+            $this->initializeSession();
+            $this->checkSessionToken();
+            $this->checkSessionLifetime();
+            $controller->setSessionService($this->session);
+            $controller->execute();
+        } catch (AuthenticationRequiredException $e) {
+            $controller->output($controller->loginForm($e->getMessageObject()));
+        } catch (RedirectException $e) {
+            $controller->redirect();
+        }
+    }
+
+    /**
+     * This request handler can handle any request when not in CLI mode.
+     *
+     * @param ServerRequestInterface $request
+     * @return bool Returns always TRUE
+     */
+    public function canHandleRequest(ServerRequestInterface $request)
+    {
+        return true;
+    }
+
+    /**
+     * Returns the priority - how eager the handler is to actually handle the request.
+     *
+     * @return int The priority of the request handler.
+     */
+    public function getPriority()
+    {
+        return 20;
+    }
+
+    /**
+     * Initialize session object.
+     * Subclass will throw exception if session can not be created or if
+     * preconditions like a valid encryption key are not set.
+     */
+    protected function initializeSession()
+    {
+        /** @var \TYPO3\CMS\Install\Service\SessionService $session */
+        $this->session = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Service\SessionService::class);
+        if (!$this->session->hasSession()) {
+            $this->session->startSession();
+        }
+    }
+
+    /**
+     * Use form protection API to find out if protected POST forms are ok.
+     *
+     * @throws Exception
+     */
+    protected function checkSessionToken()
+    {
+        $postValues = $this->request->getParsedBody()['install'];
+        $tokenOk = false;
+        if (!empty($postValues)) {
+            // A token must be given as soon as there is POST data
+            if (isset($postValues['token'])) {
+                /** @var $formProtection \TYPO3\CMS\Core\FormProtection\InstallToolFormProtection */
+                $formProtection = \TYPO3\CMS\Core\FormProtection\FormProtectionFactory::get(
+                    \TYPO3\CMS\Core\FormProtection\InstallToolFormProtection::class
+                );
+                $action = (string)$postValues['action'];
+                if ($action === '') {
+                    throw new Exception(
+                        'No POST action given for token check',
+                        1369326594
+                    );
+                }
+                $tokenOk = $formProtection->validateToken($postValues['token'], 'installTool', $action);
+            }
+        } else {
+            $tokenOk = true;
+        }
+
+        $this->handleSessionTokenCheck($tokenOk);
+    }
+
+    /**
+     * If session token was not ok, the session is reset and either
+     * a redirect is initialized (will load the same step step controller again) or
+     * if in install tool, the login form is displayed.
+     *
+     * @param bool $tokenOk
+     * @throws AuthenticationRequiredException
+     * @throws RedirectException
+     */
+    protected function handleSessionTokenCheck($tokenOk)
+    {
+        if (!$tokenOk) {
+            $this->session->resetSession();
+            $this->session->startSession();
+
+            if ($this->isInitialInstallationInProgress()) {
+                throw new RedirectException('Initial installation in progress', 1504032139);
+            }
+            /** @var $message \TYPO3\CMS\Install\Status\ErrorStatus */
+            $message = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Status\ErrorStatus::class);
+            $message->setTitle('Invalid form token');
+            $message->setMessage(
+                    'The form protection token was invalid. You have been logged out, please log in and try again.'
+                );
+            throw new AuthenticationRequiredException('Invalid form token', 1504030706, null, $message);
+        }
+    }
+
+    /**
+     * Check if session expired.
+     *
+     * If session expired, the current step of step controller is reloaded
+     * (if first installation is running) - or the login form is displayed.
+     */
+    protected function checkSessionLifetime()
+    {
+        if ($this->session->isExpired()) {
+            // Session expired, log out user, start new session
+            $this->session->resetSession();
+            $this->session->startSession();
+
+            if ($this->isInitialInstallationInProgress()) {
+                throw new RedirectException('Initial installation in progress', 1504032125);
+            }
+            /** @var $message \TYPO3\CMS\Install\Status\ErrorStatus */
+            $message = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Status\ErrorStatus::class);
+            $message->setTitle('Session expired');
+            $message->setMessage(
+                'Your Install Tool session has expired. You have been logged out, please log in and try again.'
+            );
+            throw new AuthenticationRequiredException('Session expired', 1504030725, null, $message);
+        }
+    }
+
+    /**
+     * First installation is in progress, if LocalConfiguration does not exist,
+     * or if isInitialInstallationInProgress is not set or FALSE.
+     *
+     * @return bool TRUE if installation is in progress
+     */
+    protected function isInitialInstallationInProgress()
+    {
+        /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
+        $configurationManager = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class);
+
+        $localConfigurationFileLocation = $configurationManager->getLocalConfigurationFileLocation();
+        $localConfigurationFileExists = @is_file($localConfigurationFileLocation);
+        return !$localConfigurationFileExists
+            || !empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['isInitialInstallationInProgress'])
+        ;
+    }
+
+    /**
+     * @return bool
+     */
+    protected function isInstallToolAvailable()
+    {
+        /** @var \TYPO3\CMS\Install\Service\EnableFileService $installToolEnableService */
+        $installToolEnableService = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Service\EnableFileService::class);
+        if ($installToolEnableService->isFirstInstallAllowed()) {
+            return true;
+        }
+        return $installToolEnableService->checkInstallToolEnableFile();
+    }
+
+    /**
+     * Check if the install tool password is set
+     */
+    protected function isInstallToolPasswordSet()
+    {
+        return !empty($GLOBALS['TYPO3_CONF_VARS']['BE']['installToolPassword']);
+    }
+
+    /**
+     * Create PackageStates.php if missing and LocalConfiguration exists.
+     *
+     * It is fired if PackageStates.php is deleted on a running instance,
+     * all packages marked as "part of minimal system" are activated in this case.
+     *
+     * The step installer creates typo3conf/, LocalConfiguration and PackageStates in
+     * one call, so an "installation in progress" does not trigger creation of
+     * PackageStates here.
+     *
+     * @throws \Exception
+     */
+    public function recreatePackageStatesFileIfNotExisting()
+    {
+        /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
+        $configurationManager = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class);
+        $localConfigurationFileLocation = $configurationManager->getLocalConfigurationFileLocation();
+        $localConfigurationFileExists = is_file($localConfigurationFileLocation);
+        $packageStatesFilePath = PATH_typo3conf . 'PackageStates.php';
+        $localConfigurationBackupFilePath = preg_replace(
+            '/\\.php$/',
+            '.beforePackageStatesMigration.php',
+            $configurationManager->getLocalConfigurationFileLocation()
+        );
+
+        if (file_exists($packageStatesFilePath)
+            || (is_dir(PATH_typo3conf) && !$localConfigurationFileExists)
+            || !is_dir(PATH_typo3conf)
+        ) {
+            return;
+        }
+
+        try {
+            /** @var \TYPO3\CMS\Core\Package\FailsafePackageManager $packageManager */
+            $packageManager = \TYPO3\CMS\Core\Core\Bootstrap::getInstance()->getEarlyInstance(\TYPO3\CMS\Core\Package\PackageManager::class);
+
+            // Activate all packages required for a minimal usable system
+            $packages = $packageManager->getAvailablePackages();
+            foreach ($packages as $package) {
+                /** @var $package \TYPO3\CMS\Core\Package\PackageInterface */
+                if ($package instanceof \TYPO3\CMS\Core\Package\PackageInterface
+                    && $package->isPartOfMinimalUsableSystem()
+                ) {
+                    $packageManager->activatePackage($package->getPackageKey());
+                }
+            }
+
+            // Backup LocalConfiguration.php
+            copy(
+                $configurationManager->getLocalConfigurationFileLocation(),
+                $localConfigurationBackupFilePath
+            );
+
+            $packageManager->forceSortAndSavePackageStates();
+
+            // Perform a reload to self, so bootstrap now uses new PackageStates.php
+            throw new RedirectException('Changed PackageStates.php', 1504032160);
+        } catch (\Exception $exception) {
+            if (file_exists($packageStatesFilePath)) {
+                unlink($packageStatesFilePath);
+            }
+            if (file_exists($localConfigurationBackupFilePath)) {
+                unlink($localConfigurationBackupFilePath);
+            }
+            throw $exception;
+        }
+    }
+
+    /**
+     * Checks the trusted hosts pattern setting
+     */
+    public function adjustTrustedHostsPatternIfNeeded()
+    {
+        if (GeneralUtility::hostHeaderValueMatchesTrustedHostsPattern($_SERVER['HTTP_HOST'])) {
+            return;
+        }
+
+        /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
+        $configurationManager = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class);
+        $configurationManager->setLocalConfigurationValueByPath('SYS/trustedHostsPattern', '.*');
+        throw new RedirectException('Trusted hosts pattern adapted', 1504032088);
+    }
+
+    /**
+     * Call silent upgrade class, redirect to self if configuration was changed.
+     */
+    public function executeSilentConfigurationUpgradesIfNeeded()
+    {
+        /** @var SilentConfigurationUpgradeService $upgradeService */
+        $upgradeService = GeneralUtility::makeInstance(SilentConfigurationUpgradeService::class);
+        try {
+            $upgradeService->execute();
+        } catch (RedirectException $e) {
+            throw new RedirectException('Silent configuration upgrade', 1504032097);
+        }
+    }
+}
index 331c202..96923c4 100644 (file)
@@ -1,4 +1,5 @@
 <?php
+declare(strict_types=1);
 namespace TYPO3\CMS\Install\Http;
 
 /*
@@ -18,6 +19,9 @@ use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Core\Bootstrap;
 use TYPO3\CMS\Core\Http\RequestHandlerInterface;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Install\Controller\AbstractController;
+use TYPO3\CMS\Install\Controller\Exception;
+use TYPO3\CMS\Install\Exception\AuthenticationRequiredException;
 
 /**
  * Default request handler for all requests inside the TYPO3 Install Tool, which does a simple hardcoded
@@ -32,6 +36,16 @@ class RequestHandler implements RequestHandlerInterface
     protected $bootstrap;
 
     /**
+     * @var ServerRequestInterface
+     */
+    protected $request;
+
+    /**
+     * @var \TYPO3\CMS\Install\Service\SessionService
+     */
+    protected $session = null;
+
+    /**
      * Constructor handing over the bootstrap
      *
      * @param Bootstrap $bootstrap
@@ -42,38 +56,51 @@ class RequestHandler implements RequestHandlerInterface
     }
 
     /**
-     * Handles an install tool request
-     * Execute 'tool' or 'step' controller depending on install[controller] GET/POST parameter
+     * Handles an install tool request for normal operations
      *
      * @param ServerRequestInterface $request
      */
     public function handleRequest(ServerRequestInterface $request)
     {
+        $this->request = $request;
         $getPost = !empty($request->getQueryParams()['install']) ? $request->getQueryParams()['install'] : $request->getParsedBody()['install'];
-        switch ($getPost['controller']) {
-            case 'tool':
-                $controllerClassName = \TYPO3\CMS\Install\Controller\ToolController::class;
-                break;
-            case 'ajax':
-                $controllerClassName = \TYPO3\CMS\Install\Controller\AjaxController::class;
-                break;
-            default:
-                $controllerClassName = \TYPO3\CMS\Install\Controller\StepController::class;
+        if ($getPost['controller'] === 'ajax') {
+            $controllerClassName = \TYPO3\CMS\Install\Controller\AjaxController::class;
+        } else {
+            $controllerClassName = \TYPO3\CMS\Install\Controller\ToolController::class;
+        }
+        /** @var AbstractController $controller */
+        $controller = GeneralUtility::makeInstance($controllerClassName);
+        try {
+            $this->initializeSession();
+            $this->checkSessionToken();
+            $this->checkSessionLifetime();
+            $controller->setSessionService($this->session);
+            $controller->execute();
+        } catch (AuthenticationRequiredException $e) {
+            // show the login form
+            $controller->output($controller->loginForm($e->getMessageObject()));
+        } catch (Exception\RedirectException $e) {
+            $controller->redirect();
         }
-        GeneralUtility::makeInstance($controllerClassName)->execute();
     }
 
     /**
-     * This request handler can handle any request when not in CLI mode and the install tool flag is set
-     * please note that both checks are needed, as when in "failsafe" mode, the TYPO3_REQUESTTYPE is not
-     * necessarily set at this point.
+     * This request handler can handle any request when not in CLI mode.
+     * Warning: Order of these methods is security relevant and interferes with different access
+     * conditions (new/existing installation). See the single method comments for details.
      *
      * @param ServerRequestInterface $request
      * @return bool Returns always TRUE
      */
     public function canHandleRequest(ServerRequestInterface $request)
     {
-        return true;
+        return
+            $this->isInstallToolAvailable()
+            && $this->bootstrap->checkIfEssentialConfigurationExists()
+            && !$this->isInitialInstallationInProgress()
+            && $this->isInstallToolPasswordSet()
+        ;
     }
 
     /**
@@ -83,6 +110,133 @@ class RequestHandler implements RequestHandlerInterface
      */
     public function getPriority()
     {
-        return 20;
+        return 50;
+    }
+
+    /**
+     * Checks if ENABLE_INSTALL_TOOL exists.
+     * Does not check for LocalConfiguration.php file as this is done within
+     * Bootstrap->checkIfEssentialConfigurationExists() before.
+     *
+     * @return bool
+     */
+    protected function isInstallToolAvailable()
+    {
+        /** @var \TYPO3\CMS\Install\Service\EnableFileService $installToolEnableService */
+        $installToolEnableService = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Service\EnableFileService::class);
+        return $installToolEnableService->checkInstallToolEnableFile();
+    }
+
+    /**
+     * Checks if first installation is in progress
+     * Does not check for LocalConfiguration.php file as this is done within
+     * Bootstrap->checkIfEssentialConfigurationExists() before.
+     *
+     * @return bool TRUE if installation is in progress
+     */
+    protected function isInitialInstallationInProgress()
+    {
+        return !empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['isInitialInstallationInProgress']);
+    }
+
+    /**
+     * Check if the install tool password is set
+     */
+    protected function isInstallToolPasswordSet()
+    {
+        return !empty($GLOBALS['TYPO3_CONF_VARS']['BE']['installToolPassword']);
+    }
+
+    /**
+     * Initialize session object.
+     * Subclass will throw exception if session can not be created or if
+     * preconditions like a valid encryption key are not set.
+     */
+    protected function initializeSession()
+    {
+        /** @var \TYPO3\CMS\Install\Service\SessionService $session */
+        $this->session = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Service\SessionService::class);
+        if (!$this->session->hasSession()) {
+            $this->session->startSession();
+        }
+    }
+
+    /**
+     * Use form protection API to find out if protected POST forms are ok.
+     *
+     * @throws Exception
+     */
+    protected function checkSessionToken()
+    {
+        $postValues = $this->request->getParsedBody()['install'];
+        $tokenOk = false;
+        if (!empty($postValues)) {
+            // A token must be given as soon as there is POST data
+            if (isset($postValues['token'])) {
+                /** @var $formProtection \TYPO3\CMS\Core\FormProtection\InstallToolFormProtection */
+                $formProtection = \TYPO3\CMS\Core\FormProtection\FormProtectionFactory::get(
+                    \TYPO3\CMS\Core\FormProtection\InstallToolFormProtection::class
+                );
+                $action = (string)$postValues['action'];
+                if ($action === '') {
+                    throw new Exception(
+                        'No POST action given for token check',
+                        1369326593
+                    );
+                }
+                $tokenOk = $formProtection->validateToken($postValues['token'], 'installTool', $action);
+            }
+        } else {
+            $tokenOk = true;
+        }
+
+        $this->handleSessionTokenCheck($tokenOk);
+    }
+
+    /**
+     * If session token was not ok, the session is reset and either
+     * a redirect is initialized (will load the same step step controller again) or
+     * if in install tool, the login form is displayed.
+     *
+     * @param bool $tokenOk
+     * @throws AuthenticationRequiredException if a form token was submitted but was not valid
+     */
+    protected function handleSessionTokenCheck($tokenOk)
+    {
+        if (!$tokenOk) {
+            $this->session->resetSession();
+            $this->session->startSession();
+
+            /** @var $message \TYPO3\CMS\Install\Status\ErrorStatus */
+            $message = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Status\ErrorStatus::class);
+            $message->setTitle('Invalid form token');
+            $message->setMessage(
+                'The form protection token was invalid. You have been logged out, please log in and try again.'
+            );
+            throw new AuthenticationRequiredException('Invalid form token', 1504030810, null, $message);
+        }
+    }
+
+    /**
+     * Check if session expired.
+     * If the session has expired, the login form is displayed.
+     *
+     * @throws AuthenticationRequiredException if the session has expired
+     */
+    protected function checkSessionLifetime()
+    {
+        if ($this->session->isExpired()) {
+            // Session expired, log out user, start new session
+            $this->session->resetSession();
+            $this->session->startSession();
+
+            /** @var $message \TYPO3\CMS\Install\Status\ErrorStatus */
+            $message = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Status\ErrorStatus::class);
+            $message->setTitle('Session expired');
+            $message->setMessage(
+                'Your Install Tool session has expired. You have been logged out, please log in and try again.'
+            );
+            throw new AuthenticationRequiredException('Session expired', 1504030839, null, $message);
+        }
     }
 }