Index: t3lib/class.t3lib_beuserauth.php =================================================================== --- t3lib/class.t3lib_beuserauth.php (revision 8881) +++ t3lib/class.t3lib_beuserauth.php (working copy) @@ -393,6 +393,16 @@ return $isUserAllowedToLogin; } + + /** + * Logs out the current user and clears the form protection tokens. + */ + public function logoff() { + t3lib_formProtection_Factory::get( + 't3lib_formProtection_Backend' + )->clean(); + parent::logoff(); + } } Index: t3lib/core_autoload.php =================================================================== --- t3lib/core_autoload.php (revision 8881) +++ t3lib/core_autoload.php (working copy) @@ -116,6 +116,9 @@ 't3lib_error_exceptionhandlerinterface' => PATH_t3lib . 'error/interface.t3lib_error_exceptionhandlerinterface.php', 't3lib_browselinkshook' => PATH_t3lib . 'interfaces/interface.t3lib_browselinkshook.php', 't3lib_extfilefunctions_processdatahook' => PATH_t3lib . 'interfaces/interface.t3lib_extfilefunctions_processdatahook.php', + 't3lib_formprotection_factory' => PATH_t3lib . 'formprotection/class.t3lib_formprotection_factory.php', + 't3lib_formprotection_abstract' => PATH_t3lib . 'formprotection/class.t3lib_formprotection_abstract.php', + 't3lib_formprotection_backend' => PATH_t3lib . 'formprotection/class.t3lib_formprotection_backend.php', 't3lib_localrecordlistgettablehook' => PATH_t3lib . 'interfaces/interface.t3lib_localrecordlistgettablehook.php', 't3lib_pageselect_getpagehook' => PATH_t3lib . 'interfaces/interface.t3lib_pageselect_getpagehook.php', 't3lib_pageselect_getrecordoverlayhook' => PATH_t3lib . 'interfaces/interface.t3lib_pageselect_getrecordoverlayhook.php', Index: t3lib/formprotection/class.t3lib_formprotection_backend.php =================================================================== --- t3lib/formprotection/class.t3lib_formprotection_backend.php (revision 0) +++ t3lib/formprotection/class.t3lib_formprotection_backend.php (revision 0) @@ -0,0 +1,179 @@ + +* All rights reserved +* +* This script is part of the TYPO3 project. The TYPO3 project is +* free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* The GNU General Public License can be found at +* http://www.gnu.org/copyleft/gpl.html. +* +* This script is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* This copyright notice MUST APPEAR in all copies of the script! +***************************************************************/ + +/** + * Class t3lib_formProtection_Backend. + * + * This class provides protection against cross-site request forgery (XSRF/CSRF) + * for forms in the BE. + * + * How to use: + * + * For each form in the BE (or link that changes some data), create a token and + * insert is as a hidden form element. The name of the form element does not + * matter; you only need it to get the form token for verifying it. + * + *
+ * $formToken = t3lib_formProtection_Factory::get(
+ *     t3lib_formProtection_Factory::TYPE_BACK_END
+ * )->generateToken(
+ *     'BE user setup', 'edit'
+ * );
+ * $this->content .= '';
+ * 
+ * + * The three parameters $formName, $action and $formInstanceName can be + * arbitrary strings, but they should make the form token as specific as + * possible. For different forms (e.g. BE user setup and editing a tt_content + * record) or different records (with different UIDs) from the same table, + * those values should be different. + * + * For editing a tt_content record, the call could look like this: + * + *
+ * $formToken = t3lib_formProtection_Factory::get(
+ *     t3lib_formProtection_Factory::TYPE_BACK_END
+ * )->getFormProtection()->generateToken(
+ *    'tt_content', 'edit', $uid
+ * );
+ * 
+ * + * At the end of the form, you need to persist the tokens. This makes sure that + * generated tokens get saved, and also that removed tokens stay removed: + * + *
+ * t3lib_formProtection_Factory::get(
+ *     t3lib_formProtection_Factory::TYPE_BACK_END
+ * )->persistTokens();
+ * 
+ * + * In BE lists, it might be necessary to generate hundreds of tokens. So the + * tokens do not get automatically persisted after creation for performance + * reasons. + * + * + * When processing the data that has been submitted by the form, you can check + * that the form token is valid like this: + * + *
+ * if ($dataHasBeenSubmitted && t3lib_formProtection_Factory::get(
+ *         t3lib_formProtection_Factory::TYPE_BACK_END
+ *     )->validateToken(
+ *         (string) t3lib_div::_POST('formToken'),
+ *         'BE user setup', 'edit
+ *     )
+ * ) {
+ *     // processes the data
+ * } else {
+ *     // no need to do anything here as the BE form protection will create a
+ *     // flash message for an invalid token
+ * }
+ * 
+ * + * Note that validateToken invalidates the token with the token ID. So calling + * validate with the same parameters two times in a row will always return FALSE + * for the second call. + * + * It is important that the tokens get validated before the tokens are + * persisted. This makes sure that the tokens that get invalidated by + * validateToken cannot be used again. + * + * $Id$ + * + * @package TYPO3 + * @subpackage t3lib + * + * @author Oliver Klee + */ +class t3lib_formProtection_Backend extends t3lib_formProtection_Abstract { + /** + * the maximum number of tokens that can exist at the same time + * + * @var integer + */ + protected $maximumNumberOfTokens = 20000; + + /** + * Only allow construction if we have a backend session + */ + public function __construct() { + if (!isset($GLOBALS['BE_USER'])) { + throw new t3lib_error_Exception( + 'A back-end form protection may only be instantiated if there' . + ' is an active back-end session.', + 1285067843 + ); + } + parent::__construct(); + } + + /** + * Creates or displayes an error message telling the user that the submitted + * form token is invalid. + * + * @return void + */ + protected function createValidationErrorMessage() { + $message = t3lib_div::makeInstance( + 't3lib_FlashMessage', + $GLOBALS['LANG']->sL( + 'LLL:EXT:lang/locallang_core.xml:error.formProtection.tokenInvalid' + ), + '', + t3lib_FlashMessage::ERROR + ); + t3lib_FlashMessageQueue::addMessage($message); + } + + /** + * Retrieves all saved tokens. + * + * @return array + * the saved tokens as, will be empty if no tokens have been saved + */ + protected function retrieveTokens() { + $tokens = $GLOBALS['BE_USER']->getSessionData('formTokens'); + if (!is_array($tokens)) { + $tokens = array(); + } + + $this->tokens = $tokens; + } + + /** + * Saves the tokens so that they can be used by a later incarnation of this + * class. + * + * @return void + */ + public function persistTokens() { + $GLOBALS['BE_USER']->setAndSaveSessionData('formTokens', $this->tokens); + } +} + +if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/formprotection/class.t3lib_formprotection_backend.php']) { + include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/formprotection/class.t3lib_formprotection_backend.php']); +} +?> \ No newline at end of file Property changes on: t3lib/formprotection/class.t3lib_formprotection_backend.php ___________________________________________________________________ Added: svn:mime-type + text/plain Index: t3lib/formprotection/class.t3lib_formprotection_factory.php =================================================================== --- t3lib/formprotection/class.t3lib_formprotection_factory.php (revision 0) +++ t3lib/formprotection/class.t3lib_formprotection_factory.php (revision 0) @@ -0,0 +1,143 @@ + +* All rights reserved +* +* This script is part of the TYPO3 project. The TYPO3 project is +* free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* The GNU General Public License can be found at +* http://www.gnu.org/copyleft/gpl.html. +* +* This script is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* This copyright notice MUST APPEAR in all copies of the script! +***************************************************************/ + +/** + * Class t3lib_formProtection_Factory. + * + * This class creates and manages instances of the various form protection + * classes. + * + * This class provides only static methods. It can not be instantiated. + * + * Usage for the back-end form protection: + * + *
+ * $formProtection = t3lib_formProtection_Factory::get(
+ *     't3lib_formProtection_BackEnd'
+ * );
+ * 
+ * + * Usage for the install tool form protection: + * + *
+ * $formProtection = t3lib_formProtection_Factory::get(
+ *     'tx_install_formprotection'
+ * );
+ * $formProtection->injectInstallTool($this);
+ * 
+ * + * $Id$ + * + * @package TYPO3 + * @subpackage t3lib + * + * @author Oliver Klee + * @author Ernesto Baschny + */ +final class t3lib_formProtection_Factory { + /** + * created instances of form protections using the type as array key + * + * @var array + */ + static protected $instances = array(); + + /** + * Private constructor to prevent instantiation. + */ + private function __construct() {} + + /** + * Gets a form protection instance for the requested class $className. + * + * If there already is an existing instance of the requested $className, the + * existing instance will be returned. + * + * @param string $className + * the name of the class for which to return an instance, must be + * "t3lib_formProtection_BackEnd" or "tx_install_FormProtection" + * + * @return t3lib_formProtection_Abstract the requested instance + */ + static public function get($className) { + if (!isset(self::$instances[$className])) { + if (!class_exists($className, TRUE)) { + throw new InvalidArgumentException( + '$className must be the name of an existing class, but ' . + 'actually was "' . $className . '".', + 1285352962 + ); + } + + $instance = t3lib_div::makeInstance($className); + if (!$instance instanceof t3lib_formProtection_Abstract) { + throw new InvalidArgumentException( + '$className must be a subclass of ' . + 't3lib_formProtection_Abstract, but actually was "' . + $className . '".', + 1285353026 + ); + } + self::$instances[$className] = $instance; + } + return self::$instances[$className]; + } + + /** + * Sets the instance that will be returned by get() for a specific class + * name. + * + * Note: This function is intended for testing purposes only. + * + * @param string $className + * the name of the class for which to set an instance, must be + * "t3lib_formProtection_BackEnd" or "tx_install_FormProtection" + * @param t3lib_formProtection_Abstract $instance + * the instance to set + * + * @return void + */ + static public function set($className, t3lib_formProtection_Abstract $instance) { + self::$instances[$className] = $instance; + } + + /** + * Purges all existing instances. + * + * This function is particularly useful when cleaning up in unit testing. + * + * @return void + */ + static public function purgeInstances() { + foreach (self::$instances as $key => $instance) { + $instance->__destruct(); + unset(self::$instances[$key]); + } + } +} + +if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/formprotection/class.t3lib_formprotection_factory.php']) { + include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/formprotection/class.t3lib_formprotection_factory.php']); +} +?> \ No newline at end of file Property changes on: t3lib/formprotection/class.t3lib_formprotection_factory.php ___________________________________________________________________ Added: svn:mime-type + text/plain Index: t3lib/formprotection/class.t3lib_formprotection_abstract.php =================================================================== --- t3lib/formprotection/class.t3lib_formprotection_abstract.php (revision 0) +++ t3lib/formprotection/class.t3lib_formprotection_abstract.php (revision 0) @@ -0,0 +1,247 @@ + +* All rights reserved +* +* This script is part of the TYPO3 project. The TYPO3 project is +* free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* The GNU General Public License can be found at +* http://www.gnu.org/copyleft/gpl.html. +* +* This script is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* This copyright notice MUST APPEAR in all copies of the script! +***************************************************************/ + +/** + * Class t3lib_formProtection_Abstract. + * + * This class provides protection against cross-site request forgery (XSRF/CSRF) + * for forms. + * + * For documentation on how to use this class, please see the documentation of + * the corresponding subclasses, e.g. t3lib_formProtection_Backend. + * + * $Id$ + * + * @package TYPO3 + * @subpackage t3lib + * + * @author Oliver Klee + */ +abstract class t3lib_formProtection_Abstract { + /** + * the maximum number of tokens that can exist at the same time + * + * @var integer + */ + protected $maximumNumberOfTokens = 0; + + /** + * Valid tokens sorted from oldest to newest. + * + * [tokenId] => array(formName, formInstanceName) + * + * @var array + */ + protected $tokens = array(); + + /** + * Constructor. Makes sure existing tokens are read and available for + * checking. + */ + public function __construct() { + $this->retrieveTokens(); + } + + /** + * Frees as much memory as possible. + */ + public function __destruct() { + $this->tokens = array(); + } + + /** + * Deletes all existing tokens and persists the (empty) token table. + * + * This function is intended to be called when a user logs on or off. + * + * @return void + */ + public function clean() { + $this->tokens = array(); + $this->persistTokens(); + } + + /** + * Generates and stores a token for a form. + * + * Calling this function two times with the same parameters will create + * two valid, different tokens. + * + * Generating more tokens than $maximumNumberOfEntries will cause the oldest + * tokens to get dropped. + * + * Note: This function does not persist the tokens. + * + * @param string $formName + * the name of the form, for example a table name like "tt_content", + * or some other identifier like "install_tool_password", must not be + * empty + * @param string $action + * the name of the action of the form, for example "new", "delete" or + * "edit", may also be empty + * @param string $formInstanceName + * a string used to differentiate two instances of the same form, + * form example a record UID or a comma-separated list of UIDs, + * may also be empty + * + * @return string the 32-character hex ID of the generated token + */ + public function generateToken( + $formName, $action = '', $formInstanceName = '' + ) { + if ($formName == '') { + throw new InvalidArgumentException('$formName must not be empty.'); + } + + do { + $tokenId = bin2hex(t3lib_div::generateRandomBytes(16)); + } while (isset($this->tokens[$tokenId])); + + $this->tokens[$tokenId] = array( + 'formName' => $formName, + 'action' => $action, + 'formInstanceName' => $formInstanceName, + ); + $this->preventOverflow(); + + return $tokenId; + } + + /** + * Checks whether the token $tokenId is valid in the form $formName with + * $formInstanceName. + * + * A token is valid if $tokenId, $formName and $formInstanceName match and + * the token has not been used yet. + * + * Calling this function will mark the token $tokenId as invalud (if it + * exists). + * + * So calling this function with the same parameters two times will return + * FALSE the second time. + * + * @param string $tokenId + * a form token to check, may also be empty or utterly misformed + * @param string $formName + * the name of the form to check, for example "tt_content", + * may also be empty or utterly misformed + * @param string $action + * the action of the form to check, for example "edit", + * may also be empty or utterly misformed + * @param string $formInstanceName + * the instance name of the form to check, for example "42" or "foo" + * or "31,42", may also be empty or utterly misformed + * + * @return boolean + * TRUE if $tokenId, $formName, $action and $formInstanceName match + * and the token has not been used yet, FALSE otherwise + */ + public function validateToken( + $tokenId, $formName, $action = '', $formInstanceName = '' + ) { + if (isset($this->tokens[$tokenId])) { + $token = $this->tokens[$tokenId]; + $isValid = ($token['formName'] == $formName) + && ($token['action'] == $action) + && ($token['formInstanceName'] == $formInstanceName); + $this->dropToken($tokenId); + } else { + $isValid = FALSE; + } + + if (!$isValid) { + $this->createValidationErrorMessage(); + } + + return $isValid; + } + + /** + * Creates or displayes an error message telling the user that the submitted + * form token is invalid. + * + * This function may also be empty if the validation error should be handled + * silently. + * + * @return void + */ + abstract protected function createValidationErrorMessage(); + + /** + * Retrieves all saved tokens. + * + * @return array + * the saved tokens, will be empty if no tokens have been saved + */ + abstract protected function retrieveTokens(); + + /** + * Saves the tokens so that they can be used by a later incarnation of this + * class. + * + * @return void + */ + abstract public function persistTokens(); + + /** + * Drops the token with the ID $tokenId. + * + * If there is no token with that ID, this function is a no-op. + * + * Note: This function does not persist the tokens. + * + * @param string $tokenId + * the 32-character ID of an existing token, must not be empty + * + * @return void + */ + protected function dropToken($tokenId) { + if (isset($this->tokens[$tokenId])) { + unset($this->tokens[$tokenId]); + } + } + + /** + * Checks whether the number of current tokens still is at most + * $this->maximumNumberOfTokens. + * + * If there are more tokens, the oldest tokens are removed until the number + * of tokens is low enough. + * + * Note: This function does not persist the tokens. + * + * @return void + */ + protected function preventOverflow() { + if (empty($this->tokens)) { + return; + } + + while (count($this->tokens) > $this->maximumNumberOfTokens) { + reset($this->tokens); + $this->dropToken(key($this->tokens)); + } + } +} +?> \ No newline at end of file Property changes on: t3lib/formprotection/class.t3lib_formprotection_abstract.php ___________________________________________________________________ Added: svn:mime-type + text/plain Index: typo3/sysext/install/Resources/Private/Templates/AlterPasswordForm.html =================================================================== --- typo3/sysext/install/Resources/Private/Templates/AlterPasswordForm.html (revision 8881) +++ typo3/sysext/install/Resources/Private/Templates/AlterPasswordForm.html (working copy) @@ -23,6 +23,7 @@
  • +
  • Index: typo3/sysext/install/mod/class.tx_install_formprotection.php =================================================================== --- typo3/sysext/install/mod/class.tx_install_formprotection.php (revision 0) +++ typo3/sysext/install/mod/class.tx_install_formprotection.php (revision 0) @@ -0,0 +1,168 @@ + +* All rights reserved +* +* This script is part of the TYPO3 project. The TYPO3 project is +* free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* The GNU General Public License can be found at +* http://www.gnu.org/copyleft/gpl.html. +* +* This script is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* This copyright notice MUST APPEAR in all copies of the script! +***************************************************************/ + +/** + * Class tx_install_FormProtection. + * + * This class provides protection against cross-site request forgery (XSRF/CSRF) + * in the install tool. + * + * + * How to use this in the install tool: + * + * For each form in the install tool (or link that changes some data), create a + * token and insert is as a hidden form element. The name of the form element + * does not matter; you only need it to get the form token for verifying it. + * + *
    + * $formToken = $this->formProtection->generateToken(
    + *    'installToolPassword', 'change'
    + * );
    + * // then puts the generated form token in a hidden field in the template
    + * 
    + * + * The three parameters $formName, $action and $formInstanceName can be + * arbitrary strings, but they should make the form token as specific as + * possible. For different forms (e.g. the password change and editing a the + * configuration), those values should be different. + * + * At the end of the form, you need to persist the tokens. This makes sure that + * generated tokens get saved, and also that removed tokens stay removed: + * + *
    + * $this->formProtection()->persistTokens();
    + * 
    + * + * + * When processing the data that has been submitted by the form, you can check + * that the form token is valid like this: + * + *
    + * if ($dataHasBeenSubmitted && $this->formProtection()->validateToken(
    + *     (string) $_POST['formToken'],
    + *     'installToolPassword',
    + *     'change'
    + * ) {
    + *     // processes the data
    + * } else {
    + *     // no need to do anything here as the install tool form protection will
    + *     // create an error message for an invalid token
    + * }
    + * 
    + * + * Note that validateToken invalidates the token with the token ID. So calling + * validate with the same parameters two times in a row will always return FALSE + * for the second call. + * + * It is important that the tokens get validated before the tokens are + * persisted. This makes sure that the tokens that get invalidated by + * validateToken cannot be used again. + * + * $Id$ + * + * @package TYPO3 + * @subpackage t3lib + * + * @author Oliver Klee + */ +class tx_install_FormProtection extends t3lib_formProtection_Abstract { + /** + * the maximum number of tokens that can exist at the same time + * + * @var integer + */ + protected $maximumNumberOfTokens = 100; + + /** + * an instance of the install tool used for displaying messages + * + * @var tx_install + */ + protected $installTool = NULL; + + /** + * Frees as much memory as possible. + */ + public function __destruct() { + $this->installTool = NULL; + parent::__destruct(); + } + + /** + * Injects the current instance of the install tool. + * + * This instance will be used for displaying messages. + * + * @param tx_install $installTool the current instance of the install tool + * + * @return void + */ + public function injectInstallTool(tx_install $installTool) { + $this->installTool = $installTool; + } + + /** + * Creates or displayes an error message telling the user that the submitted + * form token is invalid. + * + * @return void + */ + protected function createValidationErrorMessage() { + $this->installTool->addErrorMessage( + 'Validating the security token of this form has failed. ' . + 'Please reload the form and submit it again.' + ); + } + + /** + * Retrieves all saved tokens. + * + * @return array + * the saved tokens, will be empty if no tokens have been saved + */ + protected function retrieveTokens() { + if (isset($_SESSION['installToolFormTokens']) + && is_array($_SESSION['installToolFormTokens']) + ) { + $this->tokens = $_SESSION['installToolFormTokens']; + } else { + $this->tokens = array(); + } + } + + /** + * Saves the tokens so that they can be used by a later incarnation of this + * class. + * + * @return void + */ + public function persistTokens() { + $_SESSION['installToolFormTokens'] = $this->tokens; + } +} + +if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/install/mod/class.tx_install_formprotection.php']) { + include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/install/mod/class.tx_install_formprotection.php']); +} +?> \ No newline at end of file Property changes on: typo3/sysext/install/mod/class.tx_install_formprotection.php ___________________________________________________________________ Added: svn:mime-type + text/plain Index: typo3/sysext/install/mod/class.tx_install.php =================================================================== --- typo3/sysext/install/mod/class.tx_install.php (revision 8881) +++ typo3/sysext/install/mod/class.tx_install.php (working copy) @@ -159,6 +159,8 @@ /** * Install Tool module * + * $Id$ + * * @author Kasper Skårhøj * @author Ingmar Schlecht * @package TYPO3 @@ -218,6 +220,13 @@ */ protected $session = NULL; + /** + * the form protection instance used for creating and verifying form tokens + * + * @var tx_install_FormProtection + */ + protected $formProtection = NULL; + var $menuitems = array( 'config' => 'Basic Configuration', 'database' => 'Database Analyser', @@ -349,6 +358,11 @@ if($this->redirect_url) { t3lib_utility_Http::redirect($this->redirect_url); } + + $this->formProtection = t3lib_formProtection_Factory::get( + 'tx_install_FormProtection' + ); + $this->formProtection->injectInstallTool($this); } else { $this->loginForm(); } @@ -503,6 +517,7 @@ TRUE, TRUE ); + // Send content to the page wrapper function $this->output($this->outputWrapper($content)); } @@ -729,6 +744,7 @@ if (is_file($enableInstallToolFile) && trim(file_get_contents($enableInstallToolFile)) !== 'KEEP_FILE') { unlink(PATH_typo3conf . 'ENABLE_INSTALL_TOOL'); } + $this->formProtection->clean(); $this->session->destroySession(); t3lib_utility_Http::redirect($this->scriptSelf); break; @@ -882,6 +898,8 @@ break; } } + + $this->formProtection->persistTokens(); } /** @@ -2072,13 +2090,24 @@ $doit=1; if ($k=='BE' && $vk=='installToolPassword') { if ($value) { - if (isset($_POST['installToolPassword_check']) && (!t3lib_div::_GP('installToolPassword_check') || strcmp(t3lib_div::_GP('installToolPassword_check'),$value))) { - $doit=0; - $this->errorMessages[] = ' - The two passwords did not - match! The password was not - changed. - '; + if (isset($_POST['installToolPassword_check'])) { + if (!$this->formProtection->validateToken( + (string) $_POST['formToken'], + 'installToolPassword', + 'change' + )) { + $doit = FALSE; + break; + } + + if (!t3lib_div::_GP('installToolPassword_check') + || strcmp(t3lib_div::_GP('installToolPassword_check'), $value) + ) { + $doit = FALSE; + $this->errorMessages[] + = 'The two passwords did not ' . + 'match! The password was not changed.'; + } } if (t3lib_div::_GP('installToolPassword_md5')) $value =md5($value); } else $doit=0; @@ -7921,7 +7950,10 @@ 'action' => $this->scriptSelf.'?TYPO3_INSTALL[type]=extConfig', 'enterPassword' => 'Enter new password:', 'enterAgain' => 'Enter again:', - 'submit' => 'Set new password' + 'submit' => 'Set new password', + 'formToken' => $this->formProtection->generateToken( + 'installToolPassword', 'change' + ), ); // Fill the markers $content = t3lib_parsehtml::substituteMarkerArray( @@ -8276,6 +8308,20 @@ $bytes = t3lib_div::generateRandomBytes($keyLength); return substr(bin2hex($bytes), -96); } + + /** + * Adds an error message that should be displayed. + * + * @param string $messageText + * the text of the message to display, must not be empty + */ + public function addErrorMessage($messageText) { + if ($messageText == '') { + throw new InvalidArgumentException('$messageText must not be empty.'); + } + + $this->errorMessages[] = $messageText; + } } if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/install/mod/class.tx_install.php']) { Index: typo3/sysext/install/ext_autoload.php =================================================================== --- typo3/sysext/install/ext_autoload.php (revision 8881) +++ typo3/sysext/install/ext_autoload.php (working copy) @@ -1,10 +1,13 @@ t3lib_extMgm::extPath('install', 'report/class.tx_install_report_installstatus.php'), + 'tx_install' => $extensionPath . 'mod/class.tx_install.php', + 'tx_install_ajax' => $extensionPath . 'mod/class.tx_install_ajax.php', + 'tx_install_formprotection' => $extensionPath . 'mod/class.tx_install_formprotection.php', + 'tx_install_session' => $extensionPath . 'mod/class.tx_install_session.php', + 'tx_install_report_installstatus' => $extensionPath . 'report/class.tx_install_report_installstatus.php', ); ?> \ No newline at end of file Index: typo3/sysext/setup/mod/index.php =================================================================== --- typo3/sysext/setup/mod/index.php (revision 8881) +++ typo3/sysext/setup/mod/index.php (working copy) @@ -127,11 +127,10 @@ /** * If settings are submitted to _POST[DATA], store them - * NOTICE: This method is called before the template.php is included. See buttom of document - * - * @return void + * NOTICE: This method is called before the template.php is included. See + * bottom of document. */ - function storeIncomingData() { + public function storeIncomingData() { /* @var $BE_USER t3lib_beUserAuth */ global $BE_USER; @@ -142,8 +141,14 @@ $storeRec = array(); $fieldList = $this->getFieldsFromShowItem(); - if (is_array($d)) { - + $formProtection = t3lib_formProtection_Factory::get( + 't3lib_formProtection_Backend' + ); + if (is_array($d) && $formProtection->validateToken( + (string) t3lib_div::_POST('formToken'), + 'BE user setup', 'edit' + ) + ) { // UC hashed before applying changes $save_before = md5(serialize($BE_USER->uc)); @@ -414,19 +419,22 @@ $this->content .= $this->doc->spacer(20) . $this->doc->getDynTabMenu($menuItems, 'user-setup', false, false, 100, 1, false, 1, $this->dividers2tabs); + $formProtection = t3lib_formProtection_Factory::get( + 't3lib_formProtection_Backend' + ); + $formToken = $formProtection->generateToken('BE user setup', 'edit'); // Submit and reset buttons $this->content .= $this->doc->spacer(20); $this->content .= $this->doc->section('', t3lib_BEfunc::cshItem('_MOD_user_setup', 'reset', $BACK_PATH) . ' + ' ); - - // Notice $this->content .= $this->doc->spacer(30); $flashMessage = t3lib_div::makeInstance( @@ -935,4 +943,6 @@ $SOBE->main(); $SOBE->printContent(); +t3lib_formProtection_Factory::get('t3lib_formProtection_Backend') + ->persistTokens(); ?> \ No newline at end of file Index: typo3/sysext/lang/locallang_core.xml =================================================================== --- typo3/sysext/lang/locallang_core.xml (revision 8881) +++ typo3/sysext/lang/locallang_core.xml (working copy) @@ -265,6 +265,7 @@ + Index: tests/t3lib/t3lib_beuserauthTest.php =================================================================== --- tests/t3lib/t3lib_beuserauthTest.php (revision 0) +++ tests/t3lib/t3lib_beuserauthTest.php (revision 0) @@ -0,0 +1,69 @@ + + */ +class t3lib_beUserAuthTest extends tx_phpunit_testcase { + /** + * @var t3lib_beUserAuth + */ + private $fixture = NULL; + + public function setUp() { + $this->fixture = new t3lib_beUserAuth(); + } + + public function tearDown() { + unset($this->fixture); + + t3lib_formProtection_Factory::purgeInstances(); + } + + + ///////////////////////////////////////// + // Tests concerning the form protection + ///////////////////////////////////////// + + /** + * @test + */ + public function logoffCleansFormProtection() { + $formProtection = $this->getMock( + 't3lib_formProtection_Backend', array('clean') + ); + $formProtection->expects($this->atLeastOnce())->method('clean'); + t3lib_formProtection_Factory::set( + 't3lib_formProtection_Backend', $formProtection + ); + + $this->fixture->logoff(); + } +} +?> Index: tests/t3lib/formprotection/t3lib_formProtection_FactoryTest.php =================================================================== --- tests/t3lib/formprotection/t3lib_formProtection_FactoryTest.php (revision 0) +++ tests/t3lib/formprotection/t3lib_formProtection_FactoryTest.php (revision 0) @@ -0,0 +1,172 @@ + + * @author Ernesto Baschny + */ +class t3lib_formProtection_FactoryTest extends tx_phpunit_testcase { + public function setUp() { + } + + public function tearDown() { + t3lib_formProtection_Factory::purgeInstances(); + } + + + ///////////////////////// + // Tests concerning get + ///////////////////////// + + /** + * @test + * + * @expectedException InvalidArgumentException + */ + public function getForInexistentClassThrowsException() { + t3lib_formProtection_Factory::get('noSuchClass'); + } + + /** + * @test + * + * @expectedException InvalidArgumentException + */ + public function getForClassThatIsNoFormProtectionSubclassThrowsException() { + t3lib_formProtection_Factory::get('t3lib_formProtection_FactoryTest'); + } + + /** + * @test + */ + public function getForTypeBackEndWithExistingBackEndReturnsBackEndFormProtection() { + $this->assertTrue( + t3lib_formProtection_Factory::get( + 't3lib_formProtection_Backend' + ) instanceof t3lib_formProtection_Backend + ); + } + + /** + * @test + */ + public function getForTypeBackEndCalledTwoTimesReturnsTheSameInstance() { + $this->assertSame( + t3lib_formProtection_Factory::get( + 't3lib_formProtection_Backend' + ), + t3lib_formProtection_Factory::get( + 't3lib_formProtection_Backend' + ) + ); + } + + /** + * @test + */ + public function getForTypeInstallToolReturnsInstallToolFormProtection() { + $this->assertTrue( + t3lib_formProtection_Factory::get( + 'tx_install_FormProtection' + ) instanceof tx_install_FormProtection + ); + } + + /** + * @test + */ + public function getForTypeInstallToolCalledTwoTimesReturnsTheSameInstance() { + $this->assertSame( + t3lib_formProtection_Factory::get( + 'tx_install_FormProtection' + ), + t3lib_formProtection_Factory::get( + 'tx_install_FormProtection' + ) + ); + } + + /** + * @test + */ + public function getForTypesInstallToolAndBackEndReturnsDifferentInstances() { + $this->assertNotSame( + t3lib_formProtection_Factory::get( + 'tx_install_FormProtection' + ), + t3lib_formProtection_Factory::get( + 't3lib_formProtection_Backend' + ) + ); + } + + + ///////////////////////// + // Tests concerning set + ///////////////////////// + + /** + * @test + */ + public function setSetsInstanceForType() { + $instance = new t3lib_formProtection_Testing(); + t3lib_formProtection_Factory::set( + 't3lib_formProtection_Backend', $instance + ); + + $this->assertSame( + $instance, + t3lib_formProtection_Factory::get( + 't3lib_formProtection_Backend' + ) + ); + } + + /** + * @test + */ + public function setNotSetsInstanceForOtherType() { + $instance = new t3lib_formProtection_Testing(); + t3lib_formProtection_Factory::set( + 't3lib_formProtection_Backend', $instance + ); + + $this->assertNotSame( + $instance, + t3lib_formProtection_Factory::get( + 'tx_install_FormProtection' + ) + ); + } +} +?> \ No newline at end of file Property changes on: tests/t3lib/formprotection/t3lib_formProtection_FactoryTest.php ___________________________________________________________________ Added: svn:mime-type + text/plain Index: tests/t3lib/formprotection/t3lib_formProtection_AbstractTest.php =================================================================== --- tests/t3lib/formprotection/t3lib_formProtection_AbstractTest.php (revision 0) +++ tests/t3lib/formprotection/t3lib_formProtection_AbstractTest.php (revision 0) @@ -0,0 +1,475 @@ + + */ +class t3lib_formProtection_AbstractTest extends tx_phpunit_testcase { + /** + * @var t3lib_formProtection_Testing + */ + private $fixture; + + public function setUp() { + $this->fixture = new t3lib_formProtection_Testing(); + } + + public function tearDown() { + $this->fixture->__destruct(); + unset($this->fixture); + } + + + ///////////////////////////////////////// + // Tests concerning the basic functions + ///////////////////////////////////////// + + /** + * @test + */ + public function constructionRetrievesTokens() { + $className = uniqid('t3lib_formProtection'); + eval( + 'class ' . $className . ' extends t3lib_formProtection_Testing {' . + 'public $tokensHaveBeenRetrieved = FALSE; ' . + 'protected function retrieveTokens() {' . + '$this->tokensHaveBeenRetrieved = TRUE;' . + '}' . + '}' + ); + + $fixture = new $className(); + + $this->assertTrue( + $fixture->tokensHaveBeenRetrieved + ); + } + + /** + * @test + */ + public function cleanMakesTokenInvalid() { + $formName = 'foo'; + $tokenId = $this->fixture->generateToken($formName); + + $this->fixture->clean(); + + $this->assertFalse( + $this->fixture->validateToken($tokenId, $formName) + ); + } + + /** + * @test + */ + public function cleanPersistsTokens() { + $fixture = $this->getMock( + 't3lib_formProtection_Testing', array('persistTokens') + ); + $fixture->expects($this->once())->method('persistTokens'); + + $fixture->clean(); + } + + + /////////////////////////////////// + // Tests concerning generateToken + /////////////////////////////////// + + /** + * @test + */ + public function generateTokenFormForEmptyFormNameThrowsException() { + $this->setExpectedException( + 'InvalidArgumentException', '$formName must not be empty.' + ); + + $this->fixture->generateToken('', 'edit', 'bar'); + } + + /** + * @test + */ + public function generateTokenFormForEmptyActionNotThrowsException() { + $this->fixture->generateToken('foo', '', '42'); + } + + /** + * @test + */ + public function generateTokenFormForEmptyFormInstanceNameNotThrowsException() { + $this->fixture->generateToken('foo', 'edit', ''); + } + + /** + * @test + */ + public function generateTokenFormForOmittedActionAndFormInstanceNameNotThrowsException() { + $this->fixture->generateToken('foo'); + } + + /** + * @test + */ + public function generateTokenReturns32CharacterHexToken() { + $this->assertRegexp( + '/^[0-9a-f]{32}$/', + $this->fixture->generateToken('foo') + ); + } + + /** + * @test + */ + public function generateTokenCalledTwoTimesWithSameParametersReturnsDifferentTokens() { + $this->assertNotEquals( + $this->fixture->generateToken('foo', 'edit', 'bar'), + $this->fixture->generateToken('foo', 'edit', 'bar') + ); + } + + /** + * @test + */ + public function generatingTooManyTokensInvalidatesOldestToken() { + $this->fixture->setMaximumNumberOfTokens(2); + + $formName = 'foo'; + + $token1 = $this->fixture->generateToken($formName); + $token2 = $this->fixture->generateToken($formName); + $token3 = $this->fixture->generateToken($formName); + + $this->assertFalse( + $this->fixture->validateToken($token1, $formName) + ); + } + + /** + * @test + */ + public function generatingTooManyTokensNotInvalidatesNewestToken() { + $this->fixture->setMaximumNumberOfTokens(2); + + $formName = 'foo'; + $formInstanceName = 'bar'; + + $token1 = $this->fixture->generateToken($formName); + $token2 = $this->fixture->generateToken($formName); + $token3 = $this->fixture->generateToken($formName); + + $this->assertTrue( + $this->fixture->validateToken($token3, $formName) + ); + } + + /** + * @test + */ + public function generatingTooManyTokensNotInvalidatesTokenInTheMiddle() { + $this->fixture->setMaximumNumberOfTokens(2); + + $formName = 'foo'; + $formInstanceName = 'bar'; + + $token1 = $this->fixture->generateToken($formName); + $token2 = $this->fixture->generateToken($formName); + $token3 = $this->fixture->generateToken($formName); + + $this->assertTrue( + $this->fixture->validateToken($token2, $formName) + ); + } + + + /////////////////////////////////// + // Tests concerning validateToken + /////////////////////////////////// + + /** + * @test + */ + public function validateTokenWithFourEmptyParametersNotThrowsException() { + $this->fixture->validateToken('', '', '', ''); + } + + /** + * @test + */ + public function validateTokenWithTwoEmptyAndTwoMissingParametersNotThrowsException() { + $this->fixture->validateToken('', ''); + } + + /** + * @test + */ + public function validateTokenWithDataFromGenerateTokenWithFormInstanceNameReturnsTrue() { + $formName = 'foo'; + $action = 'edit'; + $formInstanceName = 'bar'; + + $this->assertTrue( + $this->fixture->validateToken( + $this->fixture->generateToken($formName, $action, $formInstanceName), + $formName, + $action, + $formInstanceName + ) + ); + } + + /** + * @test + */ + public function validateTokenWithDataFromGenerateTokenWithMissingActionAndFormInstanceNameReturnsTrue() { + $formName = 'foo'; + + $this->assertTrue( + $this->fixture->validateToken( + $this->fixture->generateToken($formName), $formName + ) + ); + } + + /** + * @test + */ + public function validateTokenWithValidDataDropsToken() { + $formName = 'foo'; + + $fixture = $this->getMock( + 't3lib_formProtection_Testing', array('dropToken') + ); + + $tokenId = $fixture->generateToken($formName); + $fixture->expects($this->once())->method('dropToken') + ->with($tokenId); + + $fixture->validateToken($tokenId, $formName); + } + + /** + * @test + */ + public function validateTokenWithValidDataCalledTwoTimesReturnsFalseOnSecondCall() { + $formName = 'foo'; + $action = 'edit'; + $formInstanceName = 'bar'; + + $tokenId = $this->fixture->generateToken($formName, $action, $formInstanceName); + + $this->fixture->validateToken($tokenId, $formName, $action, $formInstanceName); + + $this->assertFalse( + $this->fixture->validateToken($tokenId, $formName, $action, $formInstanceName) + ); + } + + /** + * @test + */ + public function validateTokenWithMismatchingTokenIdReturnsFalse() { + $formName = 'foo'; + $action = 'edit'; + $formInstanceName = 'bar'; + + $this->fixture->generateToken($formName, $action, $formInstanceName); + + $this->assertFalse( + $this->fixture->validateToken( + 'Hello world!', $formName, $action, $formInstanceName + ) + ); + } + + /** + * @test + */ + public function validateTokenWithMismatchingFormNameReturnsFalse() { + $formName = 'foo'; + $action = 'edit'; + $formInstanceName = 'bar'; + + $tokenId = $this->fixture->generateToken($formName, $action, $formInstanceName); + + $this->assertFalse( + $this->fixture->validateToken( + $tokenId, 'espresso', $action, $formInstanceName + ) + ); + } + + /** + * @test + */ + public function validateTokenWithMismatchingActionReturnsFalse() { + $formName = 'foo'; + $action = 'edit'; + $formInstanceName = 'bar'; + + $tokenId = $this->fixture->generateToken($formName, $action, $formInstanceName); + + $this->assertFalse( + $this->fixture->validateToken( + $tokenId, $formName, 'delete', $formInstanceName + ) + ); + } + + /** + * @test + */ + public function validateTokenWithMismatchingFormInstanceNameReturnsFalse() { + $formName = 'foo'; + $action = 'edit'; + $formInstanceName = 'bar'; + + $tokenId = $this->fixture->generateToken($formName, $action, $formInstanceName); + + $this->assertFalse( + $this->fixture->validateToken( + $tokenId, $formName, $action, 'beer' + ) + ); + } + + /** + * @test + */ + public function validateTokenWithTwoTokensForSameFormNameAndActionAndFormInstanceNameReturnsTrueForBoth() { + $formName = 'foo'; + $action = 'edit'; + $formInstanceName = 'bar'; + + $tokenId1 = $this->fixture->generateToken($formName, $action, $formInstanceName); + $tokenId2 = $this->fixture->generateToken($formName, $action, $formInstanceName); + + $this->assertTrue( + $this->fixture->validateToken( + $tokenId1, $formName, $action, $formInstanceName + ) + ); + $this->assertTrue( + $this->fixture->validateToken( + $tokenId2, $formName, $action, $formInstanceName + ) + ); + } + + /** + * @test + */ + public function validateTokenWithTwoTokensForSameFormNameAndActionAndFormInstanceNameCalledInReverseOrderReturnsTrueForBoth() { + $formName = 'foo'; + $action = 'edit'; + $formInstanceName = 'bar'; + + $tokenId1 = $this->fixture->generateToken($formName, $action, $formInstanceName); + $tokenId2 = $this->fixture->generateToken($formName, $action, $formInstanceName); + + $this->assertTrue( + $this->fixture->validateToken( + $tokenId2, $formName, $action, $formInstanceName + ) + ); + $this->assertTrue( + $this->fixture->validateToken( + $tokenId1, $formName, $action, $formInstanceName + ) + ); + } + + /** + * @test + */ + public function validateTokenForValidTokenNotCallsCreateValidationErrorMessage() { + $fixture = $this->getMock( + 't3lib_formProtection_Testing', array('createValidationErrorMessage') + ); + $fixture->expects($this->never())->method('createValidationErrorMessage'); + + $formName = 'foo'; + $action = 'edit'; + $formInstanceName = 'bar'; + + $token = $fixture->generateToken($formName, $action, $formInstanceName); + $fixture->validateToken( + $token, $formName, $action, $formInstanceName + ); + + $fixture->__destruct(); + } + + /** + * @test + */ + public function validateTokenForInvalidTokenCallsCreateValidationErrorMessage() { + $fixture = $this->getMock( + 't3lib_formProtection_Testing', array('createValidationErrorMessage') + ); + $fixture->expects($this->once())->method('createValidationErrorMessage'); + + $formName = 'foo'; + $action = 'edit'; + $formInstanceName = 'bar'; + + $fixture->generateToken($formName, $action, $formInstanceName); + $fixture->validateToken( + 'an invalid token ...', $formName, $action, $formInstanceName + ); + + $fixture->__destruct(); + } + + /** + * @test + */ + public function validateTokenForInvalidFormNameCallsCreateValidationErrorMessage() { + $fixture = $this->getMock( + 't3lib_formProtection_Testing', array('createValidationErrorMessage') + ); + $fixture->expects($this->once())->method('createValidationErrorMessage'); + + $formName = 'foo'; + $action = 'edit'; + $formInstanceName = 'bar'; + + $token = $fixture->generateToken($formName, $action, $formInstanceName); + $fixture->validateToken( + $token, 'another form name', $action, $formInstanceName + ); + + $fixture->__destruct(); + } +} +?> \ No newline at end of file Property changes on: tests/t3lib/formprotection/t3lib_formProtection_AbstractTest.php ___________________________________________________________________ Added: svn:mime-type + text/plain Index: tests/t3lib/formprotection/fixtures/class.t3lib_formprotection_testing.php =================================================================== --- tests/t3lib/formprotection/fixtures/class.t3lib_formprotection_testing.php (revision 0) +++ tests/t3lib/formprotection/fixtures/class.t3lib_formprotection_testing.php (revision 0) @@ -0,0 +1,99 @@ + +* All rights reserved +* +* This script is part of the TYPO3 project. The TYPO3 project is +* free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* The GNU General Public License can be found at +* http://www.gnu.org/copyleft/gpl.html. +* +* This script is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* This copyright notice MUST APPEAR in all copies of the script! +***************************************************************/ + +/** + * Class t3lib_formProtection_Testing. + * + * This is a testing subclass of the abstract t3lib_formProtection_Abstract + * class. + * + * $Id$ + * + * @package TYPO3 + * @subpackage t3lib + * + * @author Oliver Klee + */ +class t3lib_formProtection_Testing extends t3lib_formProtection_Abstract { + /** + * the maximum number of tokens that can exist at the same time + * + * @var integer + */ + protected $maximumNumberOfTokens = 100; + + /** + * Sets the maximum number of tokens that can exist at the same time. + * + * @param integer $number maximum number of tokens, must be > 0 + * + * @return void + */ + public function setMaximumNumberOfTokens($number) { + $this->maximumNumberOfTokens = $number; + } + + /** + * Creates or displayes an error message telling the user that the submitted + * form token is invalid. + * + * @return void + */ + protected function createValidationErrorMessage() {} + + /** + * Retrieves all saved tokens. + * + * @return array the saved tokens as a two-dimensional array, will be empty + * if no tokens have been saved + */ + protected function retrieveTokens() {} + + /** + * Saves the tokens so that they can be used by a later incarnation of this + * class. + * + * @return void + */ + public function persistTokens() {} + + /** + * Drops the token with the ID $tokenId and persists all tokens. + * + * If there is no token with that ID, this function is a no-op. + * + * @param string $tokenId + * the 32-character ID of an existing token, must not be empty + * + * @return void + */ + public function dropToken($tokenId) { + parent::dropToken($tokenId); + } +} + +if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/formprotection/class.t3lib_formprotection_testing.php']) { + include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/formprotection/class.t3lib_formprotection_testing.php']); +} +?> \ No newline at end of file Property changes on: tests/t3lib/formprotection/fixtures/class.t3lib_formprotection_testing.php ___________________________________________________________________ Added: svn:mime-type + text/plain Index: tests/t3lib/formprotection/t3lib_formProtection_BackendTest.php =================================================================== --- tests/t3lib/formprotection/t3lib_formProtection_BackendTest.php (revision 0) +++ tests/t3lib/formprotection/t3lib_formProtection_BackendTest.php (revision 0) @@ -0,0 +1,198 @@ + + */ +class t3lib_formProtection_BackendTest extends tx_phpunit_testcase { + /** + * a backup of the current BE user + * + * @var t3lib_beUserAuth + */ + private $backEndUserBackup = NULL; + + /** + * @var t3lib_formProtection_Backend + */ + private $fixture; + + public function setUp() { + $this->backEndUserBackup = $GLOBALS['BE_USER']; + $GLOBALS['BE_USER'] = $this->getMock( + 't3lib_beUserAuth', + array('getSessionData', 'setAndSaveSessionData') + ); + + $className = $this->createAccessibleProxyClass(); + $this->fixture = new $className; + } + + public function tearDown() { + $this->fixture->__destruct(); + unset($this->fixture); + + $GLOBALS['BE_USER'] = $this->backEndUserBackup; + + t3lib_FlashMessageQueue::getAllMessagesAndFlush(); + } + + + ////////////////////// + // Utility functions + ////////////////////// + + /** + * Creates a subclass t3lib_formProtection_Backend with retrieveTokens made + * public. + * + * @return string the name of the created class, will not be empty + */ + private function createAccessibleProxyClass() { + $className = 't3lib_formProtection_BackendAccessibleProxy'; + if (!class_exists($className)) { + eval( + 'class ' . $className . ' extends t3lib_formProtection_Backend {' . + ' public function createValidationErrorMessage() {' . + ' parent::createValidationErrorMessage();' . + ' }' . + ' public function retrieveTokens() {' . + ' return parent::retrieveTokens();' . + ' }' . + '}' + ); + } + + return $className; + } + + + //////////////////////////////////// + // Tests for the utility functions + //////////////////////////////////// + + /** + * @test + */ + public function createAccessibleProxyCreatesBackendFormProtectionSubclass() { + $className = $this->createAccessibleProxyClass(); + + $this->assertTrue( + (new $className()) instanceof t3lib_formProtection_Backend + ); + } + + + ////////////////////////////////////////////////////////// + // Tests concerning the reading and saving of the tokens + ////////////////////////////////////////////////////////// + + /** + * @test + */ + public function retrieveTokensReadsTokensFromSessionData() { + $GLOBALS['BE_USER']->expects($this->once())->method('getSessionData') + ->with('formTokens')->will($this->returnValue(array())); + + $this->fixture->retrieveTokens(); + } + + /** + * @test + */ + public function tokensFromSessionDataAreAvailableForValidateToken() { + $tokenId = '51a655b55c54d54e5454c5f521f6552a'; + $formName = 'foo'; + $action = 'edit'; + $formInstanceName = '42'; + + $GLOBALS['BE_USER']->expects($this->once())->method('getSessionData') + ->with('formTokens')->will($this->returnValue(array( + $tokenId => array( + 'formName' => $formName, + 'action' => $action, + 'formInstanceName' => $formInstanceName, + ), + ))); + + $this->fixture->retrieveTokens(); + + $this->assertTrue( + $this->fixture->validateToken($tokenId, $formName, $action, $formInstanceName) + ); + } + + /** + * @test + */ + public function persistTokensWritesTokensToSession() { + $formName = 'foo'; + $action = 'edit'; + $formInstanceName = '42'; + + $tokenId = $this->fixture->generateToken( + $formName, $action, $formInstanceName + ); + $allTokens = array( + $tokenId => array( + 'formName' => $formName, + 'action' => $action, + 'formInstanceName' => $formInstanceName, + ), + ); + + $GLOBALS['BE_USER']->expects($this->once()) + ->method('setAndSaveSessionData')->with('formTokens', $allTokens); + + $this->fixture->persistTokens(); + } + + + ////////////////////////////////////////////////// + // Tests concerning createValidationErrorMessage + ////////////////////////////////////////////////// + + /** + * @test + */ + public function createValidationErrorMessageAddsErrorFlashMessage() { + $this->fixture->createValidationErrorMessage(); + + $messages = t3lib_FlashMessageQueue::getAllMessagesAndFlush(); + $this->assertContains( + $GLOBALS['LANG']->sL( + 'LLL:EXT:lang/locallang_core.xml:error.formProtection.tokenInvalid' + ), + $messages[0]->render() + ); + } +} +?> \ No newline at end of file Property changes on: tests/t3lib/formprotection/t3lib_formProtection_BackendTest.php ___________________________________________________________________ Added: svn:mime-type + text/plain Index: tests/typo3/sysext/install/tx_install_FormProtectionTest.php =================================================================== --- tests/typo3/sysext/install/tx_install_FormProtectionTest.php (revision 0) +++ tests/typo3/sysext/install/tx_install_FormProtectionTest.php (revision 0) @@ -0,0 +1,190 @@ + + */ +class tx_install_FormProtectionTest extends tx_phpunit_testcase { + /** + * @var tx_install_FormProtection + */ + private $fixture; + + /** + * backup of $_SESSION + * + * @var array + */ + private $sessionBackup; + + public function setUp() { + $this->sessionBackup = $_SESSION; + + $className = $this->createAccessibleProxyClass(); + $this->fixture = new $className(); + } + + public function tearDown() { + $this->fixture->__destruct(); + unset($this->fixture); + + t3lib_FlashMessageQueue::getAllMessagesAndFlush(); + + $_SESSION = $this->sessionBackup; + } + + + ////////////////////// + // Utility functions + ////////////////////// + + /** + * Creates a subclass tx_install_FormProtection with retrieveTokens made + * public. + * + * @return string the name of the created class, will not be empty + */ + private function createAccessibleProxyClass() { + $className = 'tx_install_FormProtectionAccessibleProxy'; + if (!class_exists($className)) { + eval( + 'class ' . $className . ' extends tx_install_FormProtection {' . + ' public function createValidationErrorMessage() {' . + ' parent::createValidationErrorMessage();' . + ' }' . + ' public function retrieveTokens() {' . + ' return parent::retrieveTokens();' . + ' }' . + '}' + ); + } + + return $className; + } + + + //////////////////////////////////// + // Tests for the utility functions + //////////////////////////////////// + + /** + * @test + */ + public function createAccessibleProxyCreatesInstallToolFormProtectionSubclass() { + $className = $this->createAccessibleProxyClass(); + + $this->assertTrue( + (new $className()) instanceof tx_install_FormProtection + ); + } + + + ////////////////////////////////////////////////////////// + // Tests concerning the reading and saving of the tokens + ////////////////////////////////////////////////////////// + + /** + * @test + */ + public function tokensFromSessionDataAreAvailableForValidateToken() { + $tokenId = '51a655b55c54d54e5454c5f521f6552a'; + $formName = 'foo'; + $action = 'edit'; + $formInstanceName = '42'; + + $_SESSION['installToolFormTokens'] = array( + $tokenId => array( + 'formName' => $formName, + 'action' => $action, + 'formInstanceName' => $formInstanceName, + ), + ); + + $this->fixture->retrieveTokens(); + + $this->assertTrue( + $this->fixture->validateToken( + $tokenId, $formName, $action, $formInstanceName + ) + ); + } + + /** + * @test + */ + public function persistTokensWritesTokensToSession() { + $formName = 'foo'; + $action = 'edit'; + $formInstanceName = '42'; + + $tokenId = $this->fixture->generateToken( + $formName, $action, $formInstanceName + ); + + $this->fixture->persistTokens(); + + $this->assertEquals( + array( + $tokenId => array( + 'formName' => $formName, + 'action' => $action, + 'formInstanceName' => $formInstanceName, + ), + ), + $_SESSION['installToolFormTokens'] + ); + } + + + ////////////////////////////////////////////////// + // Tests concerning createValidationErrorMessage + ////////////////////////////////////////////////// + + /** + * @test + */ + public function createValidationErrorMessageAddsErrorMessage() { + $installTool = $this->getMock( + 'tx_install', array('addErrorMessage'), array(), '', FALSE + ); + $installTool->expects($this->once())->method('addErrorMessage') + ->with( + 'Validating the security token of this form has failed. ' . + 'Please reload the form and submit it again.' + ); + $this->fixture->injectInstallTool($installTool); + + $this->fixture->createValidationErrorMessage(); + } +} +?> \ No newline at end of file Property changes on: tests/typo3/sysext/install/tx_install_FormProtectionTest.php ___________________________________________________________________ Added: svn:mime-type + text/plain