[BUGFIX] Always use backend form protection in backend URI builder 34/45934/2
authorChristian Weiske <weiske@mogic.com>
Fri, 8 Jan 2016 10:52:08 +0000 (11:52 +0100)
committerMarkus Klein <markus.klein@typo3.org>
Fri, 15 Jan 2016 12:34:34 +0000 (13:34 +0100)
Backend edit links in frontend editing used FrontendFormProtection
when a frontend user was logged in.
We force using BackendFormProtection when building URLs with the
backend UriBuilder now.

Resolves: #72582
Releases: master, 7.6
Change-Id: I6cd33c47cfed2eeb30af84c3dc40f8e54f3bf197
Reviewed-on: https://review.typo3.org/45934
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Tested-by: Markus Klein <markus.klein@typo3.org>
typo3/sysext/backend/Classes/Routing/UriBuilder.php
typo3/sysext/core/Classes/FormProtection/FormProtectionFactory.php

index 32f7f33..b1dd6e3 100644 (file)
@@ -81,7 +81,7 @@ class UriBuilder
             // If the route has the "public" option set, no token is generated.
             if ($route->getOption('access') !== 'public') {
                 $parameters = array(
-                    'ajaxToken' => FormProtectionFactory::get()->generateToken('ajaxCall', $name)
+                    'ajaxToken' => FormProtectionFactory::get('backend')->generateToken('ajaxCall', $name)
                 ) + $parameters;
             }
 
@@ -93,7 +93,7 @@ class UriBuilder
             // If the route has the "public" option set, no token is generated.
             if ($route->getOption('access') !== 'public') {
                 $parameters = array(
-                    'token' => FormProtectionFactory::get()->generateToken('route', $name)
+                    'token' => FormProtectionFactory::get('backend')->generateToken('route', $name)
                 ) + $parameters;
             }
 
@@ -119,7 +119,7 @@ class UriBuilder
     {
         $parameters = array(
             'M' => $moduleName,
-            'moduleToken' => FormProtectionFactory::get()->generateToken('moduleCall', $moduleName)
+            'moduleToken' => FormProtectionFactory::get('backend')->generateToken('moduleCall', $moduleName)
         ) + $parameters;
         return $this->buildUri($parameters, $referenceType);
     }
@@ -143,7 +143,7 @@ class UriBuilder
             'ajaxID' => $ajaxIdentifier
         ) + $parameters;
         if (!empty($GLOBALS['TYPO3_CONF_VARS']['BE']['AJAX'][$ajaxIdentifier]['csrfTokenCheck'])) {
-            $parameters['ajaxToken'] = FormProtectionFactory::get()->generateToken('ajaxCall', $ajaxIdentifier);
+            $parameters['ajaxToken'] = FormProtectionFactory::get('backend')->generateToken('ajaxCall', $ajaxIdentifier);
         }
         return $this->buildUri($parameters, $referenceType);
     }
index 56b2e60..084c943 100644 (file)
@@ -14,10 +14,13 @@ namespace TYPO3\CMS\Core\FormProtection;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
+use TYPO3\CMS\Core\Messaging\FlashMessage;
 use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
 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\Lang\LanguageService;
 
 /**
@@ -55,65 +58,65 @@ class FormProtectionFactory
     }
 
     /**
-     * Gets a form protection instance for the requested class $className.
+     * Gets a form protection instance for the requested type or class.
      *
-     * If there already is an existing instance of the requested $className, the
-     * existing instance will be returned. If no $className is provided, the factory
+     * If there already is an existing instance of the requested $classNameOrType, the
+     * existing instance will be returned. If no $classNameOrType is provided, the factory
      * detects the scope and returns the appropriate form protection object.
      *
-     * @param string $className
+     * @param string $classNameOrType Name of a form protection class, or one
+     *                                of the pre-defined form protection types:
+     *                                frontend, backend, installtool
      * @return \TYPO3\CMS\Core\FormProtection\AbstractFormProtection the requested instance
      */
