[BUGFIX] Use TCA to get `type` field instead of hardcoded fieldname
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Tests / Unit / Form / FormDataProvider / DatabaseUserPermissionCheckTest.php
1 <?php
2 namespace TYPO3\CMS\Backend\Tests\Unit\Form\FormDataProvider;
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 Prophecy\Argument;
18 use Prophecy\Prophecy\ObjectProphecy;
19 use TYPO3\CMS\Backend\Form\Exception\AccessDeniedContentEditException;
20 use TYPO3\CMS\Backend\Form\Exception\AccessDeniedEditInternalsException;
21 use TYPO3\CMS\Backend\Form\Exception\AccessDeniedHookException;
22 use TYPO3\CMS\Backend\Form\Exception\AccessDeniedPageEditException;
23 use TYPO3\CMS\Backend\Form\Exception\AccessDeniedPageNewException;
24 use TYPO3\CMS\Backend\Form\Exception\AccessDeniedRootNodeException;
25 use TYPO3\CMS\Backend\Form\Exception\AccessDeniedTableModifyException;
26 use TYPO3\CMS\Backend\Form\FormDataProvider\DatabaseUserPermissionCheck;
27 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
28 use TYPO3\CMS\Core\Type\Bitmask\Permission;
29
30 /**
31 * Test case
32 */
33 class DatabaseUserPermissionCheckTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
34 {
35 /**
36 * @var DatabaseUserPermissionCheck
37 */
38 protected $subject;
39
40 /**
41 * @var BackendUserAuthentication | ObjectProphecy
42 */
43 protected $beUserProphecy;
44
45 protected function setUp()
46 {
47 $this->subject = new DatabaseUserPermissionCheck();
48
49 $this->beUserProphecy = $this->prophesize(BackendUserAuthentication::class);
50 $GLOBALS['BE_USER'] = $this->beUserProphecy->reveal();
51 $GLOBALS['BE_USER']->user['uid'] = 42;
52 }
53
54 /**
55 * @test
56 */
57 public function addDataSetsUserPermissionsOnPageForAdminUser()
58 {
59 $this->beUserProphecy->isAdmin()->willReturn(true);
60
61 $result = $this->subject->addData([]);
62
63 $this->assertSame(Permission::ALL, $result['userPermissionOnPage']);
64 }
65
66 /**
67 * @test
68 */
69 public function addDataThrowsExceptionIfUserHasNoTablesModifyPermissionForGivenTable()
70 {
71 $input = [
72 'tableName' => 'tt_content',
73 ];
74 $this->beUserProphecy->isAdmin()->willReturn(false);
75 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(false);
76
77 $this->expectException(AccessDeniedTableModifyException::class);
78 $this->expectExceptionCode(1437683248);
79
80 $this->subject->addData($input);
81 }
82
83 /**
84 * @test
85 */
86 public function addDataThrowsExceptionIfUserHasNoContentEditPermissionsOnPage()
87 {
88 $input = [
89 'tableName' => 'tt_content',
90 'command' => 'edit',
91 'vanillaUid' => 123,
92 'parentPageRow' => [
93 'pid' => 321,
94 ],
95 ];
96 $this->beUserProphecy->isAdmin()->willReturn(false);
97 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
98 $this->beUserProphecy->calcPerms(['pid' => 321])->willReturn(Permission::NOTHING);
99
100 $this->expectException(AccessDeniedContentEditException::class);
101 $this->expectExceptionCode(1437679657);
102
103 $this->subject->addData($input);
104 }
105
106 /**
107 * @test
108 */
109 public function addDataAddsUserPermissionsOnPageForContentIfUserHasCorrespondingPermissions()
110 {
111 $input = [
112 'tableName' => 'tt_content',
113 'command' => 'edit',
114 'vanillaUid' => 123,
115 'parentPageRow' => [
116 'pid' => 321,
117 ],
118 ];
119 $this->beUserProphecy->isAdmin()->willReturn(false);
120 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
121 $this->beUserProphecy->calcPerms(['pid' => 321])->willReturn(Permission::CONTENT_EDIT);
122 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::any())->willReturn(true);
123
124 $result = $this->subject->addData($input);
125
126 $this->assertSame(Permission::CONTENT_EDIT, $result['userPermissionOnPage']);
127 }
128
129 /**
130 * @test
131 */
132 public function addDataThrowsExceptionIfCommandIsEditTableIsPagesAndUserHasNoPagePermissions()
133 {
134 $input = [
135 'tableName' => 'pages',
136 'command' => 'edit',
137 'vanillaUid' => 123,
138 'databaseRow' => [
139 'uid' => 123,
140 'pid' => 321
141 ],
142 ];
143 $this->beUserProphecy->isAdmin()->willReturn(false);
144 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
145 $this->beUserProphecy->calcPerms($input['databaseRow'])->willReturn(Permission::NOTHING);
146
147 $this->expectException(AccessDeniedPageEditException::class);
148 $this->expectExceptionCode(1437679336);
149
150 $this->subject->addData($input);
151 }
152
153 /**
154 * @test
155 */
156 public function addDataThrowsExceptionIfCommandIsEditTableIsPagesAndUserHasNoDoktypePermissions()
157 {
158 $input = [
159 'tableName' => 'pages',
160 'command' => 'edit',
161 'vanillaUid' => 123,
162 'databaseRow' => [
163 'uid' => 123,
164 'pid' => 321,
165 'doktype' => 1,
166 ],
167 'processedTca' => [
168 'ctrl' => [
169 'type' => 'doktype'
170 ]
171 ]
172 ];
173 $this->beUserProphecy->isAdmin()->willReturn(false);
174 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
175 $this->beUserProphecy->check('pagetypes_select', $input['databaseRow']['doktype'])->willReturn(false);
176 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::cetera())->willReturn(true);
177 $this->beUserProphecy->calcPerms($input['databaseRow'])->willReturn(Permission::ALL);
178
179 $this->expectException(AccessDeniedPageEditException::class);
180 $this->expectExceptionCode(1437679336);
181
182 $this->subject->addData($input);
183 }
184
185 /**
186 * @test
187 */
188 public function addDataAddsUserPermissionsOnPageIfTableIsPagesAndUserHasPagePermissions()
189 {
190 $input = [
191 'tableName' => 'pages',
192 'command' => 'edit',
193 'vanillaUid' => 123,
194 'databaseRow' => [
195 'uid' => 123,
196 'pid' => 321,
197 'doktype' => 1,
198 ],
199 'processedTca' => [
200 'ctrl' => [
201 'type' => 'doktype'
202 ]
203 ]
204 ];
205 $this->beUserProphecy->isAdmin()->willReturn(false);
206 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
207 $this->beUserProphecy->check('pagetypes_select', $input['databaseRow']['doktype'])->willReturn(true);
208 $this->beUserProphecy->calcPerms($input['databaseRow'])->willReturn(Permission::PAGE_EDIT);
209 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::cetera())->willReturn(true);
210
211 $result = $this->subject->addData($input);
212
213 $this->assertSame(Permission::PAGE_EDIT, $result['userPermissionOnPage']);
214 }
215
216 /**
217 * @test
218 */
219 public function addDataSetsPermissionsToAllIfRootLevelRestrictionForTableIsIgnoredForContentEditRecord()
220 {
221 $input = [
222 'tableName' => 'tt_content',
223 'command' => 'edit',
224 'vanillaUid' => 123,
225 'databaseRow' => [
226 'uid' => 123,
227 'pid' => 0,
228 ],
229 ];
230 $this->beUserProphecy->isAdmin()->willReturn(false);
231 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
232 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::cetera())->willReturn(true);
233 $GLOBALS['TCA'][$input['tableName']]['ctrl']['security']['ignoreRootLevelRestriction'] = true;
234
235 $result = $this->subject->addData($input);
236
237 $this->assertSame(Permission::ALL, $result['userPermissionOnPage']);
238 }
239
240 /**
241 * @test
242 */
243 public function addDataThrowsExceptionIfRootNodeShouldBeEditedWithoutPermissions()
244 {
245 $input = [
246 'tableName' => 'tt_content',
247 'command' => 'edit',
248 'vanillaUid' => 123,
249 'databaseRow' => [
250 'uid' => 123,
251 'pid' => 0,
252 ],
253 ];
254 $this->beUserProphecy->isAdmin()->willReturn(false);
255 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
256 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::cetera())->willReturn(true);
257
258 $this->expectException(AccessDeniedRootNodeException::class);
259 $this->expectExceptionCode(1437679856);
260
261 $this->subject->addData($input);
262 }
263
264 /**
265 * @test
266 */
267 public function addDataThrowsExceptionIfRecordEditAccessInternalsReturnsFalse()
268 {
269 $input = [
270 'tableName' => 'tt_content',
271 'command' => 'edit',
272 'vanillaUid' => 123,
273 'parentPageRow' => [
274 'uid' => 123,
275 'pid' => 321,
276 ],
277 ];
278 $this->beUserProphecy->isAdmin()->willReturn(false);
279 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
280 $this->beUserProphecy->calcPerms($input['parentPageRow'])->willReturn(Permission::ALL);
281 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::cetera())->willReturn(false);
282
283 $this->expectException(AccessDeniedEditInternalsException::class);
284 $this->expectExceptionCode(1437687404);
285
286 $this->subject->addData($input);
287 }
288
289 /**
290 * @test
291 */
292 public function addDataThrowsExceptionForNewContentRecordWithoutPermissions()
293 {
294 $input = [
295 'tableName' => 'tt_content',
296 'command' => 'new',
297 'vanillaUid' => 123,
298 'parentPageRow' => [
299 'uid' => 123,
300 'pid' => 321,
301 ],
302 ];
303 $this->beUserProphecy->isAdmin()->willReturn(false);
304 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
305 $this->beUserProphecy->calcPerms($input['parentPageRow'])->willReturn(Permission::NOTHING);
306
307 $this->expectException(AccessDeniedContentEditException::class);
308 $this->expectExceptionCode(1437745759);
309
310 $this->subject->addData($input);
311 }
312
313 /**
314 * @test
315 */
316 public function addDataThrowsExceptionForNewPageWithoutPermissions()
317 {
318 $input = [
319 'tableName' => 'pages',
320 'command' => 'new',
321 'vanillaUid' => 123,
322 'parentPageRow' => [
323 'uid' => 123,
324 'pid' => 321,
325 ],
326 ];
327 $this->beUserProphecy->isAdmin()->willReturn(false);
328 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
329 $this->beUserProphecy->calcPerms($input['parentPageRow'])->willReturn(Permission::NOTHING);
330
331 $this->expectException(AccessDeniedPageNewException::class);
332 $this->expectExceptionCode(1437745640);
333
334 $this->subject->addData($input);
335 }
336
337 /**
338 * @test
339 */
340 public function addDataThrowsExceptionIfHookDeniesAccess()
341 {
342 $input = [
343 'tableName' => 'tt_content',
344 'command' => 'edit',
345 'vanillaUid' => 123,
346 'parentPageRow' => [
347 'uid' => 123,
348 'pid' => 321,
349 ],
350 ];
351 $this->beUserProphecy->isAdmin()->willReturn(false);
352 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
353 $this->beUserProphecy->calcPerms($input['parentPageRow'])->willReturn(Permission::ALL);
354 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::cetera())->willReturn(true);
355
356 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/alt_doc.php']['makeEditForm_accessCheck'] = [
357 'unitTest' => function () {
358 return false;
359 }
360 ];
361
362 $this->expectException(AccessDeniedHookException::class);
363 $this->expectExceptionCode(1437689705);
364
365 $this->subject->addData($input);
366 }
367
368 /**
369 * @test
370 */
371 public function addDataSetsUserPermissionsOnPageForNewPageIfPageNewIsDeniedAndHookAllowsAccess()
372 {
373 $input = [
374 'tableName' => 'pages',
375 'command' => 'new',
376 'vanillaUid' => 123,
377 'parentPageRow' => [
378 'uid' => 123,
379 'pid' => 321,
380 ],
381 ];
382 $this->beUserProphecy->isAdmin()->willReturn(false);
383 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
384 $this->beUserProphecy->calcPerms($input['parentPageRow'])->willReturn(Permission::CONTENT_EDIT);
385 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::cetera())->willReturn(true);
386
387 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/alt_doc.php']['makeEditForm_accessCheck'] = [
388 'unitTest' => function () {
389 return true;
390 }
391 ];
392
393 $result = $this->subject->addData($input);
394
395 $this->assertSame(Permission::CONTENT_EDIT, $result['userPermissionOnPage']);
396 }
397
398 /**
399 * @test
400 */
401 public function addDataSetsUserPermissionsOnPageForNewPage()
402 {
403 $input = [
404 'tableName' => 'pages',
405 'command' => 'new',
406 'vanillaUid' => 123,
407 'parentPageRow' => [
408 'uid' => 123,
409 'pid' => 321,
410 ],
411 ];
412 $this->beUserProphecy->isAdmin()->willReturn(false);
413 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
414 $this->beUserProphecy->calcPerms($input['parentPageRow'])->willReturn(Permission::PAGE_NEW);
415 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::cetera())->willReturn(true);
416
417 $result = $this->subject->addData($input);
418
419 $this->assertSame(Permission::PAGE_NEW, $result['userPermissionOnPage']);
420 }
421
422 /**
423 * @test
424 */
425 public function addDataSetsUserPermissionsOnPageForNewContentRecord()
426 {
427 $input = [
428 'tableName' => 'tt_content',
429 'command' => 'new',
430 'vanillaUid' => 123,
431 'parentPageRow' => [
432 'uid' => 123,
433 'pid' => 321,
434 ],
435 ];
436 $this->beUserProphecy->isAdmin()->willReturn(false);
437 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
438 $this->beUserProphecy->calcPerms($input['parentPageRow'])->willReturn(Permission::CONTENT_EDIT);
439 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::cetera())->willReturn(true);
440
441 $result = $this->subject->addData($input);
442
443 $this->assertSame(Permission::CONTENT_EDIT, $result['userPermissionOnPage']);
444 }
445
446 /**
447 * @test
448 */
449 public function addDataSetsPermissionsToAllIfRootLevelRestrictionForTableIsIgnoredForNewContentRecord()
450 {
451 $input = [
452 'tableName' => 'pages',
453 'command' => 'new',
454 'vanillaUid' => 123,
455 'parentPageRow' => null,
456 ];
457 $this->beUserProphecy->isAdmin()->willReturn(false);
458 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
459 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::cetera())->willReturn(true);
460 $GLOBALS['TCA'][$input['tableName']]['ctrl']['security']['ignoreRootLevelRestriction'] = true;
461
462 $result = $this->subject->addData($input);
463
464 $this->assertSame(Permission::ALL, $result['userPermissionOnPage']);
465 }
466
467 /**
468 * @test
469 */
470 public function addDataThrowsExceptionForNewRecordsOnRootLevelWithoutPermissions()
471 {
472 $input = [
473 'tableName' => 'pages',
474 'command' => 'new',
475 'vanillaUid' => 123,
476 'parentPageRow' => null,
477 ];
478
479 $this->beUserProphecy->isAdmin()->willReturn(false);
480 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
481
482 $this->expectException(AccessDeniedRootNodeException::class);
483 $this->expectExceptionCode(1437745221);
484
485 $this->subject->addData($input);
486 }
487 }