4c88f47000737141a875d75e984890a3cd09b104
[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\Components\TestingFramework\Core\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 addDataAddsUserPermissionsOnPageIfTableIsPagesAndUserHasPagePermissions()
157 {
158 $input = [
159 'tableName' => 'pages',
160 'command' => 'edit',
161 'vanillaUid' => 123,
162 'databaseRow' => [
163 'uid' => 123,
164 'pid' => 321
165 ],
166 ];
167 $this->beUserProphecy->isAdmin()->willReturn(false);
168 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
169 $this->beUserProphecy->calcPerms($input['databaseRow'])->willReturn(Permission::PAGE_EDIT);
170 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::cetera())->willReturn(true);
171
172 $result = $this->subject->addData($input);
173
174 $this->assertSame(Permission::PAGE_EDIT, $result['userPermissionOnPage']);
175 }
176
177 /**
178 * @test
179 */
180 public function addDataSetsPermissionsToAllIfRootLevelRestrictionForTableIsIgnoredForContentEditRecord()
181 {
182 $input = [
183 'tableName' => 'tt_content',
184 'command' => 'edit',
185 'vanillaUid' => 123,
186 'databaseRow' => [
187 'uid' => 123,
188 'pid' => 0,
189 ],
190 ];
191 $this->beUserProphecy->isAdmin()->willReturn(false);
192 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
193 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::cetera())->willReturn(true);
194 $GLOBALS['TCA'][$input['tableName']]['ctrl']['security']['ignoreRootLevelRestriction'] = true;
195
196 $result = $this->subject->addData($input);
197
198 $this->assertSame(Permission::ALL, $result['userPermissionOnPage']);
199 }
200
201 /**
202 * @test
203 */
204 public function addDataThrowsExceptionIfRootNodeShouldBeEditedWithoutPermissions()
205 {
206 $input = [
207 'tableName' => 'tt_content',
208 'command' => 'edit',
209 'vanillaUid' => 123,
210 'databaseRow' => [
211 'uid' => 123,
212 'pid' => 0,
213 ],
214 ];
215 $this->beUserProphecy->isAdmin()->willReturn(false);
216 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
217 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::cetera())->willReturn(true);
218
219 $this->expectException(AccessDeniedRootNodeException::class);
220 $this->expectExceptionCode(1437679856);
221
222 $this->subject->addData($input);
223 }
224
225 /**
226 * @test
227 */
228 public function addDataThrowsExceptionIfRecordEditAccessInternalsReturnsFalse()
229 {
230 $input = [
231 'tableName' => 'tt_content',
232 'command' => 'edit',
233 'vanillaUid' => 123,
234 'parentPageRow' => [
235 'uid' => 123,
236 'pid' => 321,
237 ],
238 ];
239 $this->beUserProphecy->isAdmin()->willReturn(false);
240 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
241 $this->beUserProphecy->calcPerms($input['parentPageRow'])->willReturn(Permission::ALL);
242 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::cetera())->willReturn(false);
243
244 $this->expectException(AccessDeniedEditInternalsException::class);
245 $this->expectExceptionCode(1437687404);
246
247 $this->subject->addData($input);
248 }
249
250 /**
251 * @test
252 */
253 public function addDataThrowsExceptionForNewContentRecordWithoutPermissions()
254 {
255 $input = [
256 'tableName' => 'tt_content',
257 'command' => 'new',
258 'vanillaUid' => 123,
259 'parentPageRow' => [
260 'uid' => 123,
261 'pid' => 321,
262 ],
263 ];
264 $this->beUserProphecy->isAdmin()->willReturn(false);
265 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
266 $this->beUserProphecy->calcPerms($input['parentPageRow'])->willReturn(Permission::NOTHING);
267
268 $this->expectException(AccessDeniedContentEditException::class);
269 $this->expectExceptionCode(1437745759);
270
271 $this->subject->addData($input);
272 }
273
274 /**
275 * @test
276 */
277 public function addDataThrowsExceptionForNewPageWithoutPermissions()
278 {
279 $input = [
280 'tableName' => 'pages',
281 'command' => 'new',
282 'vanillaUid' => 123,
283 'parentPageRow' => [
284 'uid' => 123,
285 'pid' => 321,
286 ],
287 ];
288 $this->beUserProphecy->isAdmin()->willReturn(false);
289 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
290 $this->beUserProphecy->calcPerms($input['parentPageRow'])->willReturn(Permission::NOTHING);
291
292 $this->expectException(AccessDeniedPageNewException::class);
293 $this->expectExceptionCode(1437745640);
294
295 $this->subject->addData($input);
296 }
297
298 /**
299 * @test
300 */
301 public function addDataThrowsExceptionIfHookDeniesAccess()
302 {
303 $input = [
304 'tableName' => 'tt_content',
305 'command' => 'edit',
306 'vanillaUid' => 123,
307 'parentPageRow' => [
308 'uid' => 123,
309 'pid' => 321,
310 ],
311 ];
312 $this->beUserProphecy->isAdmin()->willReturn(false);
313 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
314 $this->beUserProphecy->calcPerms($input['parentPageRow'])->willReturn(Permission::ALL);
315 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::cetera())->willReturn(true);
316
317 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/alt_doc.php']['makeEditForm_accessCheck'] = [
318 'unitTest' => function () {
319 return false;
320 }
321 ];
322
323 $this->expectException(AccessDeniedHookException::class);
324 $this->expectExceptionCode(1437689705);
325
326 $this->subject->addData($input);
327 }
328
329 /**
330 * @test
331 */
332 public function addDataSetsUserPermissionsOnPageForNewPageIfPageNewIsDeniedAndHookAllowsAccess()
333 {
334 $input = [
335 'tableName' => 'pages',
336 'command' => 'new',
337 'vanillaUid' => 123,
338 'parentPageRow' => [
339 'uid' => 123,
340 'pid' => 321,
341 ],
342 ];
343 $this->beUserProphecy->isAdmin()->willReturn(false);
344 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
345 $this->beUserProphecy->calcPerms($input['parentPageRow'])->willReturn(Permission::CONTENT_EDIT);
346 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::cetera())->willReturn(true);
347
348 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/alt_doc.php']['makeEditForm_accessCheck'] = [
349 'unitTest' => function () {
350 return true;
351 }
352 ];
353
354 $result = $this->subject->addData($input);
355
356 $this->assertSame(Permission::CONTENT_EDIT, $result['userPermissionOnPage']);
357 }
358
359 /**
360 * @test
361 */
362 public function addDataSetsUserPermissionsOnPageForNewPage()
363 {
364 $input = [
365 'tableName' => 'pages',
366 'command' => 'new',
367 'vanillaUid' => 123,
368 'parentPageRow' => [
369 'uid' => 123,
370 'pid' => 321,
371 ],
372 ];
373 $this->beUserProphecy->isAdmin()->willReturn(false);
374 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
375 $this->beUserProphecy->calcPerms($input['parentPageRow'])->willReturn(Permission::PAGE_NEW);
376 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::cetera())->willReturn(true);
377
378 $result = $this->subject->addData($input);
379
380 $this->assertSame(Permission::PAGE_NEW, $result['userPermissionOnPage']);
381 }
382
383 /**
384 * @test
385 */
386 public function addDataSetsUserPermissionsOnPageForNewContentRecord()
387 {
388 $input = [
389 'tableName' => 'tt_content',
390 'command' => 'new',
391 'vanillaUid' => 123,
392 'parentPageRow' => [
393 'uid' => 123,
394 'pid' => 321,
395 ],
396 ];
397 $this->beUserProphecy->isAdmin()->willReturn(false);
398 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
399 $this->beUserProphecy->calcPerms($input['parentPageRow'])->willReturn(Permission::CONTENT_EDIT);
400 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::cetera())->willReturn(true);
401
402 $result = $this->subject->addData($input);
403
404 $this->assertSame(Permission::CONTENT_EDIT, $result['userPermissionOnPage']);
405 }
406
407 /**
408 * @test
409 */
410 public function addDataSetsPermissionsToAllIfRootLevelRestrictionForTableIsIgnoredForNewContentRecord()
411 {
412 $input = [
413 'tableName' => 'pages',
414 'command' => 'new',
415 'vanillaUid' => 123,
416 'parentPageRow' => null,
417 ];
418 $this->beUserProphecy->isAdmin()->willReturn(false);
419 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
420 $this->beUserProphecy->recordEditAccessInternals($input['tableName'], Argument::cetera())->willReturn(true);
421 $GLOBALS['TCA'][$input['tableName']]['ctrl']['security']['ignoreRootLevelRestriction'] = true;
422
423 $result = $this->subject->addData($input);
424
425 $this->assertSame(Permission::ALL, $result['userPermissionOnPage']);
426 }
427
428 /**
429 * @test
430 */
431 public function addDataThrowsExceptionForNewRecordsOnRootLevelWithoutPermissions()
432 {
433 $input = [
434 'tableName' => 'pages',
435 'command' => 'new',
436 'vanillaUid' => 123,
437 'parentPageRow' => null,
438 ];
439
440 $this->beUserProphecy->isAdmin()->willReturn(false);
441 $this->beUserProphecy->check('tables_modify', $input['tableName'])->willReturn(true);
442
443 $this->expectException(AccessDeniedRootNodeException::class);
444 $this->expectExceptionCode(1437745221);
445
446 $this->subject->addData($input);
447 }
448 }