TYPO3 API  SVNRelease
RequestHashServiceTest.php
Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003 *  Copyright notice
00004 *
00005 *  (c) 2009 Sebastian Kurfürst <sebastian@typo3.org>
00006 *  All rights reserved
00007 *
00008 *  This class is a backport of the corresponding class of FLOW3.
00009 *  All credits go to the v5 team.
00010 *
00011 *  This script is part of the TYPO3 project. The TYPO3 project is
00012 *  free software; you can redistribute it and/or modify
00013 *  it under the terms of the GNU General Public License as published by
00014 *  the Free Software Foundation; either version 2 of the License, or
00015 *  (at your option) any later version.
00016 *
00017 *  The GNU General Public License can be found at
00018 *  http://www.gnu.org/copyleft/gpl.html.
00019 *
00020 *  This script is distributed in the hope that it will be useful,
00021 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00022 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00023 *  GNU General Public License for more details.
00024 *
00025 *  This copyright notice MUST APPEAR in all copies of the script!
00026 ***************************************************************/
00027 
00028 /**
00029  * Testcase for the Request Hash Service
00030  *
00031  * @version $Id: RequestHashService_testcase.php 2334 2010-06-14 16:28:25Z sebastian $
00032  * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser Public License, version 3 or later
00033  */
00034 class Tx_Extbase_Tests_Unit_Security_Channel_RequestHashServiceTest extends Tx_Extbase_Tests_Unit_BaseTestCase {
00035 
00036     public function dataProviderForGenerateRequestHash() {
00037         return array(
00038             // Simple cases
00039             array(
00040                 array(),
00041                 array(),
00042             ),
00043             array(
00044                 array('field1'),
00045                 array('field1' => 1),
00046             ),
00047             array(
00048                 array('field1', 'field2'),
00049                 array(
00050                     'field1' => 1,
00051                     'field2' => 1
00052                 ),
00053             ),
00054             // recursion
00055             array(
00056                 array('field1', 'field[subfield1]', 'field[subfield2]'),
00057                 array(
00058                     'field1' => 1,
00059                     'field' => array(
00060                         'subfield1' => 1,
00061                         'subfield2' => 1
00062                     )
00063                 ),
00064             ),
00065             // recursion with duplicated field name
00066             array(
00067                 array('field1', 'field[subfield1]', 'field[subfield2]', 'field1'),
00068                 array(
00069                     'field1' => 1,
00070                     'field' => array(
00071                         'subfield1' => 1,
00072                         'subfield2' => 1
00073                     )
00074                 ),
00075             ),
00076             // Recursion with un-named fields at the end (...[]). There, they should be made explicit by increasing the counter
00077             array(
00078                 array('field1', 'field[subfield1][]', 'field[subfield1][]', 'field[subfield2]'),
00079                 array(
00080                     'field1' => 1,
00081                     'field' => array(
00082                         'subfield1' => array(
00083                             0 => 1,
00084                             1 => 1
00085                         ),
00086                         'subfield2' => 1
00087                     )
00088                 ),
00089             ),
00090         );
00091     }
00092 
00093     // Data provider for error cases which should throw an exception
00094     public function dataProviderForGenerateRequestHashWithUnallowedValues() {
00095         return array(
00096             // Overriding form fields (string overridden by array)
00097             array(
00098                 array('field1', 'field2', 'field2[bla]', 'field2[blubb]'),
00099             ),
00100             array(
00101                 array('field1', 'field2[bla]', 'field2[bla][blubb][blubb]'),
00102             ),
00103             // Overriding form fields (array overridden by string)
00104             array(
00105                 array('field1', 'field2[bla]', 'field2[blubb]', 'field2'),
00106             ),
00107             array(
00108                 array('field1', 'field2[bla][blubb][blubb]', 'field2[bla]'),
00109             ),
00110             // Empty [] not as last argument
00111             array(
00112                 array('field1', 'field2[][bla]'),
00113             )
00114 
00115         );
00116     }
00117 
00118     /**
00119      * @test
00120      * @author Sebastian Kurfürst <sebastian@typo3.org>
00121      * @dataProvider dataProviderForGenerateRequestHash
00122      */
00123     public function generateRequestHashGeneratesTheCorrectHashesInNormalOperation($input, $expected) {
00124         $requestHashService = $this->getMock('Tx_Extbase_Security_Channel_RequestHashService', array('serializeAndHashFormFieldArray'));
00125         $requestHashService->expects($this->once())->method('serializeAndHashFormFieldArray')->with($expected);
00126         $requestHashService->generateRequestHash($input);
00127     }
00128 
00129     /**
00130      * @test
00131      * @author Sebastian Kurfürst <sebastian@typo3.org>
00132      * @dataProvider dataProviderForGenerateRequestHashWithUnallowedValues
00133      * @expectedException Tx_Extbase_Security_Exception_InvalidArgumentForRequestHashGeneration
00134      */
00135     public function generateRequestHashThrowsExceptionInWrongCases($input) {
00136         $requestHashService = $this->getMock('Tx_Extbase_Security_Channel_RequestHashService', array('serializeAndHashFormFieldArray'));
00137         $requestHashService->generateRequestHash($input);
00138     }
00139 
00140     /**
00141      * @test
00142      * @author Sebastian Kurfürst <sebastian@typo3.org>
00143      */
00144     public function serializeAndHashFormFieldArrayWorks() {
00145         $formFieldArray = array(
00146             'bla' => array(
00147                 'blubb' => 1,
00148                 'hu' => 1
00149             )
00150         );
00151         $mockHash = '12345';
00152 
00153         $hashService = $this->getMock($this->buildAccessibleProxy('Tx_Extbase_Security_Cryptography_HashService'), array('generateHash'));
00154         $hashService->expects($this->once())->method('generateHash')->with(serialize($formFieldArray))->will($this->returnValue($mockHash));
00155 
00156         $requestHashService = $this->getMock($this->buildAccessibleProxy('Tx_Extbase_Security_Channel_RequestHashService'), array('dummy'));
00157         $requestHashService->_set('hashService', $hashService);
00158 
00159         $expected = serialize($formFieldArray) . $mockHash;
00160         $actual = $requestHashService->_call('serializeAndHashFormFieldArray', $formFieldArray);
00161         $this->assertEquals($expected, $actual);
00162     }
00163 
00164     /**
00165      * @test
00166      * @author Sebastian Kurfürst
00167      */
00168     public function verifyRequestHashSetsHmacVerifiedToFalseIfRequestDoesNotHaveAnHmacArgument() {
00169         $request = $this->getMock($this->buildAccessibleProxy('Tx_Extbase_MVC_Web_Request'), array('hasArgument', 'setHmacVerified'));
00170         $request->expects($this->once())->method('hasArgument')->with('__hmac')->will($this->returnValue(FALSE));
00171         $request->expects($this->once())->method('setHmacVerified')->with(FALSE);
00172         $requestHashService = new Tx_Extbase_Security_Channel_RequestHashService;
00173         $requestHashService->verifyRequest($request);
00174     }
00175 
00176     /**
00177      * @test
00178      * @expectedException Tx_Extbase_Security_Exception_SyntacticallyWrongRequestHash
00179      * @author Sebastian Kurfürst
00180      */
00181     public function verifyRequestHashThrowsExceptionIfHmacIsShortherThan40Characters() {
00182         $request = $this->getMock($this->buildAccessibleProxy('Tx_Extbase_MVC_Web_Request'), array('hasArgument', 'getArgument', 'setHmacVerified'));
00183         $request->expects($this->once())->method('hasArgument')->with('__hmac')->will($this->returnValue(TRUE));
00184         $request->expects($this->once())->method('getArgument')->with('__hmac')->will($this->returnValue('abc'));
00185         $requestHashService = new Tx_Extbase_Security_Channel_RequestHashService;
00186         $requestHashService->verifyRequest($request);
00187     }
00188 
00189     /**
00190      * @test
00191      * @author Sebastian Kurfürst
00192      */
00193     public function verifyRequestHashValidatesTheHashAndSetsHmacVerifiedToFalseIfHashCouldNotBeVerified() {
00194         $request = $this->getMock($this->buildAccessibleProxy('Tx_Extbase_MVC_Web_Request'), array('hasArgument', 'getArgument', 'setHmacVerified'));
00195         $request->expects($this->once())->method('hasArgument')->with('__hmac')->will($this->returnValue(TRUE));
00196         $request->expects($this->once())->method('getArgument')->with('__hmac')->will($this->returnValue('11111' . '0000000000000000000000000000000000000000'));
00197         $request->expects($this->once())->method('setHmacVerified')->with(FALSE);
00198 
00199         $hashService = $this->getMock('Tx_Extbase_Security_Cryptography_HashService', array('validateHash'));
00200         $hashService->expects($this->once())->method('validateHash')->with('11111', '0000000000000000000000000000000000000000')->will($this->returnValue(FALSE));
00201 
00202         $requestHashService = $this->getMock($this->buildAccessibleProxy('Tx_Extbase_Security_Channel_RequestHashService'), array('dummy'));
00203         $requestHashService->_set('hashService', $hashService);
00204         $requestHashService->verifyRequest($request);
00205     }
00206 
00207     /**
00208      * @test
00209      * @author Sebastian Kurfürst
00210      */
00211     public function verifyRequestHashValidatesTheHashAndSetsHmacVerifiedToTrueIfArgumentsAreIncludedInTheAllowedArgumentList() {
00212         $data = serialize(array('a' => 1));
00213         $request = $this->getMock($this->buildAccessibleProxy('Tx_Extbase_MVC_Web_Request'), array('hasArgument', 'getArgument', 'getArguments', 'setHmacVerified'));
00214         $request->expects($this->once())->method('hasArgument')->with('__hmac')->will($this->returnValue(TRUE));
00215         $request->expects($this->once())->method('getArgument')->with('__hmac')->will($this->returnValue($data . '0000000000000000000000000000000000000000'));
00216         $request->expects($this->once())->method('getArguments')->will($this->returnValue(array(
00217             '__hmac' => 'ABC',
00218             '__referrer' => '...',
00219             'a' => 'bla'
00220         )));
00221         $request->expects($this->once())->method('setHmacVerified')->with(TRUE);
00222 
00223         $hashService = $this->getMock('Tx_Extbase_Security_Cryptography_HashService', array('validateHash'));
00224         $hashService->expects($this->once())->method('validateHash')->with($data, '0000000000000000000000000000000000000000')->will($this->returnValue(TRUE));
00225 
00226         $requestHashService = $this->getMock($this->buildAccessibleProxy('Tx_Extbase_Security_Channel_RequestHashService'), array('checkFieldNameInclusion'));
00227         $requestHashService->expects($this->once())->method('checkFieldNameInclusion')->with(array('a' => 'bla'), array('a' => 1))->will($this->returnValue(TRUE));
00228         $requestHashService->_set('hashService', $hashService);
00229         $requestHashService->verifyRequest($request);
00230     }
00231 
00232     /**
00233      * @test
00234      * @author Sebastian Kurfürst
00235      */
00236     public function verifyRequestHashValidatesTheHashAndSetsHmacVerifiedToFalseIfNotAllArgumentsAreIncludedInTheAllowedArgumentList() {
00237         $data = serialize(array('a' => 1));
00238         $request = $this->getMock($this->buildAccessibleProxy('Tx_Extbase_MVC_Web_Request'), array('hasArgument', 'getArgument', 'getArguments', 'setHmacVerified'));
00239         $request->expects($this->once())->method('hasArgument')->with('__hmac')->will($this->returnValue(TRUE));
00240         $request->expects($this->once())->method('getArgument')->with('__hmac')->will($this->returnValue($data . '0000000000000000000000000000000000000000'));
00241         $request->expects($this->once())->method('getArguments')->will($this->returnValue(array(
00242             '__hmac' => 'ABC',
00243             '__referrer' => '...',
00244             'a' => 'bla',
00245             'b' => 'blubb'
00246         )));
00247         $request->expects($this->once())->method('setHmacVerified')->with(FALSE);
00248 
00249         $hashService = $this->getMock('Tx_Extbase_Security_Cryptography_HashService', array('validateHash'));
00250         $hashService->expects($this->once())->method('validateHash')->with($data, '0000000000000000000000000000000000000000')->will($this->returnValue(TRUE));
00251 
00252         $requestHashService = $this->getMock($this->buildAccessibleProxy('Tx_Extbase_Security_Channel_RequestHashService'), array('checkFieldNameInclusion'));
00253         $requestHashService->expects($this->once())->method('checkFieldNameInclusion')->with(array('a' => 'bla', 'b' => 'blubb'), array('a' => 1))->will($this->returnValue(FALSE));
00254         $requestHashService->_set('hashService', $hashService);
00255         $requestHashService->verifyRequest($request);
00256     }
00257 
00258     /**
00259      * Data Provider for checkFieldNameInclusionWorks
00260      */
00261     public function dataProviderForCheckFieldNameInclusion() {
00262         return array(
00263             // Simple fields with requestfields = responsefields
00264             array(
00265                 // Request
00266                 array(
00267                     'a' => 'X',
00268                     'b' => 'X',
00269                     'c' => 'X'
00270                 ),
00271                 // Allowed
00272                 array(
00273                     'a' => 1,
00274                     'b' => 1,
00275                     'c' => 1
00276                 ),
00277                 // Expected result
00278                 TRUE
00279             ),
00280             // Simple fields with requestfields < responsefields
00281             array(
00282                 // Request
00283                 array(
00284                     'a' => 'X',
00285                     'c' => 'X'
00286                 ),
00287                 // Allowed
00288                 array(
00289                     'a' => 1,
00290                     'b' => 1,
00291                     'c' => 1
00292                 ),
00293                 // Expected result
00294                 TRUE
00295             ),
00296             // Simple fields with requestfields > responsefields
00297             array(
00298                 // Request
00299                 array(
00300                     'a' => 'X',
00301                     'b' => 'X',
00302                     'c' => 'X'
00303                 ),
00304                 // Allowed
00305                 array(
00306                     'a' => 1,
00307                     'b' => 1
00308                 ),
00309                 // Expected result
00310                 FALSE
00311             ),
00312             // Hierarchical fields with requestfields < responsefields
00313             array(
00314                 // Request
00315                 array(
00316                     'a' => array(
00317                         'b' => 'X'
00318                     ),
00319                     'c' => 'X'
00320                 ),
00321                 // Allowed
00322                 array(
00323                     'a' => array(
00324                         'b' => 1,
00325                         'abc' => 1
00326                     ),
00327                     'c' => 1
00328                 ),
00329                 // Expected result
00330                 TRUE
00331             ),
00332             // Hierarchical fields with requestfields > responsefields
00333             array(
00334                 // Request
00335                 array(
00336                     'a' => array(
00337                         'b' => 'X',
00338                         'abc' => 'X'
00339                     ),
00340                     'c' => 'X'
00341                 ),
00342                 // Allowed
00343                 array(
00344                     'a' => array(
00345                         'b' => 1
00346                     ),
00347                     'c' => 1
00348                 ),
00349                 // Expected result
00350                 FALSE
00351             ),
00352             // hierarchical fields with requestfields != responsefields (different types) - 1
00353             array(
00354                 // Request
00355                 array(
00356                     'a' => array(
00357                         'b' => 'X',
00358                         'c' => 'X'
00359                     ),
00360                     'b' => 'X',
00361                     'c' => 'X'
00362                 ),
00363                 // Allowed
00364                 array(
00365                     'a' => 1,
00366                     'b' => 1,
00367                     'c' => 1
00368                 ),
00369                 // Expected result
00370                 FALSE
00371             ),
00372             // hierarchical fields with requestfields != responsefields (different types) - 2
00373             array(
00374                 // Request
00375                 array(
00376                     'a' => 'X',
00377                     'b' => 'X',
00378                     'c' => 'X'
00379                 ),
00380                 // Allowed
00381                 array(
00382                     'a' => array(
00383                         'x' => 1,
00384                         'y' => 1
00385                     ),
00386                     'b' => 1,
00387                     'c' => 1
00388                 ),
00389                 // Expected result
00390                 FALSE
00391             ),
00392 
00393             // hierarchical fields with requestfields != responsefields (different types)
00394             // This case happens if an array of checkboxes is rendered, in case they are fully unchecked.
00395             array(
00396                 // Request
00397                 array(
00398                     'a' => '', // this is the only allowed value.
00399                     'b' => 'X',
00400                     'c' => 'X'
00401                 ),
00402                 // Allowed
00403                 array(
00404                     'a' => array(
00405                         'x' => 1,
00406                         'y' => 1
00407                     ),
00408                     'b' => 1,
00409                     'c' => 1
00410                 ),
00411                 // Expected result
00412                 TRUE
00413             ),
00414         );
00415     }
00416 
00417     /**
00418      * @test
00419      * @author Sebastian Kurfürst <sebastian@typo3.org>
00420      * @dataProvider dataProviderForCheckFieldNameInclusion
00421      */
00422     public function checkFieldNameInclusionWorks($requestArguments, $allowedFields, $expectedResult) {
00423         $requestHashService = $this->getMock($this->buildAccessibleProxy('Tx_Extbase_Security_Channel_RequestHashService'), array('dummy'));
00424         $this->assertEquals($expectedResult, $requestHashService->_call('checkFieldNameInclusion', $requestArguments, $allowedFields));
00425     }
00426 }
00427 ?>