-    public static function get($className = 'default')
+    public static function get($classNameOrType = 'default')
     {
-        if (isset(self::$instances[$className])) {
-            return self::$instances[$className];
+        if (isset(self::$instances[$classNameOrType])) {
+            return self::$instances[$classNameOrType];
         }
-        if ($className === 'default') {
-            $classNameAndConstructorArguments = self::getClassNameAndConstructorArgumentsByState();
+        if ($classNameOrType === 'default' || $classNameOrType === 'installtool' || $classNameOrType === 'frontend' || $classNameOrType === 'backend') {
+            $classNameAndConstructorArguments = self::getClassNameAndConstructorArgumentsByType($classNameOrType);
         } else {
             $classNameAndConstructorArguments = func_get_args();
         }
-        self::$instances[$className] = self::createInstance($classNameAndConstructorArguments);
-        return self::$instances[$className];
+        self::$instances[$classNameOrType] = self::createInstance($classNameAndConstructorArguments);
+        return self::$instances[$classNameOrType];
     }
 
     /**
-     * Returns the class name depending on TYPO3_MODE and
-     * active backend session.
+     * Returns the class name and parameters depending on the given type.
+     * If the type cannot be used currently, protection is disabled.
      *
-     * @return array
+     * @param string $type Valid types: default, installtool, frontend, backend. "default" makes an autodection on the current state
+     * @return array Array of arguments
      */
-    protected static function getClassNameAndConstructorArgumentsByState()
+    protected static function getClassNameAndConstructorArgumentsByType($type)
     {
-        switch (true) {
-            case self::isInstallToolSession():
-                $classNameAndConstructorArguments = [
-                    InstallToolFormProtection::class
-                ];
-                break;
-            case self::isFrontendSession():
-                $classNameAndConstructorArguments = [
-                    FrontendFormProtection::class,
-                    $GLOBALS['TSFE']->fe_user
-                ];
-                break;
-            case self::isBackendSession():
-                $classNameAndConstructorArguments = [
-                    BackendFormProtection::class,
-                    $GLOBALS['BE_USER'],
-                    GeneralUtility::makeInstance(Registry::class),
-                    self::getMessageClosure(
-                        $GLOBALS['LANG'],
-                        GeneralUtility::makeInstance(FlashMessageService::class)->getMessageQueueByIdentifier(),
-                        (bool)(TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_AJAX)
-                    )
-                ];
-                break;
-            default:
-                $classNameAndConstructorArguments = [
-                    DisabledFormProtection::class
-                ];
+        if (self::isInstallToolSession() && ($type === 'default' || $type === 'installtool')) {
+            $classNameAndConstructorArguments = [
+                InstallToolFormProtection::class
+            ];
+        } elseif (self::isFrontendSession() && ($type === 'default' || $type === 'frontend')) {
+            $classNameAndConstructorArguments = [
+                FrontendFormProtection::class,
+                $GLOBALS['TSFE']->fe_user
+            ];
+        } elseif (self::isBackendSession() && ($type === 'default' || $type === 'backend')) {
+            $classNameAndConstructorArguments = [
+                BackendFormProtection::class,
+                $GLOBALS['BE_USER'],
+                GeneralUtility::makeInstance(Registry::class),
+                self::getMessageClosure(
+                    $GLOBALS['LANG'],
+                    GeneralUtility::makeInstance(FlashMessageService::class)->getMessageQueueByIdentifier(),
+                    (bool)(TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_AJAX)
+                )
+            ];
+        } else {
+            // failed to use preferred type, disable form protection
+            $classNameAndConstructorArguments = [
+                DisabledFormProtection::class
+            ];
         }
         return $classNameAndConstructorArguments;
     }
@@ -135,7 +138,7 @@ class FormProtectionFactory
      */
     protected static function isBackendSession()
     {
-        return isset($GLOBALS['BE_USER']) && $GLOBALS['BE_USER'] instanceof \TYPO3\CMS\Core\Authentication\BackendUserAuthentication && isset($GLOBALS['BE_USER']->user['uid']);
+        return isset($GLOBALS['BE_USER']) && $GLOBALS['BE_USER'] instanceof BackendUserAuthentication && isset($GLOBALS['BE_USER']->user['uid']);
     }
 
     /**
@@ -145,7 +148,7 @@ class FormProtectionFactory
      */
     protected static function isFrontendSession()
     {
-        return TYPO3_MODE === 'FE' && is_object($GLOBALS['TSFE']) && $GLOBALS['TSFE']->fe_user instanceof \TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication && isset($GLOBALS['TSFE']->fe_user->user['uid']);
+        return TYPO3_MODE === 'FE' && is_object($GLOBALS['TSFE']) && $GLOBALS['TSFE']->fe_user instanceof FrontendUserAuthentication && isset($GLOBALS['TSFE']->fe_user->user['uid']);
     }
 
     /**
@@ -158,12 +161,12 @@ class FormProtectionFactory
     public static function getMessageClosure(LanguageService $languageService, FlashMessageQueue $messageQueue, $isAjaxCall)
     {
         return function () use ($languageService, $messageQueue, $isAjaxCall) {
-            /** @var \TYPO3\CMS\Core\Messaging\FlashMessage $flashMessage */
+            /** @var FlashMessage $flashMessage */
             $flashMessage = GeneralUtility::makeInstance(
-                \TYPO3\CMS\Core\Messaging\FlashMessage::class,
+                FlashMessage::class,
                 $languageService->sL('LLL:EXT:lang/locallang_core.xlf:error.formProtection.tokenInvalid'),
                 '',
-                \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR,
+                FlashMessage::ERROR,
                 !$isAjaxCall
             );
             $messageQueue->enqueue($flashMessage);
@@ -184,7 +187,7 @@ class FormProtectionFactory
         if (!class_exists($className)) {
             throw new \InvalidArgumentException('$className must be the name of an existing class, but ' . 'actually was "' . $className . '".', 1285352962);
         }
-        $instance = call_user_func_array([\TYPO3\CMS\Core\Utility\GeneralUtility::class, 'makeInstance'], $classNameAndConstructorArguments);
+        $instance = call_user_func_array([GeneralUtility::class, 'makeInstance'], $classNameAndConstructorArguments);
         if (!$instance instanceof AbstractFormProtection) {
             throw new \InvalidArgumentException('$className must be a subclass of ' . AbstractFormProtection::class . ', but actually was "' . $className . '".', 1285353026);
         }
@@ -198,13 +201,13 @@ class FormProtectionFactory
      * Note: This function is intended for testing purposes only.
      *
      * @access private
-     * @param string $className
+     * @param string $classNameOrType
      * @param AbstractFormProtection $instance
      * @return void
      */
-    public static function set($className, AbstractFormProtection $instance)
+    public static function set($classNameOrType, AbstractFormProtection $instance)
     {
-        self::$instances[$className] = $instance;
+        self::$instances[$classNameOrType] = $instance;
     }
 
     /**