[TASK] Protect user TSconfig properties in BackendUserAuthentication
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Authentication / BackendUserAuthenticationTest.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Core\Tests\Unit\Authentication;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use Prophecy\Argument;
19 use Prophecy\Prophecy\ObjectProphecy;
20 use Psr\Log\NullLogger;
21 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
22 use TYPO3\CMS\Core\Database\Connection;
23 use TYPO3\CMS\Core\Database\ConnectionPool;
24 use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
25 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
26 use TYPO3\CMS\Core\FormProtection\BackendFormProtection;
27 use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
28 use TYPO3\CMS\Core\Resource\ResourceStorage;
29 use TYPO3\CMS\Core\Tests\Unit\Database\Mocks\MockPlatform;
30 use TYPO3\CMS\Core\Type\Bitmask\JsConfirmation;
31 use TYPO3\CMS\Core\Utility\GeneralUtility;
32 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
33
34 /**
35 * Test case
36 */
37 class BackendUserAuthenticationTest extends UnitTestCase
38 {
39 /**
40 * @var array
41 */
42 protected $defaultFilePermissions = [
43 // File permissions
44 'addFile' => false,
45 'readFile' => false,
46 'writeFile' => false,
47 'copyFile' => false,
48 'moveFile' => false,
49 'renameFile' => false,
50 'deleteFile' => false,
51 // Folder permissions
52 'addFolder' => false,
53 'readFolder' => false,
54 'writeFolder' => false,
55 'copyFolder' => false,
56 'moveFolder' => false,
57 'renameFolder' => false,
58 'deleteFolder' => false,
59 'recursivedeleteFolder' => false
60 ];
61
62 /**
63 * Tear down
64 */
65 protected function tearDown(): void
66 {
67 FormProtectionFactory::purgeInstances();
68 parent::tearDown();
69 }
70
71 /////////////////////////////////////////
72 // Tests concerning the form protection
73 /////////////////////////////////////////
74 /**
75 * @test
76 */
77 public function logoffCleansFormProtectionIfBackendUserIsLoggedIn(): void
78 {
79 /** @var ObjectProphecy|Connection $connection */
80 $connection = $this->prophesize(Connection::class);
81 $connection->delete('sys_lockedrecords', Argument::cetera())->willReturn(1);
82
83 /** @var ObjectProphecy|ConnectionPool $connectionPool */
84 $connectionPool = $this->prophesize(ConnectionPool::class);
85 $connectionPool->getConnectionForTable(Argument::cetera())->willReturn($connection->reveal());
86
87 GeneralUtility::addInstance(ConnectionPool::class, $connectionPool->reveal());
88
89 /** @var ObjectProphecy|\TYPO3\CMS\Core\FormProtection\AbstractFormProtection $formProtection */
90 $formProtection = $this->prophesize(BackendFormProtection::class);
91 $formProtection->clean()->shouldBeCalled();
92
93 FormProtectionFactory::set(
94 'default',
95 $formProtection->reveal()
96 );
97
98 $GLOBALS['BE_USER'] = $this->getMockBuilder(BackendUserAuthentication::class)->getMock();
99 $GLOBALS['BE_USER']->user = ['uid' => $this->getUniqueId()];
100 $GLOBALS['BE_USER']->setLogger(new NullLogger());
101
102 /** @var BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject $subject */
103 $subject = $this->getMockBuilder(BackendUserAuthentication::class)
104 ->setMethods(['dummy'])
105 ->disableOriginalConstructor()
106 ->getMock();
107
108 $subject->setLogger(new NullLogger());
109 $subject->logoff();
110 }
111
112 /**
113 * @return array
114 */
115 public function getTSConfigDataProvider(): array
116 {
117 $completeConfiguration = [
118 'value' => 'oneValue',
119 'value.' => ['oneProperty' => 'oneValue'],
120 'permissions.' => [
121 'file.' => [
122 'default.' => ['readAction' => '1'],
123 '1.' => ['writeAction' => '1'],
124 '0.' => ['readAction' => '0'],
125 ],
126 ]
127 ];
128
129 return [
130 'single level string' => [
131 $completeConfiguration,
132 'permissions',
133 [
134 'value' => null,
135 'properties' =>
136 [
137 'file.' => [
138 'default.' => ['readAction' => '1'],
139 '1.' => ['writeAction' => '1'],
140 '0.' => ['readAction' => '0'],
141 ],
142 ],
143 ],
144 ],
145 'two levels string' => [
146 $completeConfiguration,
147 'permissions.file',
148 [
149 'value' => null,
150 'properties' =>
151 [
152 'default.' => ['readAction' => '1'],
153 '1.' => ['writeAction' => '1'],
154 '0.' => ['readAction' => '0'],
155 ],
156 ],
157 ],
158 'three levels string' => [
159 $completeConfiguration,
160 'permissions.file.default',
161 [
162 'value' => null,
163 'properties' =>
164 ['readAction' => '1'],
165 ],
166 ],
167 'three levels string with integer property' => [
168 $completeConfiguration,
169 'permissions.file.1',
170 [
171 'value' => null,
172 'properties' => ['writeAction' => '1'],
173 ],
174 ],
175 'three levels string with integer zero property' => [
176 $completeConfiguration,
177 'permissions.file.0',
178 [
179 'value' => null,
180 'properties' => ['readAction' => '0'],
181 ],
182 ],
183 'four levels string with integer zero property, value, no properties' => [
184 $completeConfiguration,
185 'permissions.file.0.readAction',
186 [
187 'value' => '0',
188 'properties' => null,
189 ],
190 ],
191 'four levels string with integer property, value, no properties' => [
192 $completeConfiguration,
193 'permissions.file.1.writeAction',
194 [
195 'value' => '1',
196 'properties' => null,
197 ],
198 ],
199 'one level, not existent string' => [
200 $completeConfiguration,
201 'foo',
202 [
203 'value' => null,
204 'properties' => null,
205 ],
206 ],
207 'two level, not existent string' => [
208 $completeConfiguration,
209 'foo.bar',
210 [
211 'value' => null,
212 'properties' => null,
213 ],
214 ],
215 'two level, where second level does not exist' => [
216 $completeConfiguration,
217 'permissions.bar',
218 [
219 'value' => null,
220 'properties' => null,
221 ],
222 ],
223 'three level, where third level does not exist' => [
224 $completeConfiguration,
225 'permissions.file.foo',
226 [
227 'value' => null,
228 'properties' => null,
229 ],
230 ],
231 'three level, where second and third level does not exist' => [
232 $completeConfiguration,
233 'permissions.foo.bar',
234 [
235 'value' => null,
236 'properties' => null,
237 ],
238 ],
239 'value and properties' => [
240 $completeConfiguration,
241 'value',
242 [
243 'value' => 'oneValue',
244 'properties' => ['oneProperty' => 'oneValue'],
245 ],
246 ],
247 ];
248 }
249
250 /**
251 * @param array $completeConfiguration
252 * @param string $objectString
253 * @param array $expectedConfiguration
254 * @dataProvider getTSConfigDataProvider
255 * @test
256 */
257 public function getTSConfigReturnsCorrectArrayForGivenObjectString(array $completeConfiguration, $objectString, array $expectedConfiguration): void
258 {
259 /** @var BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject $subject */
260 $subject = $this->getAccessibleMock(BackendUserAuthentication::class, ['dummy'], [], '', false);
261 $subject->setLogger(new NullLogger());
262 $subject->_set('userTS', $completeConfiguration);
263
264 $actualConfiguration = $subject->getTSConfig($objectString);
265 $this->assertSame($expectedConfiguration, $actualConfiguration);
266 }
267
268 /**
269 * @return array
270 */
271 public function getFilePermissionsTakesUserDefaultAndStoragePermissionsIntoAccountIfUserIsNotAdminDataProvider(): array
272 {
273 return [
274 'Only read permissions' => [
275 [
276 'addFile' => 0,
277 'readFile' => 1,
278 'writeFile' => 0,
279 'copyFile' => 0,
280 'moveFile' => 0,
281 'renameFile' => 0,
282 'deleteFile' => 0,
283 'addFolder' => 0,
284 'readFolder' => 1,
285 'copyFolder' => 0,
286 'moveFolder' => 0,
287 'renameFolder' => 0,
288 'writeFolder' => 0,
289 'deleteFolder' => 0,
290 'recursivedeleteFolder' => 0,
291 ]
292 ],
293 'Uploading allowed' => [
294 [
295 'addFile' => 1,
296 'readFile' => 1,
297 'writeFile' => 1,
298 'copyFile' => 1,
299 'moveFile' => 1,
300 'renameFile' => 1,
301 'deleteFile' => 1,
302 'addFolder' => 0,
303 'readFolder' => 1,
304 'copyFolder' => 0,
305 'moveFolder' => 0,
306 'renameFolder' => 0,
307 'writeFolder' => 0,
308 'deleteFolder' => 0,
309 'recursivedeleteFolder' => 0
310 ]
311 ],
312 'One value is enough' => [
313 [
314 'addFile' => 1,
315 ]
316 ],
317 ];
318 }
319
320 /**
321 * @param array $userTsConfiguration
322 * @test
323 * @dataProvider getFilePermissionsTakesUserDefaultAndStoragePermissionsIntoAccountIfUserIsNotAdminDataProvider
324 */
325 public function getFilePermissionsTakesUserDefaultPermissionsFromTsConfigIntoAccountIfUserIsNotAdmin(array $userTsConfiguration): void
326 {
327 /** @var BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject $subject */
328 $subject = $this->getMockBuilder(BackendUserAuthentication::class)
329 ->setMethods(['isAdmin', 'getTSConfig'])
330 ->getMock();
331
332 $subject
333 ->expects($this->any())
334 ->method('isAdmin')
335 ->will($this->returnValue(false));
336
337 $subject->setLogger(new NullLogger());
338 $subject
339 ->expects($this->any())
340 ->method('getTSConfig')
341 ->will($this->returnValue([
342 'permissions.' => [
343 'file.' => [
344 'default.' => $userTsConfiguration
345 ],
346 ]
347 ]));
348
349 $expectedPermissions = array_merge($this->defaultFilePermissions, $userTsConfiguration);
350 array_walk(
351 $expectedPermissions,
352 function (&$value) {
353 $value = (bool)$value;
354 }
355 );
356
357 $this->assertEquals($expectedPermissions, $subject->getFilePermissions());
358 }
359
360 /**
361 * @return array
362 */
363 public function getFilePermissionsFromStorageDataProvider(): array
364 {
365 $defaultPermissions = [
366 'addFile' => true,
367 'readFile' => true,
368 'writeFile' => true,
369 'copyFile' => true,
370 'moveFile' => true,
371 'renameFile' => true,
372 'deleteFile' => true,
373 'addFolder' => true,
374 'readFolder' => true,
375 'copyFolder' => true,
376 'moveFolder' => true,
377 'renameFolder' => true,
378 'writeFolder' => true,
379 'deleteFolder' => true,
380 'recursivedeleteFolder' => true
381 ];
382
383 return [
384 'Overwrites given storage permissions with default permissions' => [
385 $defaultPermissions,
386 1,
387 [
388 'addFile' => 0,
389 'recursivedeleteFolder' =>0
390 ],
391 [
392 'addFile' => 0,
393 'readFile' => 1,
394 'writeFile' => 1,
395 'copyFile' => 1,
396 'moveFile' => 1,
397 'renameFile' => 1,
398 'deleteFile' => 1,
399 'addFolder' => 1,
400 'readFolder' => 1,
401 'copyFolder' => 1,
402 'moveFolder' => 1,
403 'renameFolder' => 1,
404 'writeFolder' => 1,
405 'deleteFolder' => 1,
406 'recursivedeleteFolder' => 0
407 ]
408 ],
409 'Overwrites given storage 0 permissions with default permissions' => [
410 $defaultPermissions,
411 0,
412 [
413 'addFile' => 0,
414 'recursivedeleteFolder' =>0
415 ],
416 [
417 'addFile' => false,
418 'readFile' => true,
419 'writeFile' => true,
420 'copyFile' => true,
421 'moveFile' => true,
422 'renameFile' => true,
423 'deleteFile' => true,
424 'addFolder' => true,
425 'readFolder' => true,
426 'copyFolder' => true,
427 'moveFolder' => true,
428 'renameFolder' => true,
429 'writeFolder' => true,
430 'deleteFolder' => true,
431 'recursivedeleteFolder' => false
432 ]
433 ],
434 'Returns default permissions if no storage permissions are found' => [
435 $defaultPermissions,
436 1,
437 [],
438 [
439 'addFile' => true,
440 'readFile' => true,
441 'writeFile' => true,
442 'copyFile' => true,
443 'moveFile' => true,
444 'renameFile' => true,
445 'deleteFile' => true,
446 'addFolder' => true,
447 'readFolder' => true,
448 'copyFolder' => true,
449 'moveFolder' => true,
450 'renameFolder' => true,
451 'writeFolder' => true,
452 'deleteFolder' => true,
453 'recursivedeleteFolder' => true
454 ]
455 ],
456 ];
457 }
458
459 /**
460 * @param array $defaultPermissions
461 * @param int $storageUid
462 * @param array $storagePermissions
463 * @param array $expectedPermissions
464 * @test
465 * @dataProvider getFilePermissionsFromStorageDataProvider
466 */
467 public function getFilePermissionsFromStorageOverwritesDefaultPermissions(array $defaultPermissions, $storageUid, array $storagePermissions, array $expectedPermissions): void
468 {
469 /** @var BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject $subject */
470 $subject = $this->getMockBuilder(BackendUserAuthentication::class)
471 ->setMethods(['isAdmin', 'getFilePermissions', 'getTSConfig'])
472 ->getMock();
473 $storageMock = $this->createMock(ResourceStorage::class);
474 $storageMock->expects($this->any())->method('getUid')->will($this->returnValue($storageUid));
475
476 $subject
477 ->expects($this->any())
478 ->method('isAdmin')
479 ->will($this->returnValue(false));
480
481 $subject
482 ->expects($this->any())
483 ->method('getFilePermissions')
484 ->will($this->returnValue($defaultPermissions));
485
486 $subject
487 ->expects($this->any())
488 ->method('getTSConfig')
489 ->will($this->returnValue([
490 'permissions.' => [
491 'file.' => [
492 'storage.' => [
493 $storageUid . '.' => $storagePermissions
494 ],
495 ],
496 ]
497 ]));
498
499 $this->assertEquals($expectedPermissions, $subject->getFilePermissionsForStorage($storageMock));
500 }
501
502 /**
503 * @param array $defaultPermissions
504 * @param $storageUid
505 * @param array $storagePermissions
506 * @test
507 * @dataProvider getFilePermissionsFromStorageDataProvider
508 */
509 public function getFilePermissionsFromStorageAlwaysReturnsDefaultPermissionsForAdmins(array $defaultPermissions, $storageUid, array $storagePermissions): void
510 {
511 /** @var BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject $subject */
512 $subject = $this->getMockBuilder(BackendUserAuthentication::class)
513 ->setMethods(['isAdmin', 'getFilePermissions', 'getTSConfig'])
514 ->getMock();
515 $storageMock = $this->createMock(ResourceStorage::class);
516 $storageMock->expects($this->any())->method('getUid')->will($this->returnValue($storageUid));
517
518 $subject
519 ->expects($this->any())
520 ->method('isAdmin')
521 ->will($this->returnValue(true));
522
523 $subject
524 ->expects($this->any())
525 ->method('getFilePermissions')
526 ->will($this->returnValue($defaultPermissions));
527
528 $subject
529 ->expects($this->any())
530 ->method('getTSConfig')
531 ->will($this->returnValue([
532 'permissions.' => [
533 'file.' => [
534 'storage.' => [
535 $storageUid . '.' => $storagePermissions
536 ],
537 ],
538 ]
539 ]));
540
541 $this->assertEquals($defaultPermissions, $subject->getFilePermissionsForStorage($storageMock));
542 }
543
544 /**
545 * @return array
546 */
547 public function getFilePermissionsTakesUserDefaultPermissionsFromRecordIntoAccountIfUserIsNotAdminDataProvider(): array
548 {
549 return [
550 'No permission' => [
551 '',
552 [
553 'addFile' => false,
554 'readFile' => false,
555 'writeFile' => false,
556 'copyFile' => false,
557 'moveFile' => false,
558 'renameFile' => false,
559 'deleteFile' => false,
560 'addFolder' => false,
561 'readFolder' => false,
562 'copyFolder' => false,
563 'moveFolder' => false,
564 'renameFolder' => false,
565 'writeFolder' => false,
566 'deleteFolder' => false,
567 'recursivedeleteFolder' => false
568 ]
569 ],
570 'Standard file permissions' => [
571 'addFile,readFile,writeFile,copyFile,moveFile,renameFile,deleteFile',
572 [
573 'addFile' => true,
574 'readFile' => true,
575 'writeFile' => true,
576 'copyFile' => true,
577 'moveFile' => true,
578 'renameFile' => true,
579 'deleteFile' => true,
580 'addFolder' => false,
581 'readFolder' => false,
582 'copyFolder' => false,
583 'moveFolder' => false,
584 'renameFolder' => false,
585 'writeFolder' => false,
586 'deleteFolder' => false,
587 'recursivedeleteFolder' => false
588 ]
589 ],
590 'Standard folder permissions' => [
591 'addFolder,readFolder,moveFolder,renameFolder,writeFolder,deleteFolder',
592 [
593 'addFile' => false,
594 'readFile' => false,
595 'writeFile' => false,
596 'copyFile' => false,
597 'moveFile' => false,
598 'renameFile' => false,
599 'deleteFile' => false,
600 'addFolder' => true,
601 'readFolder' => true,
602 'writeFolder' => true,
603 'copyFolder' => false,
604 'moveFolder' => true,
605 'renameFolder' => true,
606 'deleteFolder' => true,
607 'recursivedeleteFolder' => false
608 ]
609 ],
610 'Copy folder allowed' => [
611 'readFolder,copyFolder',
612 [
613 'addFile' => false,
614 'readFile' => false,
615 'writeFile' => false,
616 'copyFile' => false,
617 'moveFile' => false,
618 'renameFile' => false,
619 'deleteFile' => false,
620 'addFolder' => false,
621 'readFolder' => true,
622 'writeFolder' => false,
623 'copyFolder' => true,
624 'moveFolder' => false,
625 'renameFolder' => false,
626 'deleteFolder' => false,
627 'recursivedeleteFolder' => false
628 ]
629 ],
630 'Copy folder and remove subfolders allowed' => [
631 'readFolder,copyFolder,recursivedeleteFolder',
632 [
633 'addFile' => false,
634 'readFile' => false,
635 'writeFile' => false,
636 'copyFile' => false,
637 'moveFile' => false,
638 'renameFile' => false,
639 'deleteFile' => false,
640 'addFolder' => false,
641 'readFolder' => true,
642 'writeFolder' => false,
643 'copyFolder' => true,
644 'moveFolder' => false,
645 'renameFolder' => false,
646 'deleteFolder' => false,
647 'recursivedeleteFolder' => true
648 ]
649 ],
650 ];
651 }
652
653 /**
654 * @test
655 *
656 * @param string $permissionValue
657 * @param array $expectedPermissions
658 *
659 * @dataProvider getFilePermissionsTakesUserDefaultPermissionsFromRecordIntoAccountIfUserIsNotAdminDataProvider
660 */
661 public function getFilePermissionsTakesUserDefaultPermissionsFromRecordIntoAccountIfUserIsNotAdmin(string $permissionValue, array $expectedPermissions): void
662 {
663 /** @var BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject $subject */
664 $subject = $this->getMockBuilder(BackendUserAuthentication::class)
665 ->setMethods(['isAdmin', 'getTSConfig'])
666 ->getMock();
667
668 $subject
669 ->expects($this->any())
670 ->method('isAdmin')
671 ->will($this->returnValue(false));
672
673 $subject
674 ->expects($this->any())
675 ->method('getTSConfig')
676 ->will($this->returnValue([]));
677 $subject->groupData['file_permissions'] = $permissionValue;
678 $this->assertEquals($expectedPermissions, $subject->getFilePermissions());
679 }
680
681 /**
682 * @test
683 */
684 public function getFilePermissionsGrantsAllPermissionsToAdminUsers(): void
685 {
686 /** @var BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject $subject */
687 $subject = $this->getMockBuilder(BackendUserAuthentication::class)
688 ->setMethods(['isAdmin'])
689 ->getMock();
690
691 $subject
692 ->expects($this->any())
693 ->method('isAdmin')
694 ->will($this->returnValue(true));
695
696 $expectedPermissions = [
697 'addFile' => true,
698 'readFile' => true,
699 'writeFile' => true,
700 'copyFile' => true,
701 'moveFile' => true,
702 'renameFile' => true,
703 'deleteFile' => true,
704 'addFolder' => true,
705 'readFolder' => true,
706 'writeFolder' => true,
707 'copyFolder' => true,
708 'moveFolder' => true,
709 'renameFolder' => true,
710 'deleteFolder' => true,
711 'recursivedeleteFolder' => true
712 ];
713
714 $this->assertEquals($expectedPermissions, $subject->getFilePermissions());
715 }
716
717 /**
718 * @test
719 */
720 public function jsConfirmationReturnsTrueIfPassedValueEqualsConfiguration(): void
721 {
722 /** @var BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject $subject */
723 $subject = $this->getMockBuilder(BackendUserAuthentication::class)
724 ->setMethods(['getTSConfig'])
725 ->getMock();
726 $subject->method('getTSConfig')->with('options.alertPopups')->willReturn(['value' => 1]);
727
728 $this->assertTrue($subject->jsConfirmation(JsConfirmation::TYPE_CHANGE));
729 $this->assertFalse($subject->jsConfirmation(JsConfirmation::COPY_MOVE_PASTE));
730 }
731
732 /**
733 * @test
734 */
735 public function jsConfirmationAllowsSettingMultipleBitsInValue(): void
736 {
737 /** @var BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject $subject */
738 $subject = $this->getMockBuilder(BackendUserAuthentication::class)
739 ->setMethods(['getTSConfig'])
740 ->getMock();
741 $subject->method('getTSConfig')->with('options.alertPopups')->willReturn(['value' => 3]);
742
743 $this->assertTrue($subject->jsConfirmation(JsConfirmation::TYPE_CHANGE));
744 $this->assertTrue($subject->jsConfirmation(JsConfirmation::COPY_MOVE_PASTE));
745 }
746
747 /**
748 * @test
749 * @dataProvider jsConfirmationsWithUnsetBits
750 *
751 * @param int $jsConfirmation
752 * @param int $typeChangeAllowed
753 * @param int $copyMovePasteAllowed
754 * @param int $deleteAllowed
755 * @param int $feEditAllowed
756 * @param int $otherAllowed
757 */
758 public function jsConfirmationAllowsUnsettingBitsInValue($jsConfirmation, $typeChangeAllowed, $copyMovePasteAllowed, $deleteAllowed, $feEditAllowed, $otherAllowed): void
759 {
760 $subject = $this->getMockBuilder(BackendUserAuthentication::class)
761 ->setMethods(['getTSConfig'])
762 ->getMock();
763 $subject->method('getTSConfig')->with('options.alertPopups')->willReturn(['value' => $jsConfirmation]);
764
765 $this->assertEquals($typeChangeAllowed, $subject->jsConfirmation(JsConfirmation::TYPE_CHANGE));
766 $this->assertEquals($copyMovePasteAllowed, $subject->jsConfirmation(JsConfirmation::COPY_MOVE_PASTE));
767 $this->assertEquals($deleteAllowed, $subject->jsConfirmation(JsConfirmation::DELETE));
768 $this->assertEquals($feEditAllowed, $subject->jsConfirmation(JsConfirmation::FE_EDIT));
769 $this->assertEquals($otherAllowed, $subject->jsConfirmation(JsConfirmation::OTHER));
770 }
771
772 /**
773 * @return array
774 */
775 public function jsConfirmationsWithUnsetBits(): array
776 {
777 return [
778 'All except "type change" and "copy/move/paste"' => [
779 252,
780 false,
781 false,
782 true,
783 true,
784 true,
785 ],
786 'All except "other"' => [
787 127,
788 true,
789 true,
790 true,
791 true,
792 false,
793 ],
794 ];
795 }
796
797 /**
798 * @test
799 */
800 public function jsConfirmationAlwaysReturnsFalseIfNoConfirmationIsSet(): void
801 {
802 /** @var BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject $subject */
803 $subject = $this->getMockBuilder(BackendUserAuthentication::class)
804 ->setMethods(['getTSConfig'])
805 ->getMock();
806 $subject->method('getTSConfig')->with('options.alertPopups')->willReturn(['value' => 0]);
807
808 $this->assertFalse($subject->jsConfirmation(JsConfirmation::TYPE_CHANGE));
809 $this->assertFalse($subject->jsConfirmation(JsConfirmation::COPY_MOVE_PASTE));
810 }
811
812 /**
813 * @test
814 */
815 public function jsConfirmationReturnsTrueIfConfigurationIsMissing(): void
816 {
817 /** @var BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject $subject */
818 $subject = $this->getMockBuilder(BackendUserAuthentication::class)
819 ->setMethods(['getTSConfig'])
820 ->getMock();
821
822 $this->assertTrue($subject->jsConfirmation(JsConfirmation::TYPE_CHANGE));
823 }
824
825 /**
826 * Data provider to test page permissions constraints
827 * returns an array of test conditions:
828 * - permission bit(s) as integer
829 * - admin flag
830 * - groups for user
831 * - expected SQL fragment
832 *
833 * @return array
834 */
835 public function getPagePermissionsClauseWithValidUserDataProvider(): array
836 {
837 return [
838 'for admin' => [
839 1,
840 true,
841 '',
842 ' 1=1'
843 ],
844 'for admin with groups' => [
845 11,
846 true,
847 '1,2',
848 ' 1=1'
849 ],
850 'for user' => [
851 2,
852 false,
853 '',
854 ' ((`pages`.`perms_everybody` & 2 = 2) OR' .
855 ' ((`pages`.`perms_userid` = 123) AND (`pages`.`perms_user` & 2 = 2)))'
856 ],
857 'for user with groups' => [
858 8,
859 false,
860 '1,2',
861 ' ((`pages`.`perms_everybody` & 8 = 8) OR' .
862 ' ((`pages`.`perms_userid` = 123) AND (`pages`.`perms_user` & 8 = 8))' .
863 ' OR ((`pages`.`perms_groupid` IN (1, 2)) AND (`pages`.`perms_group` & 8 = 8)))'
864 ],
865 ];
866 }
867
868 /**
869 * @test
870 * @dataProvider getPagePermissionsClauseWithValidUserDataProvider
871 * @param int $perms
872 * @param bool $admin
873 * @param string $groups
874 * @param string $expected
875 */
876 public function getPagePermissionsClauseWithValidUser(int $perms, bool $admin, string $groups, string $expected): void
877 {
878 // We only need to setup the mocking for the non-admin cases
879 // If this setup is done for admin cases the FIFO behavior
880 // of GeneralUtility::addInstance will influence other tests
881 // as the ConnectionPool is never used!
882 if (!$admin) {
883 /** @var Connection|ObjectProphecy $connectionProphecy */
884 $connectionProphecy = $this->prophesize(Connection::class);
885 $connectionProphecy->getDatabasePlatform()->willReturn(new MockPlatform());
886 $connectionProphecy->quoteIdentifier(Argument::cetera())->will(function ($args) {
887 return '`' . str_replace('.', '`.`', $args[0]) . '`';
888 });
889
890 /** @var QueryBuilder|ObjectProphecy $queryBuilderProphecy */
891 $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
892 $queryBuilderProphecy->expr()->willReturn(
893 new ExpressionBuilder($connectionProphecy->reveal())
894 );
895
896 /** @var ConnectionPool|ObjectProphecy $databaseProphecy */
897 $databaseProphecy = $this->prophesize(ConnectionPool::class);
898 $databaseProphecy->getQueryBuilderForTable('pages')->willReturn($queryBuilderProphecy->reveal());
899 // Shift previously added instance
900 GeneralUtility::makeInstance(ConnectionPool::class);
901 GeneralUtility::addInstance(ConnectionPool::class, $databaseProphecy->reveal());
902 }
903
904 /** @var BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject $subject */
905 $subject = $this->getMockBuilder(BackendUserAuthentication::class)
906 ->setMethods(['isAdmin'])
907 ->getMock();
908 $subject->setLogger(new NullLogger());
909 $subject->expects($this->any())
910 ->method('isAdmin')
911 ->will($this->returnValue($admin));
912
913 $subject->user = ['uid' => 123];
914 $subject->groupList = $groups;
915
916 $this->assertEquals($expected, $subject->getPagePermsClause($perms));
917 }
918 }