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