Index: t3lib/class.t3lib_beuserauth.php =================================================================== --- t3lib/class.t3lib_beuserauth.php (revision 7641) +++ t3lib/class.t3lib_beuserauth.php (working copy) @@ -70,6 +70,8 @@ * This class contains the configuration of the database fields used plus some functions for the authentication process of backend users. * * @author Kasper Skaarhoj + * @author Oliver Klee + * * @package TYPO3 * @subpackage t3lib */ @@ -141,6 +143,22 @@ 'resizeTextareas_Flexible' => 1, ); + /** + * an instance of the form protection for BE forms + * + * @var unknown_type + */ + protected $formProtection = null; + + /** + * The destructor. Frees as much memory used by this object as possible. + */ + public function __destruct() { + if ($this->formProtection !== null) { + $this->formProtection->__destruct(); + $this->formProtection = null; + } + } /** * Sets the security level for the Backend login @@ -371,7 +389,7 @@ * + backend user is a regular user and adminOnly is not defined * + backend user is an admin user * + backend user is used in CLI context and adminOnly is explicitely set to "2" - * + * * @return boolean Whether a backend user is allowed to access the backend */ protected function isUserAllowedToLogin() { @@ -388,13 +406,32 @@ return $isUserAllowedToLogin; } -} + /** + * Returns the BE form protection instance. + * + * @return t3lib_formProtection_backend the BE form protection instance. + */ + public function getFormProtection() { + if ($this->formProtection == null) { + $this->formProtection = t3lib_div::makeInstance( + 't3lib_formProtection_backend' + ); + } + return $this->formProtection; + } + /** + * Logs out the current user and clears the form protection tokens. + */ + public function logoff() { + $this->getFormProtection()->clean(); + parent::logoff(); + } +} if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_beuserauth.php']) { include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_beuserauth.php']); } - ?> \ No newline at end of file Index: t3lib/core_autoload.php =================================================================== --- t3lib/core_autoload.php (revision 7641) +++ t3lib/core_autoload.php (working copy) @@ -111,6 +111,8 @@ 't3lib_error_errorhandlerinterface' => PATH_t3lib . 'error/interface.t3lib_error_errorhandlerinterface.php', 't3lib_error_exceptionhandlerinterface' => PATH_t3lib . 'error/interface.t3lib_error_exceptionhandlerinterface.php', 't3lib_browselinkshook' => PATH_t3lib . 'interfaces/interface.t3lib_browselinkshook.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,71 @@ + +* 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) + * for forms in the BE. + * + * @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; + + /** + * 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() { + $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. + */ + 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_abstract.php =================================================================== --- t3lib/formprotection/class.t3lib_formprotection_abstract.php (revision 0) +++ t3lib/formprotection/class.t3lib_formprotection_abstract.php (revision 0) @@ -0,0 +1,210 @@ + +* 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) + * for forms. + * + * @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. + */ + 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 $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, $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, + '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. + * + * The caller is expected to silently ignore the form data if a token is + * invalid in order to provide as little feedback as possible to brute-force + * attacks. + * + * @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 $formInstanceName + * the instance name of the form to check, for example "42", + * may also be empty or utterly misformed + * + * @return boolean TRUE $tokenId, $formName, $formInstanceName match and + * the token has not been used yet, FALSE otherwise + */ + public function validateToken($tokenId, $formName, $formInstanceName = '') { + if (isset($this->tokens[$tokenId])) { + $token = $this->tokens[$tokenId]; + $isValid = ($token['formName'] == $formName) + && ($token['formInstanceName'] == $formInstanceName); + $this->dropToken($tokenId); + } else { + $isValid = FALSE; + } + + return $isValid; + } + + /** + * Retrieves all saved tokens. + * + * @return array the saved tokens as a two-dimensional array, 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. + */ + 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 + */ + 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. + */ + 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/setup/mod/index.php =================================================================== --- typo3/sysext/setup/mod/index.php (revision 7641) +++ 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,11 @@ $storeRec = array(); $fieldList = $this->getFieldsFromShowItem(); - if (is_array($d)) { - + if (is_array($d) && $BE_USER->getFormProtection()->validateToken( + (string) t3lib_div::_POST('formToken'), + 'BE user setup', $BE__USER->user['uid'] + ) + ) { // UC hashed before applying changes $save_before = md5(serialize($BE_USER->uc)); @@ -417,19 +419,21 @@ $this->content .= $this->doc->spacer(20) . $this->doc->getDynTabMenu($menuItems, 'user-setup', false, false, 100, 1, false, 1, $this->dividers2tabs); + $formToken = $BE_USER->getFormProtection()->generateToken( + 'BE user setup', $BE__USER->user['uid'] + ); // 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( @@ -898,10 +902,10 @@ } return t3lib_BEfunc::cshItem('_MOD_user_setup', $str, $this->doc->backPath, '|', false, 'margin-bottom:0px;'); } - + /** * Returns array with fields defined in $GLOBALS['TYPO3_USER_SETTINGS']['showitem'] - * + * * @param void * @return array array with fieldnames visible in form */ @@ -941,5 +945,6 @@ $SOBE->init(); $SOBE->main(); $SOBE->printContent(); +$GLOBALS['BE_USER']->getFormProtection()->persistTokens(); ?> \ No newline at end of file Index: tests/t3lib/t3lib_beuserauthTest.php =================================================================== --- tests/t3lib/t3lib_beuserauthTest.php (revision 0) +++ tests/t3lib/t3lib_beuserauthTest.php (revision 0) @@ -0,0 +1,100 @@ + + */ +class t3lib_beUserAuthTest extends tx_phpunit_testcase { + /** + * @var t3lib_beUserAuth + */ + private $fixture; + + public function setUp() { + $this->fixture = new t3lib_beUserAuth(); + } + + public function tearDown() { + $this->fixture->__destruct(); + + unset($this->fixture); + } + + + ///////////////////////////////////////// + // Tests concerning the form protection + ///////////////////////////////////////// + + /** + * @test + */ + public function getFormProtectectionReturnsBackEndFormProtectionInstance() { + $this->assertTrue( + $this->fixture->getFormProtection() + instanceof t3lib_formProtection_backend + ); + } + + /** + * @test + */ + public function getFormProtectectionCalledTwoTimesReturnsSameInstance() { + $this->assertSame( + $this->fixture->getFormProtection(), + $this->fixture->getFormProtection() + ); + } + + /** + * @test + */ + public function logoffCleansFormProtection() { + $className = uniqid('t3lib_beUserAuth'); + eval( + 'class ' . $className . ' extends t3lib_beUserAuth {' . + 'public function setFormProtection(t3lib_formProtection_backend $formProtection) {' . + '$this->formProtection = $formProtection;' . + '}' . + '}' + ); + + $formProtection = $this->getMock( + 't3lib_formProtection_backend', array('clean') + ); + $formProtection->expects($this->atLeastOnce())->method('clean'); + + $fixture = new $className(); + $fixture->setFormProtection($formProtection); + + $fixture->logoff(); + + $fixture->__destruct(); + } +} +?> \ No newline at end of file Property changes on: tests/t3lib/t3lib_beuserauthTest.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,83 @@ + +* 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 class provides protection against cross-site request forgery (XSRF) + * for forms in the BE. + * + * @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 + */ + public function setMaximumNumberOfTokens($number) { + $this->maximumNumberOfTokens = $number; + } + + /** + * 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. + */ + 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 + */ + 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_formprotectionTest.php =================================================================== --- tests/t3lib/formprotection/t3lib_formprotectionTest.php (revision 0) +++ tests/t3lib/formprotection/t3lib_formprotectionTest.php (revision 0) @@ -0,0 +1,357 @@ + + */ +class t3lib_formProtectionTest 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('', 'bar'); + } + + /** + * @test + */ + public function generateTokenFormForEmptyFormInstanceNameNotThrowsException() { + $this->fixture->generateToken('foo', ''); + } + + /** + * @test + */ + public function generateTokenFormForOmittedFormInstanceNameNotThrowsException() { + $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', 'bar'), + $this->fixture->generateToken('foo', 'bar') + ); + } + + /** + * @test + */ + public function generatingTooManyTokensInvalidatesOldestToken() { + $this->fixture->setMaximumNumberOfTokens(2); + + $formName = 'foo'; + $formInstanceName = 'bar'; + + $token1 = $this->fixture->generateToken($formName, $formInstanceName); + $token2 = $this->fixture->generateToken($formName, $formInstanceName); + $token3 = $this->fixture->generateToken($formName, $formInstanceName); + + $this->assertFalse( + $this->fixture->validateToken($token1, $formName, $formInstanceName) + ); + } + + /** + * @test + */ + public function generatingTooManyTokensNotInvalidatesNewestToken() { + $this->fixture->setMaximumNumberOfTokens(2); + + $formName = 'foo'; + $formInstanceName = 'bar'; + + $token1 = $this->fixture->generateToken($formName, $formInstanceName); + $token2 = $this->fixture->generateToken($formName, $formInstanceName); + $token3 = $this->fixture->generateToken($formName, $formInstanceName); + + $this->assertTrue( + $this->fixture->validateToken($token3, $formName, $formInstanceName) + ); + } + + /** + * @test + */ + public function generatingTooManyTokensNotInvalidatesTokenInTheMiddle() { + $this->fixture->setMaximumNumberOfTokens(2); + + $formName = 'foo'; + $formInstanceName = 'bar'; + + $token1 = $this->fixture->generateToken($formName, $formInstanceName); + $token2 = $this->fixture->generateToken($formName, $formInstanceName); + $token3 = $this->fixture->generateToken($formName, $formInstanceName); + + $this->assertTrue( + $this->fixture->validateToken($token2, $formName, $formInstanceName) + ); + } + + + /////////////////////////////////// + // Tests concerning validateToken + /////////////////////////////////// + + /** + * @test + */ + public function validateTokenWithThreeEmptyParametersNotThrowsException() { + $this->fixture->validateToken('', '', ''); + } + + /** + * @test + */ + public function validateTokenWithTwoEmptyAndOneMissingParametersNotThrowsException() { + $this->fixture->validateToken('', ''); + } + + /** + * @test + */ + public function validateTokenWithDataFromGenerateTokenWithFormInstanceNameReturnsTrue() { + $formName = 'foo'; + $formInstanceName = 'bar'; + + $this->assertTrue( + $this->fixture->validateToken( + $this->fixture->generateToken($formName, $formInstanceName), + $formName, + $formInstanceName + ) + ); + } + + /** + * @test + */ + public function validateTokenWithDataFromGenerateTokenWithMissingFormInstanceNameReturnsTrue() { + $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'; + $formInstanceName = 'bar'; + + $tokenId = $this->fixture->generateToken($formName, $formInstanceName); + + $this->fixture->validateToken($tokenId, $formName, $formInstanceName); + + $this->assertFalse( + $this->fixture->validateToken($tokenId, $formName, $formInstanceName) + ); + } + + /** + * @test + */ + public function validateTokenWithMismatchingTokenIdReturnsFalse() { + $formName = 'foo'; + $formInstanceName = 'bar'; + + $this->fixture->generateToken($formName, $formInstanceName); + + $this->assertFalse( + $this->fixture->validateToken( + 'Hello world!', $formName, $formInstanceName + ) + ); + } + + /** + * @test + */ + public function validateTokenWithMismatchingFormNameReturnsFalse() { + $formName = 'foo'; + $formInstanceName = 'bar'; + + $tokenId = $this->fixture->generateToken($formName, $formInstanceName); + + $this->assertFalse( + $this->fixture->validateToken( + $tokenId, 'espresso', $formInstanceName + ) + ); + } + + /** + * @test + */ + public function validateTokenWithMismatchingFormInstanceNameReturnsFalse() { + $formName = 'foo'; + $formInstanceName = 'bar'; + + $tokenId = $this->fixture->generateToken($formName, $formInstanceName); + + $this->assertFalse( + $this->fixture->validateToken( + $tokenId, $formName, 'beer' + ) + ); + } + + /** + * @test + */ + public function validateTokenWithTwoTokensForSameFormNameAndFormInstanceNameReturnsTrueForBoth() { + $formName = 'foo'; + $formInstanceName = 'bar'; + + $tokenId1 = $this->fixture->generateToken($formName, $formInstanceName); + $tokenId2 = $this->fixture->generateToken($formName, $formInstanceName); + + $this->assertTrue( + $this->fixture->validateToken( + $tokenId1, $formName, $formInstanceName + ) + ); + $this->assertTrue( + $this->fixture->validateToken( + $tokenId2, $formName, $formInstanceName + ) + ); + } +} +?> \ No newline at end of file Property changes on: tests/t3lib/formprotection/t3lib_formprotectionTest.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,165 @@ + + */ +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; + } + + + ////////////////////// + // 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 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'; + $formInstanceName = '42'; + + $GLOBALS['BE_USER']->expects($this->once())->method('getSessionData') + ->with('formTokens')->will($this->returnValue(array( + $tokenId => array( + 'formName' => $formName, + 'formInstanceName' => $formInstanceName, + ), + ))); + + $this->fixture->retrieveTokens(); + + $this->assertTrue( + $this->fixture->validateToken($tokenId, $formName, $formInstanceName) + ); + } + + /** + * @test + */ + public function persistTokensWritesTokensToSession() { + $formName = 'foo'; + $formInstanceName = '42'; + + $tokenId = $this->fixture->generateToken($formName, $formInstanceName); + $allTokens = array( + $tokenId => array( + 'formName' => $formName, + 'formInstanceName' => $formInstanceName, + ), + ); + + $GLOBALS['BE_USER']->expects($this->once()) + ->method('setAndSaveSessionData')->with('formTokens', $allTokens); + + $this->fixture->persistTokens(); + } +} +?> \ No newline at end of file Property changes on: tests/t3lib/formprotection/t3lib_formprotection_backendTest.php ___________________________________________________________________ Added: svn:mime-type + text/plain