[SECURITY] Validate complete referring request
[Packages/TYPO3.CMS.git] / typo3 / sysext / fluid / Tests / Unit / ViewHelpers / FormViewHelperTest.php
1 <?php
2 namespace TYPO3\CMS\Fluid\Tests\Unit\ViewHelpers;
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 use TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper;
17 use TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperInterface;
18
19 /**
20 * Test for the Form view helper
21 */
22 class FormViewHelperTest extends ViewHelperBaseTestcase
23 {
24 /**
25 * @var \TYPO3\CMS\Extbase\Service\ExtensionService
26 */
27 protected $mockExtensionService;
28
29 /**
30 * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
31 */
32 protected $mockConfigurationManager;
33
34 protected function setUp()
35 {
36 parent::setUp();
37 $this->mockExtensionService = $this->getMock(\TYPO3\CMS\Extbase\Service\ExtensionService::class);
38 $this->mockConfigurationManager = $this->getMock(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::class);
39 }
40
41 /**
42 * @test
43 */
44 public function initializeArgumentsRegistersExpectedArguments()
45 {
46 $viewHelper = $this->getMock(FormViewHelper::class, array('registerTagAttribute', 'registerUniversalTagAttributes'));
47 $viewHelper->expects($this->at(0))->method('registerTagAttribute')->with('enctype', 'string', $this->anything());
48 $viewHelper->expects($this->at(1))->method('registerTagAttribute')->with('method', 'string', $this->anything());
49 $viewHelper->expects($this->at(2))->method('registerTagAttribute')->with('name', 'string', $this->anything());
50 $viewHelper->expects($this->at(3))->method('registerTagAttribute')->with('onreset', 'string', $this->anything());
51 $viewHelper->expects($this->at(4))->method('registerTagAttribute')->with('onsubmit', 'string', $this->anything());
52 $viewHelper->expects($this->once())->method('registerUniversalTagAttributes');
53 $viewHelper->initializeArguments();
54 }
55
56 /**
57 * @test
58 */
59 public function setFormActionUriRespectsOverriddenArgument()
60 {
61 $viewHelper = $this->getAccessibleMock(FormViewHelper::class, array('hasArgument'));
62 $viewHelper->expects($this->once())->method('hasArgument')->with('actionUri')->willReturn(true);
63 $tagBuilder = $this->getMock(TagBuilder::class, array('addAttribute'));
64 $tagBuilder->expects($this->once())->method('addAttribute')->with('action', 'foobar');
65 $viewHelper->_set('tag', $tagBuilder);
66 $viewHelper->setArguments(array('actionUri' => 'foobar'));
67 $this->callInaccessibleMethod($viewHelper, 'setFormActionUri');
68 }
69
70 /**
71 * @param ViewHelperInterface $viewHelper
72 * @return void
73 */
74 protected function injectDependenciesIntoViewHelper(ViewHelperInterface $viewHelper)
75 {
76 $viewHelper->_set('configurationManager', $this->mockConfigurationManager);
77 parent::injectDependenciesIntoViewHelper($viewHelper);
78 $hashService = $this->getMock(\TYPO3\CMS\Extbase\Security\Cryptography\HashService::class, array('appendHmac'));
79 $hashService->expects($this->any())->method('appendHmac')->will($this->returnValue(''));
80 $this->mvcPropertyMapperConfigurationService->_set('hashService', $hashService);
81 $viewHelper->_set('mvcPropertyMapperConfigurationService', $this->mvcPropertyMapperConfigurationService);
82 $viewHelper->_set('hashService', $hashService);
83 }
84
85 /**
86 * @test
87 */
88 public function renderAddsObjectToViewHelperVariableContainer()
89 {
90 $formObject = new \stdClass();
91 $viewHelper = $this->getAccessibleMock(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, array('renderChildren', 'renderHiddenIdentityField', 'renderAdditionalIdentityFields', 'renderHiddenReferrerFields', 'renderHiddenSecuredReferrerField', 'renderRequestHashField', 'addFormObjectNameToViewHelperVariableContainer', 'addFieldNamePrefixToViewHelperVariableContainer', 'removeFormObjectNameFromViewHelperVariableContainer', 'removeFieldNamePrefixFromViewHelperVariableContainer', 'addFormFieldNamesToViewHelperVariableContainer', 'removeFormFieldNamesFromViewHelperVariableContainer', 'renderTrustedPropertiesField'), array(), '', false);
92 $this->injectDependenciesIntoViewHelper($viewHelper);
93 $viewHelper->setArguments(array('object' => $formObject));
94 $this->viewHelperVariableContainer->expects($this->at(0))->method('add')->with(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formObject', $formObject);
95 $this->viewHelperVariableContainer->expects($this->at(1))->method('add')->with(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'additionalIdentityProperties', array());
96 $this->viewHelperVariableContainer->expects($this->at(2))->method('remove')->with(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formObject');
97 $this->viewHelperVariableContainer->expects($this->at(3))->method('remove')->with(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'additionalIdentityProperties');
98 $viewHelper->render();
99 }
100
101 /**
102 * @test
103 */
104 public function renderAddsObjectNameToTemplateVariableContainer()
105 {
106 $objectName = 'someObjectName';
107 $viewHelper = $this->getAccessibleMock(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, array('renderChildren', 'renderHiddenIdentityField', 'renderHiddenReferrerFields', 'renderHiddenSecuredReferrerField', 'renderRequestHashField', 'addFormObjectToViewHelperVariableContainer', 'addFieldNamePrefixToViewHelperVariableContainer', 'removeFormObjectFromViewHelperVariableContainer', 'removeFieldNamePrefixFromViewHelperVariableContainer', 'addFormFieldNamesToViewHelperVariableContainer', 'removeFormFieldNamesFromViewHelperVariableContainer', 'renderTrustedPropertiesField'), array(), '', false);
108 $this->injectDependenciesIntoViewHelper($viewHelper);
109 $viewHelper->setArguments(array('name' => $objectName));
110 $this->viewHelperVariableContainer->expects($this->once())->method('add')->with(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formObjectName', $objectName);
111 $this->viewHelperVariableContainer->expects($this->once())->method('remove')->with(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formObjectName');
112 $viewHelper->render();
113 }
114
115 /**
116 * @test
117 */
118 public function formObjectNameArgumentOverrulesNameArgument()
119 {
120 $objectName = 'someObjectName';
121 $viewHelper = $this->getAccessibleMock(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, array('renderChildren', 'renderHiddenIdentityField', 'renderHiddenReferrerFields', 'renderRequestHashField', 'addFormObjectToViewHelperVariableContainer', 'addFieldNamePrefixToViewHelperVariableContainer', 'removeFormObjectFromViewHelperVariableContainer', 'removeFieldNamePrefixFromViewHelperVariableContainer', 'addFormFieldNamesToViewHelperVariableContainer', 'removeFormFieldNamesFromViewHelperVariableContainer', 'renderTrustedPropertiesField'), array(), '', false);
122 $this->injectDependenciesIntoViewHelper($viewHelper);
123 $viewHelper->setArguments(array('name' => 'formName', 'objectName' => $objectName));
124 $this->viewHelperVariableContainer->expects($this->once())->method('add')->with(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formObjectName', $objectName);
125 $this->viewHelperVariableContainer->expects($this->once())->method('remove')->with(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formObjectName');
126 $viewHelper->render();
127 }
128
129 /**
130 * @test
131 */
132 public function renderCallsRenderHiddenReferrerFields()
133 {
134 $viewHelper = $this->getAccessibleMock(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, array('renderChildren', 'renderRequestHashField', 'renderHiddenReferrerFields', 'renderTrustedPropertiesField'), array(), '', false);
135 $viewHelper->expects($this->once())->method('renderHiddenReferrerFields');
136 $this->injectDependenciesIntoViewHelper($viewHelper);
137 $viewHelper->render();
138 }
139
140 /**
141 * @test
142 */
143 public function renderCallsRenderHiddenIdentityField()
144 {
145 $object = new \stdClass();
146 $viewHelper = $this->getAccessibleMock(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, array('renderChildren', 'renderRequestHashField', 'renderHiddenReferrerFields', 'renderHiddenIdentityField', 'getFormObjectName', 'renderTrustedPropertiesField'), array(), '', false);
147 $this->injectDependenciesIntoViewHelper($viewHelper);
148 $viewHelper->setArguments(array('object' => $object));
149 $viewHelper->expects($this->atLeastOnce())->method('getFormObjectName')->will($this->returnValue('MyName'));
150 $viewHelper->expects($this->once())->method('renderHiddenIdentityField')->with($object, 'MyName');
151 $viewHelper->render();
152 }
153
154 /**
155 * @test
156 */
157 public function renderCallsRenderAdditionalIdentityFields()
158 {
159 $viewHelper = $this->getAccessibleMock(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, array('renderChildren', 'renderRequestHashField', 'renderHiddenReferrerFields', 'renderAdditionalIdentityFields', 'renderTrustedPropertiesField'), array(), '', false);
160 $viewHelper->expects($this->once())->method('renderAdditionalIdentityFields');
161 $this->injectDependenciesIntoViewHelper($viewHelper);
162 $viewHelper->render();
163 }
164
165 /**
166 * @test
167 */
168 public function renderWrapsHiddenFieldsWithDivForXhtmlCompatibilityWithRewrittenPropertyMapper()
169 {
170 $viewHelper = $this->getAccessibleMock($this->buildAccessibleProxy(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class), array('renderChildren', 'renderHiddenIdentityField', 'renderAdditionalIdentityFields', 'renderHiddenReferrerFields', 'renderHiddenSecuredReferrerField', 'renderTrustedPropertiesField'), array(), '', false);
171 $this->mvcPropertyMapperConfigurationService->_set('hashService', new \TYPO3\CMS\Extbase\Security\Cryptography\HashService());
172 $viewHelper->_set('mvcPropertyMapperConfigurationService', $this->mvcPropertyMapperConfigurationService);
173 parent::injectDependenciesIntoViewHelper($viewHelper);
174 $viewHelper->expects($this->once())->method('renderHiddenIdentityField')->will($this->returnValue('hiddenIdentityField'));
175 $viewHelper->expects($this->once())->method('renderAdditionalIdentityFields')->will($this->returnValue('additionalIdentityFields'));
176 $viewHelper->expects($this->once())->method('renderHiddenReferrerFields')->will($this->returnValue('hiddenReferrerFields'));
177 $viewHelper->expects($this->once())->method('renderChildren')->will($this->returnValue('formContent'));
178 $expectedResult = chr(10) . '<div>' . 'hiddenIdentityFieldadditionalIdentityFieldshiddenReferrerFields' . chr(10) . '</div>' . chr(10) . 'formContent';
179 $this->tagBuilder->expects($this->once())->method('setContent')->with($expectedResult);
180 $viewHelper->render();
181 }
182
183 /**
184 * @test
185 */
186 public function renderWrapsHiddenFieldsWithDivAndAnAdditionalClassForXhtmlCompatibilityWithRewrittenPropertyMapper()
187 {
188 $viewHelper = $this->getMock($this->buildAccessibleProxy(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class), array('renderChildren', 'renderHiddenIdentityField', 'renderAdditionalIdentityFields', 'renderHiddenReferrerFields', 'renderHiddenSecuredReferrerField', 'renderTrustedPropertiesField'), array(), '', false);
189 $this->mvcPropertyMapperConfigurationService->_set('hashService', new \TYPO3\CMS\Extbase\Security\Cryptography\HashService());
190 $viewHelper->_set('mvcPropertyMapperConfigurationService', $this->mvcPropertyMapperConfigurationService);
191 parent::injectDependenciesIntoViewHelper($viewHelper);
192 $viewHelper->expects($this->once())->method('renderHiddenIdentityField')->will($this->returnValue('hiddenIdentityField'));
193 $viewHelper->expects($this->once())->method('renderAdditionalIdentityFields')->will($this->returnValue('additionalIdentityFields'));
194 $viewHelper->expects($this->once())->method('renderHiddenReferrerFields')->will($this->returnValue('hiddenReferrerFields'));
195 $viewHelper->expects($this->once())->method('renderChildren')->will($this->returnValue('formContent'));
196 $expectedResult = chr(10) . '<div class="hidden">' . 'hiddenIdentityFieldadditionalIdentityFieldshiddenReferrerFields' . chr(10) . '</div>' . chr(10) . 'formContent';
197 $this->tagBuilder->expects($this->once())->method('setContent')->with($expectedResult);
198 $viewHelper->setArguments(array('hiddenFieldClassName' => 'hidden'));
199 $viewHelper->render();
200 }
201
202 /**
203 * @test
204 */
205 public function renderAdditionalIdentityFieldsFetchesTheFieldsFromViewHelperVariableContainerAndBuildsHiddenFieldsForThem()
206 {
207 $identityProperties = array(
208 'object1[object2]' => '<input type="hidden" name="object1[object2][__identity]" value="42" />',
209 'object1[object2][subobject]' => '<input type="hidden" name="object1[object2][subobject][__identity]" value="21" />'
210 );
211 $this->viewHelperVariableContainer->expects($this->once())->method('exists')->with(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'additionalIdentityProperties')->will($this->returnValue(true));
212 $this->viewHelperVariableContainer->expects($this->once())->method('get')->with(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'additionalIdentityProperties')->will($this->returnValue($identityProperties));
213 $viewHelper = $this->getAccessibleMock(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, array('renderChildren'), array(), '', false);
214 $this->injectDependenciesIntoViewHelper($viewHelper);
215 $expected = chr(10) . '<input type="hidden" name="object1[object2][__identity]" value="42" />' . chr(10) . '<input type="hidden" name="object1[object2][subobject][__identity]" value="21" />';
216 $actual = $viewHelper->_call('renderAdditionalIdentityFields');
217 $this->assertEquals($expected, $actual);
218 }
219
220 /**
221 * @test
222 */
223 public function renderHiddenReferrerFieldsAddCurrentControllerAndActionAsHiddenFields()
224 {
225 $viewHelper = $this->getAccessibleMock(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, array('dummy'), array(), '', false);
226 $this->injectDependenciesIntoViewHelper($viewHelper);
227 $this->request->expects($this->atLeastOnce())->method('getControllerExtensionName')->will($this->returnValue('extensionName'));
228 $this->request->expects($this->atLeastOnce())->method('getControllerName')->will($this->returnValue('controllerName'));
229 $this->request->expects($this->atLeastOnce())->method('getControllerActionName')->will($this->returnValue('controllerActionName'));
230 $hiddenFields = $viewHelper->_call('renderHiddenReferrerFields');
231 $expectedResult = chr(10) . '<input type="hidden" name="__referrer[@extension]" value="extensionName" />'
232 . chr(10) . '<input type="hidden" name="__referrer[@controller]" value="controllerName" />'
233 . chr(10) . '<input type="hidden" name="__referrer[@action]" value="controllerActionName" />'
234 . chr(10) . '<input type="hidden" name="__referrer[arguments]" value="" />'
235 . chr(10) . '<input type="hidden" name="__referrer[@request]" value="" />' . chr(10);
236 $this->assertEquals($expectedResult, $hiddenFields);
237 }
238
239 /**
240 * @test
241 */
242 public function renderAddsSpecifiedPrefixToTemplateVariableContainer()
243 {
244 $prefix = 'somePrefix';
245 $viewHelper = $this->getAccessibleMock(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, array('renderChildren', 'renderHiddenIdentityField', 'renderHiddenReferrerFields', 'renderRequestHashField', 'addFormFieldNamesToViewHelperVariableContainer', 'removeFormFieldNamesFromViewHelperVariableContainer', 'renderTrustedPropertiesField'), array(), '', false);
246 $this->injectDependenciesIntoViewHelper($viewHelper);
247 $viewHelper->setArguments(array('fieldNamePrefix' => $prefix));
248 $this->viewHelperVariableContainer->expects($this->once())->method('add')->with(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'fieldNamePrefix', $prefix);
249 $this->viewHelperVariableContainer->expects($this->once())->method('remove')->with(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'fieldNamePrefix');
250 $viewHelper->render();
251 }
252
253 /**
254 * @test
255 */
256 public function renderAddsDefaultFieldNamePrefixToTemplateVariableContainerIfNoPrefixIsSpecified()
257 {
258 $expectedPrefix = 'tx_someextension_someplugin';
259 $viewHelper = $this->getAccessibleMock(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, array('renderChildren', 'renderHiddenIdentityField', 'renderHiddenReferrerFields', 'renderRequestHashField', 'addFormFieldNamesToViewHelperVariableContainer', 'removeFormFieldNamesFromViewHelperVariableContainer', 'renderTrustedPropertiesField'), array(), '', false);
260 $this->mockExtensionService->expects($this->once())->method('getPluginNamespace')->with('SomeExtension', 'SomePlugin')->will($this->returnValue('tx_someextension_someplugin'));
261 $viewHelper->_set('extensionService', $this->mockExtensionService);
262 $this->injectDependenciesIntoViewHelper($viewHelper);
263 $viewHelper->setArguments(array('extensionName' => 'SomeExtension', 'pluginName' => 'SomePlugin'));
264 $this->viewHelperVariableContainer->expects($this->once())->method('add')->with(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'fieldNamePrefix', $expectedPrefix);
265 $this->viewHelperVariableContainer->expects($this->once())->method('remove')->with(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'fieldNamePrefix');
266 $viewHelper->render();
267 }
268
269 /**
270 * Data Provider for postProcessUriArgumentsForRequestHashWorks
271 */
272 public function argumentsForPostProcessUriArgumentsForRequestHash()
273 {
274 return array(
275 // simple values
276 array(
277 array(
278 'bla' => 'X',
279 'blubb' => 'Y'
280 ),
281 array(
282 'bla',
283 'blubb'
284 )
285 ),
286 // Arrays
287 array(
288 array(
289 'bla' => array(
290 'test1' => 'X',
291 'test2' => 'Y'
292 ),
293 'blubb' => 'Y'
294 ),
295 array(
296 'bla[test1]',
297 'bla[test2]',
298 'blubb'
299 )
300 )
301 );
302 }
303
304 /**
305 * @test
306 * @dataProvider argumentsForPostProcessUriArgumentsForRequestHash
307 */
308 public function postProcessUriArgumentsForRequestHashWorks($arguments, $expectedResults)
309 {
310 $viewHelper = $this->getAccessibleMock(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, array('dummy'), array(), '', false);
311 $results = array();
312 $viewHelper->_callRef('postProcessUriArgumentsForRequestHash', $arguments, $results);
313 $this->assertEquals($expectedResults, $results);
314 }
315 }