[TASK] Avoid usages of TYPO3_MODE 98/64598/3
authorBenni Mack <benni@typo3.org>
Fri, 29 May 2020 11:04:18 +0000 (13:04 +0200)
committerBenjamin Franzke <bfr@qbus.de>
Tue, 2 Jun 2020 14:37:21 +0000 (16:37 +0200)
All code that is using the constant TYPO3_MODE is hard to test,
as this needs to be encapsulated into various places. All testing framework
places run with TYPO3_MODE=FE which makes it impossible to even
consider making parts of the testing framework compatible running a pure
Frontend-based request in the future.

On top, the constant covers up cross-dependency between core dependencies
(whereas $GLOBALS[TSFE]->fe_user is actually a dependency to EXT:frontend).

Another testing-helper in Extbase's EnvironmentService allows to switch
within Extbase to simulate Frontend behaviour.

Resolves: #91521
Releases: master, 10.4
Change-Id: I85a34029e399b40d0780f907480f9059bfdb0edb
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/64598
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: Benjamin Franzke <bfr@qbus.de>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Benjamin Franzke <bfr@qbus.de>
typo3/sysext/backend/Classes/Template/ModuleTemplate.php
typo3/sysext/core/Classes/Authentication/AuthenticationService.php
typo3/sysext/core/Classes/Collection/RecordCollectionRepository.php
typo3/sysext/core/Classes/FormProtection/FormProtectionFactory.php
typo3/sysext/core/Classes/Messaging/FlashMessageQueue.php
typo3/sysext/core/Classes/Resource/AbstractRepository.php
typo3/sysext/core/Tests/Functional/Fixtures/Frontend/AdditionalConfiguration.php
typo3/sysext/core/Tests/Unit/Authentication/AuthenticationServiceTest.php
typo3/sysext/extbase/Classes/Service/EnvironmentService.php
typo3/sysext/form/Classes/Domain/Runtime/FormRuntime.php

