[TASK] Switch to json_encode/json_decode for Extbase arguments
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Tests / Unit / Mvc / Controller / MvcPropertyMappingConfigurationServiceTest.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Tests\Unit\Mvc\Controller;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Extbase\Security\Exception\InvalidArgumentForHashGenerationException;
18 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
19
20 /**
21 * Test case
22 */
23 class MvcPropertyMappingConfigurationServiceTest extends UnitTestCase
24 {
25 /**
26 * Data provider for generating the list of trusted properties
27 *
28 * @return array
29 */
30 public function dataProviderForgenerateTrustedPropertiesToken()
31 {
32 return [
33 'Simple Case - Empty' => [
34 [],
35 [],
36 ],
37 'Simple Case - Single Value' => [
38 ['field1'],
39 ['field1' => 1],
40 ],
41 'Simple Case - Two Values' => [
42 ['field1', 'field2'],
43 [
44 'field1' => 1,
45 'field2' => 1
46 ],
47 ],
48 'Recursion' => [
49 ['field1', 'field[subfield1]', 'field[subfield2]'],
50 [
51 'field1' => 1,
52 'field' => [
53 'subfield1' => 1,
54 'subfield2' => 1
55 ]
56 ],
57 ],
58 'recursion with duplicated field name' => [
59 ['field1', 'field[subfield1]', 'field[subfield2]', 'field1'],
60 [
61 'field1' => 1,
62 'field' => [
63 'subfield1' => 1,
64 'subfield2' => 1
65 ]
66 ],
67 ],
68 'Recursion with un-named fields at the end (...[]). There, they should be made explicit by increasing the counter' => [
69 ['field1', 'field[subfield1][]', 'field[subfield1][]', 'field[subfield2]'],
70 [
71 'field1' => 1,
72 'field' => [
73 'subfield1' => [
74 0 => 1,
75 1 => 1
76 ],
77 'subfield2' => 1
78 ]
79 ],
80 ],
81 ];
82 }
83
84 /**
85 * Data Provider for invalid values in generating the list of trusted properties,
86 * which should result in an exception
87 *
88 * @return array
89 */
90 public function dataProviderForgenerateTrustedPropertiesTokenWithUnallowedValues()
91 {
92 return [
93 'Overriding form fields (string overridden by array) - 1' => [
94 ['field1', 'field2', 'field2[bla]', 'field2[blubb]'],
95 1255072196
96 ],
97 'Overriding form fields (string overridden by array) - 2' => [
98 ['field1', 'field2[bla]', 'field2[bla][blubb][blubb]'],
99 1255072196
100 ],
101 'Overriding form fields (array overridden by string) - 1' => [
102 ['field1', 'field2[bla]', 'field2[blubb]', 'field2'],
103 1255072587
104 ],
105 'Overriding form fields (array overridden by string) - 2' => [
106 ['field1', 'field2[bla][blubb][blubb]', 'field2[bla]'],
107 1255072587
108 ],
109 'Empty [] not as last argument' => [
110 ['field1', 'field2[][bla]'],
111 1255072832
112 ]
113
114 ];
115 }
116
117 /**
118 * @test
119 * @dataProvider dataProviderForgenerateTrustedPropertiesToken
120 */
121 public function generateTrustedPropertiesTokenGeneratesTheCorrectHashesInNormalOperation($input, $expected)
122 {
123 $requestHashService = $this->getMockBuilder(\TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService::class)
124 ->setMethods(['serializeAndHashFormFieldArray'])
125 ->getMock();
126 $requestHashService->expects($this->once())->method('serializeAndHashFormFieldArray')->with($expected);
127 $requestHashService->generateTrustedPropertiesToken($input);
128 }
129
130 /**
131 * @param $input
132 * @param $expectExceptionCode
133 * @test
134 * @dataProvider dataProviderForgenerateTrustedPropertiesTokenWithUnallowedValues
135 */
136 public function generateTrustedPropertiesTokenThrowsExceptionInWrongCases($input, $expectExceptionCode)
137 {
138 $this->expectException(InvalidArgumentForHashGenerationException::class);
139 $this->expectExceptionCode($expectExceptionCode);
140 $requestHashService = $this->getMockBuilder(\TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService::class)
141 ->setMethods(['serializeAndHashFormFieldArray'])
142 ->getMock();
143 $requestHashService->generateTrustedPropertiesToken($input);
144 }
145
146 /**
147 * @test
148 */
149 public function serializeAndHashFormFieldArrayWorks()
150 {
151 $formFieldArray = [
152 'bla' => [
153 'blubb' => 1,
154 'hu' => 1
155 ]
156 ];
157 $mockHash = '12345';
158
159 $hashService = $this->getMockBuilder($this->buildAccessibleProxy(\TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService::class))
160 ->setMethods(['appendHmac'])
161 ->getMock();
162 $hashService->expects($this->once())->method('appendHmac')->with(json_encode($formFieldArray))->will($this->returnValue(json_encode($formFieldArray) . $mockHash));
163
164 $requestHashService = $this->getMockBuilder($this->buildAccessibleProxy(\TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService::class))
165 ->setMethods(['dummy'])
166 ->getMock();
167 $requestHashService->_set('hashService', $hashService);
168
169 $expected = json_encode($formFieldArray) . $mockHash;
170 $actual = $requestHashService->_call('serializeAndHashFormFieldArray', $formFieldArray);
171 $this->assertEquals($expected, $actual);
172 }
173
174 /**
175 * @test
176 */
177 public function initializePropertyMappingConfigurationDoesNothingIfTrustedPropertiesAreNotSet()
178 {
179 $request = $this->getMockBuilder(\TYPO3\CMS\Extbase\Mvc\Request::class)->setMethods(['getInternalArgument'])->disableOriginalConstructor()->getMock();
180 $request->expects($this->any())->method('getInternalArgument')->with('__trustedProperties')->will($this->returnValue(null));
181 $arguments = new \TYPO3\CMS\Extbase\Mvc\Controller\Arguments();
182
183 $requestHashService = new \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService;
184 $requestHashService->initializePropertyMappingConfigurationFromRequest($request, $arguments);
185 }
186
187 /**
188 * @test
189 */
190 public function initializePropertyMappingConfigurationReturnsEarlyIfNoTrustedPropertiesAreSet()
191 {
192 $trustedProperties = [
193 'foo' => 1
194 ];
195 $this->initializePropertyMappingConfiguration($trustedProperties);
196 }
197
198 /**
199 * @test
200 */
201 public function initializePropertyMappingConfigurationReturnsEarlyIfArgumentIsUnknown()
202 {
203 $trustedProperties = [
204 'nonExistingArgument' => 1
205 ];
206 $arguments = $this->initializePropertyMappingConfiguration($trustedProperties);
207 $this->assertFalse($arguments->hasArgument('nonExistingArgument'));
208 }
209
210 /**
211 * @test
212 */
213 public function initializePropertyMappingConfigurationSetsModificationAllowedIfIdentityPropertyIsSet()
214 {
215 $trustedProperties = [
216 'foo' => [
217 '__identity' => 1,
218 'nested' => [
219 '__identity' => 1,
220 ]
221 ]
222 ];
223 $arguments = $this->initializePropertyMappingConfiguration($trustedProperties);
224 $propertyMappingConfiguration = $arguments->getArgument('foo')->getPropertyMappingConfiguration();
225 $this->assertTrue($propertyMappingConfiguration->getConfigurationValue(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::class, \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED), 'ConfigurationValue is not CONFIGURATION_MODIFICATION_ALLOWED at line ' . __LINE__);
226 $this->assertNull($propertyMappingConfiguration->getConfigurationValue(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::class, \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED), 'ConfigurationValue is not NULL at line ' . __LINE__);
227 $this->assertFalse($propertyMappingConfiguration->shouldMap('someProperty'), 'Value is not FALSE at line ' . __LINE__);
228
229 $this->assertTrue($propertyMappingConfiguration->forProperty('nested')->getConfigurationValue(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::class, \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED), 'ConfigurationValue is not CONFIGURATION_MODIFICATION_ALLOWED at line ' . __LINE__);
230 $this->assertNull($propertyMappingConfiguration->forProperty('nested')->getConfigurationValue(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::class, \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED), 'ConfigurationValue is not NULL at line ' . __LINE__);
231 $this->assertFalse($propertyMappingConfiguration->forProperty('nested')->shouldMap('someProperty'), 'Value is not FALSE at line ' . __LINE__);
232 }
233
234 /**
235 * @test
236 */
237 public function initializePropertyMappingConfigurationSetsCreationAllowedIfIdentityPropertyIsNotSet()
238 {
239 $trustedProperties = [
240 'foo' => [
241 'bar' => []
242 ]
243 ];
244 $arguments = $this->initializePropertyMappingConfiguration($trustedProperties);
245 $propertyMappingConfiguration = $arguments->getArgument('foo')->getPropertyMappingConfiguration();
246 $this->assertNull($propertyMappingConfiguration->getConfigurationValue(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::class, \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED));
247 $this->assertTrue($propertyMappingConfiguration->getConfigurationValue(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::class, \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED));
248 $this->assertFalse($propertyMappingConfiguration->shouldMap('someProperty'));
249
250 $this->assertNull($propertyMappingConfiguration->forProperty('bar')->getConfigurationValue(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::class, \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED));
251 $this->assertTrue($propertyMappingConfiguration->forProperty('bar')->getConfigurationValue(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::class, \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED));
252 $this->assertFalse($propertyMappingConfiguration->forProperty('bar')->shouldMap('someProperty'));
253 }
254
255 /**
256 * @test
257 */
258 public function initializePropertyMappingConfigurationSetsAllowedFields()
259 {
260 $trustedProperties = [
261 'foo' => [
262 'bar' => 1
263 ]
264 ];
265 $arguments = $this->initializePropertyMappingConfiguration($trustedProperties);
266 $propertyMappingConfiguration = $arguments->getArgument('foo')->getPropertyMappingConfiguration();
267 $this->assertFalse($propertyMappingConfiguration->shouldMap('someProperty'));
268 $this->assertTrue($propertyMappingConfiguration->shouldMap('bar'));
269 }
270
271 /**
272 * @test
273 */
274 public function initializePropertyMappingConfigurationSetsAllowedFieldsRecursively()
275 {
276 $trustedProperties = [
277 'foo' => [
278 'bar' => [
279 'foo' => 1
280 ]
281 ]
282 ];
283 $arguments = $this->initializePropertyMappingConfiguration($trustedProperties);
284 $propertyMappingConfiguration = $arguments->getArgument('foo')->getPropertyMappingConfiguration();
285 $this->assertFalse($propertyMappingConfiguration->shouldMap('someProperty'));
286 $this->assertTrue($propertyMappingConfiguration->shouldMap('bar'));
287 $this->assertTrue($propertyMappingConfiguration->forProperty('bar')->shouldMap('foo'));
288 }
289
290 /**
291 * Helper which initializes the property mapping configuration and returns arguments
292 *
293 * @param array $trustedProperties
294 *
295 * @return \TYPO3\CMS\Extbase\Mvc\Controller\Arguments
296 */
297 protected function initializePropertyMappingConfiguration(array $trustedProperties)
298 {
299 $request = $this->getMockBuilder(\TYPO3\CMS\Extbase\Mvc\Request::class)->setMethods(['getInternalArgument'])->disableOriginalConstructor()->getMock();
300 $request->expects($this->any())->method('getInternalArgument')->with('__trustedProperties')->will($this->returnValue('fooTrustedProperties'));
301
302 $mockHashService = $this->getMockBuilder(\TYPO3\CMS\Extbase\Security\Cryptography\HashService::class)
303 ->setMethods(['validateAndStripHmac'])
304 ->getMock();
305 $mockHashService->expects($this->once())->method('validateAndStripHmac')->with('fooTrustedProperties')->will($this->returnValue(json_encode($trustedProperties)));
306
307 $requestHashService = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService::class, ['dummy']);
308 $requestHashService->_set('hashService', $mockHashService);
309
310 $mockObjectManager = $this->createMock(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface::class);
311 $mockArgument = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Mvc\Controller\Argument::class, ['getName'], [], '', false);
312
313 $propertyMappingConfiguration = new \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfiguration();
314
315 $mockArgument->_set('propertyMappingConfiguration', $propertyMappingConfiguration);
316 $mockArgument->expects($this->any())->method('getName')->will($this->returnValue('foo'));
317 $mockObjectManager->expects($this->once())->method('get')->with(\TYPO3\CMS\Extbase\Mvc\Controller\Argument::class)->will($this->returnValue($mockArgument));
318
319 $arguments = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Mvc\Controller\Arguments::class, ['dummy']);
320 $arguments->_set('objectManager', $mockObjectManager);
321 $arguments->addNewArgument('foo');
322
323 $requestHashService->initializePropertyMappingConfigurationFromRequest($request, $arguments);
324
325 return $arguments;
326 }
327 }