index f29f626..3ff85f2 100644 (file)
@@ -255,7 +255,7 @@ class ModuleTemplate
     {
         $this->pageRenderer->loadRequireJsModule('bootstrap');
 
-        if (TYPO3_MODE === 'BE' && $this->getBackendUserAuthentication() && !empty($this->getBackendUserAuthentication()->user)) {
+        if ($this->getBackendUserAuthentication() && !empty($this->getBackendUserAuthentication()->user)) {
             $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ContextHelp');
             $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/DocumentHeader');
         }
index 4e5f79c..54b9a2b 100644 (file)
@@ -125,7 +125,7 @@ class AuthenticationService extends AbstractAuthenticationService
         // Get a hashed password instance for the hash stored in db of this user
         $invalidPasswordHashException = null;
         try {
-            $hashInstance = $saltFactory->get($passwordHashInDatabase, TYPO3_MODE);
+            $hashInstance = $saltFactory->get($passwordHashInDatabase, $this->pObj->loginType);
         } catch (InvalidPasswordHashException $invalidPasswordHashException) {
             // Could not find a responsible hash algorithm for given password. This is unusual since other
             // authentication services would usually be called before this one with higher priority. We thus log
@@ -140,7 +140,7 @@ class AuthenticationService extends AbstractAuthenticationService
 
         // An instance of the currently configured salted password mechanism
         // Don't catch InvalidPasswordHashException here: Only install tool should handle those configuration failures
-        $defaultHashInstance = $saltFactory->getDefaultHashInstance(TYPO3_MODE);
+        $defaultHashInstance = $saltFactory->getDefaultHashInstance($this->pObj->loginType);
 
         // We found a hash class that can handle this type of hash
         $isValidPassword = $hashInstance->checkPassword($submittedPassword, $passwordHashInDatabase);
@@ -163,7 +163,7 @@ class AuthenticationService extends AbstractAuthenticationService
 
         if (!$isValidPassword) {
             // Failed login attempt - wrong password
-            $this->writeLogMessage(TYPO3_MODE . ' Authentication failed - wrong password for username \'%s\'', $submittedUsername);
+            $this->writeLogMessage($this->pObj->loginType . ' Authentication failed - wrong password for username \'%s\'', $submittedUsername);
             $message = 'Login-attempt from ###IP###, username \'%s\', password not accepted!';
             $this->writelog(SystemLogType::LOGIN, SystemLogLoginAction::ATTEMPT, SystemLogErrorClassification::SECURITY_NOTICE, 1, $message, [$submittedUsername]);
             $this->logger->info(sprintf($message, $submittedUsername));
@@ -191,7 +191,7 @@ class AuthenticationService extends AbstractAuthenticationService
         }
 
         // Responsible, authentication ok, domain lock ok. Log successful login and return 'auth ok, do NOT check other services'
-        $this->writeLogMessage(TYPO3_MODE . ' Authentication successful for username \'%s\'', $submittedUsername);
+        $this->writeLogMessage($this->pObj->loginType . ' Authentication successful for username \'%s\'', $submittedUsername);
         return 200;
     }
 
@@ -361,7 +361,7 @@ class AuthenticationService extends AbstractAuthenticationService
         if (!empty($params)) {
             $message = vsprintf($message, $params);
         }
-        if (TYPO3_MODE === 'FE') {
+        if ($this->pObj->loginType === 'FE') {
             $timeTracker = GeneralUtility::makeInstance(TimeTracker::class);
             $timeTracker->setTSlogMessage($message);
         }
index afca373..087f97f 100644 (file)
@@ -19,6 +19,7 @@ use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
 use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
 
 /**
  * Implements the repository for record collections.
@@ -221,13 +222,13 @@ class RecordCollectionRepository
     }
 
     /**
-     * Function to return the current TYPO3_MODE.
+     * Function to return the current TYPO3_MODE (FE/BE) based on $GLOBALS[TSFE].
      * This function can be mocked in unit tests to be able to test frontend behaviour.
      *
      * @return string
      */
-    protected function getEnvironmentMode()
+    protected function getEnvironmentMode(): string
     {
-        return TYPO3_MODE;
+        return ($GLOBALS['TSFE'] ?? null) instanceof TypoScriptFrontendController ? 'FE' : 'BE';
     }
 }
index 7e53b69..7043b4a 100644 (file)
@@ -23,6 +23,7 @@ use TYPO3\CMS\Core\Messaging\FlashMessageService;
 use TYPO3\CMS\Core\Registry;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
+use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
 
 /**
  * This class creates and manages instances of the various form protection
@@ -150,7 +151,7 @@ class FormProtectionFactory
      */
     protected static function isFrontendSession()
     {
-        return TYPO3_MODE === 'FE' && is_object($GLOBALS['TSFE']) && $GLOBALS['TSFE']->fe_user instanceof FrontendUserAuthentication && isset($GLOBALS['TSFE']->fe_user->user['uid']);
+        return ($GLOBALS['TSFE'] ?? null) instanceof TypoScriptFrontendController && $GLOBALS['TSFE']->fe_user instanceof FrontendUserAuthentication && isset($GLOBALS['TSFE']->fe_user->user['uid']);
     }
 
     /**
index ac555c0..fb7338d 100644 (file)
@@ -19,6 +19,8 @@ use TYPO3\CMS\Core\Authentication\AbstractUserAuthentication;
 use TYPO3\CMS\Core\Exception;
 use TYPO3\CMS\Core\Messaging\Renderer\FlashMessageRendererInterface;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
+use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
 
 /**
  * A class which collects and renders flash messages.
@@ -200,7 +202,10 @@ class FlashMessageQueue extends \SplQueue implements \JsonSerializable
      */
     protected function getUserByContext()
     {
-        return TYPO3_MODE === 'BE' ? $GLOBALS['BE_USER'] : $GLOBALS['TSFE']->fe_user;
+        if (($GLOBALS['TSFE'] ?? null) instanceof TypoScriptFrontendController && $GLOBALS['TSFE']->fe_user instanceof FrontendUserAuthentication) {
+            return $GLOBALS['TSFE']->fe_user;
+        }
+        return $GLOBALS['BE_USER'];
     }
 
     /**
index 2a641ca..fde1c46 100644 (file)
@@ -22,6 +22,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\MathUtility;
 use TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface;
 use TYPO3\CMS\Extbase\Persistence\RepositoryInterface;
+use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
 
 /**
  * Abstract repository implementing the basic repository methods
@@ -292,13 +293,13 @@ abstract class AbstractRepository implements RepositoryInterface, SingletonInter
     }
 
     /**
-     * Function to return the current TYPO3_MODE.
+     * Function to return the current TYPO3_MODE based on $GLOBALS['TSFE'].
      * This function can be mocked in unit tests to be able to test frontend behaviour.
      *
      * @return string
      */
     protected function getEnvironmentMode()
     {
-        return TYPO3_MODE;
+        return ($GLOBALS['TSFE'] ?? null) instanceof TypoScriptFrontendController ? 'FE' : 'BE';
     }
 }
index 6cc3512..5fe5530 100644 (file)
@@ -9,7 +9,5 @@ defined('TYPO3_MODE') or die();
 $GLOBALS['TYPO3_CONF_VARS']['FE']['debug'] = false;
 
 // Register hooks for frontend test
-if (TYPO3_MODE === 'FE') {
-    $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['Core/TypoScript/TemplateService']['runThroughTemplatesPostProcessing']['FunctionalTest'] =
-        \TYPO3\TestingFramework\Core\Functional\Framework\Frontend\Hook\TypoScriptInstructionModifier::class . '->apply';
-}
+$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['Core/TypoScript/TemplateService']['runThroughTemplatesPostProcessing']['FunctionalTest'] =
+    \TYPO3\TestingFramework\Core\Functional\Framework\Frontend\Hook\TypoScriptInstructionModifier::class . '->apply';
index a3856b3..e7c6237 100644 (file)
@@ -136,6 +136,7 @@ class AuthenticationServiceTest extends UnitTestCase
     {
         $subject = new AuthenticationService();
         $pObjProphecy = $this->prophesize(AbstractUserAuthentication::class);
+        $pObjProphecy->loginType = 'BE';
         $loggerProphecy = $this->prophesize(Logger::class);
         $subject->setLogger($loggerProphecy->reveal());
         $subject->initAuth(
@@ -164,6 +165,7 @@ class AuthenticationServiceTest extends UnitTestCase
     {
         $subject = new AuthenticationService();
         $pObjProphecy = $this->prophesize(AbstractUserAuthentication::class);
+        $pObjProphecy->loginType = 'BE';
         $loggerProphecy = $this->prophesize(Logger::class);
         $subject->setLogger($loggerProphecy->reveal());
         $subject->initAuth(
@@ -193,6 +195,7 @@ class AuthenticationServiceTest extends UnitTestCase
     {
         $subject = new AuthenticationService();
         $pObjProphecy = $this->prophesize(AbstractUserAuthentication::class);
+        $pObjProphecy->loginType = 'BE';
         $loggerProphecy = $this->prophesize(Logger::class);
         $subject->setLogger($loggerProphecy->reveal());
         $subject->initAuth(
@@ -222,6 +225,7 @@ class AuthenticationServiceTest extends UnitTestCase
     {
         $subject = new AuthenticationService();
         $pObjProphecy = $this->prophesize(AbstractUserAuthentication::class);
+        $pObjProphecy->loginType = 'BE';
         $loggerProphecy = $this->prophesize(Logger::class);
         $subject->setLogger($loggerProphecy->reveal());
         $subject->initAuth(
index d20a0ec..85249a3 100644 (file)
@@ -26,12 +26,21 @@ use TYPO3\CMS\Core\SingletonInterface;
 class EnvironmentService implements SingletonInterface
 {
     /**
+     * @var bool|null
+     */
+    protected $isFrontendMode;
+
+    /**
      * Detects if TYPO3_MODE is defined and its value is "FE"
      *
      * @return bool
      */
     public function isEnvironmentInFrontendMode(): bool
     {
+        $this->initialize();
+        if ($this->isFrontendMode !== null) {
+            return $this->isFrontendMode;
+        }
         return (defined('TYPO3_MODE') && TYPO3_MODE === 'FE') ?: false;
     }
 
@@ -42,6 +51,27 @@ class EnvironmentService implements SingletonInterface
      */
     public function isEnvironmentInBackendMode(): bool
     {
-        return (defined('TYPO3_MODE') && TYPO3_MODE === 'BE') ?: false;
+        return !$this->isEnvironmentInFrontendMode();
+    }
+
+    protected function initialize(): void
+    {
+        if ($this->isFrontendMode !== null) {
+            return;
+        }
+        if (defined('TYPO3_MODE')) {
+            $this->isFrontendMode = TYPO3_MODE === 'FE';
+        }
+    }
+
+    /**
+     * A helper method for tests to simulate TYPO3_MODE behavior, should only be used within TYPO3 Core
+     *
+     * @param bool $isFrontendMode
+     * @internal only used for testing purposes and can be removed at any time.
+     */
+    public function setFrontendMode(bool $isFrontendMode): void
+    {
+        $this->isFrontendMode = $isFrontendMode;
     }
 }
index 6f8b270..607bd5a 100644 (file)
@@ -1008,7 +1008,7 @@ class FormRuntime implements RootRenderableInterface, \ArrayAccess
             $pageId = 0;
             $languageId = (int)GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('language', 'id', 0);
 
-            if (TYPO3_MODE === 'FE') {
+            if ($this->getTypoScriptFrontendController() !== null) {
                 $pageId = $this->getTypoScriptFrontendController()->id;
             }
 
@@ -1088,10 +1088,10 @@ class FormRuntime implements RootRenderableInterface, \ArrayAccess
     }
 
     /**
-     * @return TypoScriptFrontendController
+     * @return TypoScriptFrontendController|null
      */
-    protected function getTypoScriptFrontendController(): TypoScriptFrontendController
+    protected function getTypoScriptFrontendController(): ?TypoScriptFrontendController
     {
-        return $GLOBALS['TSFE'];
+        return $GLOBALS['TSFE'] ?? null;
     }
 }