e664c4e7148d243a2f4f84626d47653f4ba3279e
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Authentication / BackendUserAuthenticationTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\Authentication;
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\Core\Authentication\BackendUserAuthentication;
20 use TYPO3\CMS\Core\Database\Connection;
21 use TYPO3\CMS\Core\Database\ConnectionPool;
22 use TYPO3\CMS\Core\Database\DatabaseConnection;
23 use TYPO3\CMS\Core\Type\Bitmask\JsConfirmation;
24 use TYPO3\CMS\Core\Utility\GeneralUtility;
25
26 /**
27 * Testcase for BackendUserAuthentication
28 */
29 class BackendUserAuthenticationTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
30 {
31 /**
32 * @var array
33 */
34 protected $defaultFilePermissions = array(
35 // File permissions
36 'addFile' => false,
37 'readFile' => false,
38 'writeFile' => false,
39 'copyFile' => false,
40 'moveFile' => false,
41 'renameFile' => false,
42 'deleteFile' => false,
43 // Folder permissions
44 'addFolder' => false,
45 'readFolder' => false,
46 'writeFolder' => false,
47 'copyFolder' => false,
48 'moveFolder' => false,
49 'renameFolder' => false,
50 'deleteFolder' => false,
51 'recursivedeleteFolder' => false
52 );
53
54 protected function setUp()
55 {
56 // reset hooks
57 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'] = array();
58 }
59
60 protected function tearDown()
61 {
62 \TYPO3\CMS\Core\FormProtection\FormProtectionFactory::purgeInstances();
63 parent::tearDown();
64 }
65
66 /////////////////////////////////////////
67 // Tests concerning the form protection
68 /////////////////////////////////////////
69 /**
70 * @test
71 */
72 public function logoffCleansFormProtectionIfBackendUserIsLoggedIn()
73 {
74 /** @var ObjectProphecy|Connection $connection */
75 $connection = $this->prophesize(Connection::class);
76 $connection->delete('be_sessions', Argument::cetera())->willReturn(1);
77
78 /** @var ObjectProphecy|ConnectionPool $connectionPool */
79 $connectionPool = $this->prophesize(ConnectionPool::class);
80 $connectionPool->getConnectionForTable(Argument::cetera())->willReturn($connection->reveal());
81
82 GeneralUtility::addInstance(ConnectionPool::class, $connectionPool->reveal());
83
84 /** @var ObjectProphecy|\TYPO3\CMS\Core\FormProtection\AbstractFormProtection $formProtection */
85 $formProtection = $this->prophesize(\TYPO3\CMS\Core\FormProtection\BackendFormProtection::class);
86 $formProtection->clean()->shouldBeCalled();
87
88 \TYPO3\CMS\Core\FormProtection\FormProtectionFactory::set(
89 'default',
90 $formProtection->reveal()
91 );
92
93 // logoff() call the static factory that has a dependency to a valid BE_USER object. Mock this away
94 $GLOBALS['BE_USER'] = $this->getMock(BackendUserAuthentication::class, array(), array(), '', false);
95 $GLOBALS['BE_USER']->user = array('uid' => $this->getUniqueId());
96 $GLOBALS['TYPO3_DB'] = $this->getMock(DatabaseConnection::class, array(), array(), '', false);
97
98 /** @var BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject $subject */
99 $subject = $this->getMock(BackendUserAuthentication::class, array('dummy'), array(), '', false);
100 $subject->logoff();
101 }
102
103 /**
104 * @return array
105 */
106 public function getTSConfigDataProvider()
107 {
108 $completeConfiguration = array(
109 'value' => 'oneValue',
110 'value.' => array('oneProperty' => 'oneValue'),
111 'permissions.' => array(
112 'file.' => array(
113 'default.' => array('readAction' => '1'),
114 '1.' => array('writeAction' => '1'),
115 '0.' => array('readAction' => '0'),
116 ),
117 )
118 );
119
120 return array(
121 'single level string' => array(
122 $completeConfiguration,
123 'permissions',
124 array(
125 'value' => null,
126 'properties' =>
127 array(
128 'file.' => array(
129 'default.' => array('readAction' => '1'),
130 '1.' => array('writeAction' => '1'),
131 '0.' => array('readAction' => '0'),
132 ),
133 ),
134 ),
135 ),
136 'two levels string' => array(
137 $completeConfiguration,
138 'permissions.file',
139 array(
140 'value' => null,
141 'properties' =>
142 array(
143 'default.' => array('readAction' => '1'),
144 '1.' => array('writeAction' => '1'),
145 '0.' => array('readAction' => '0'),
146 ),
147 ),
148 ),
149 'three levels string' => array(
150 $completeConfiguration,
151 'permissions.file.default',
152 array(
153 'value' => null,
154 'properties' =>
155 array('readAction' => '1'),
156 ),
157 ),
158 'three levels string with integer property' => array(
159 $completeConfiguration,
160 'permissions.file.1',
161 array(
162 'value' => null,
163 'properties' => array('writeAction' => '1'),
164 ),
165 ),
166 'three levels string with integer zero property' => array(
167 $completeConfiguration,
168 'permissions.file.0',
169 array(
170 'value' => null,
171 'properties' => array('readAction' => '0'),
172 ),
173 ),
174 'four levels string with integer zero property, value, no properties' => array(
175 $completeConfiguration,
176 'permissions.file.0.readAction',
177 array(
178 'value' => '0',
179 'properties' => null,
180 ),
181 ),
182 'four levels string with integer property, value, no properties' => array(
183 $completeConfiguration,
184 'permissions.file.1.writeAction',
185 array(
186 'value' => '1',
187 'properties' => null,
188 ),
189 ),
190 'one level, not existent string' => array(
191 $completeConfiguration,
192 'foo',
193 array(
194 'value' => null,
195 'properties' => null,
196 ),
197 ),
198 'two level, not existent string' => array(
199 $completeConfiguration,
200 'foo.bar',
201 array(
202 'value' => null,
203 'properties' => null,
204 ),
205 ),
206 'two level, where second level does not exist' => array(
207 $completeConfiguration,
208 'permissions.bar',
209 array(
210 'value' => null,
211 'properties' => null,
212 ),
213 ),
214 'three level, where third level does not exist' => array(
215 $completeConfiguration,
216 'permissions.file.foo',
217 array(
218 'value' => null,
219 'properties' => null,
220 ),
221 ),
222 'three level, where second and third level does not exist' => array(
223 $completeConfiguration,
224 'permissions.foo.bar',
225 array(
226 'value' => null,
227 'properties' => null,
228 ),
229 ),
230 'value and properties' => array(
231 $completeConfiguration,
232 'value',
233 array(
234 'value' => 'oneValue',
235 'properties' => array('oneProperty' => 'oneValue'),
236 ),
237 ),
238 );
239 }
240
241 /**
242 * @param array $completeConfiguration
243 * @param string $objectString
244 * @param array $expectedConfiguration
245 * @dataProvider getTSConfigDataProvider
246 * @test
247 */
248 public function getTSConfigReturnsCorrectArrayForGivenObjectString(array $completeConfiguration, $objectString, array $expectedConfiguration)
249 {
250 $subject = $this->getMock(BackendUserAuthentication::class, array('dummy'), array(), '', false);
251 $subject->userTS = $completeConfiguration;
252
253 $actualConfiguration = $subject->getTSConfig($objectString);
254 $this->assertSame($expectedConfiguration, $actualConfiguration);
255 }
256
257 /**
258 * @return array
259 */
260 public function getFilePermissionsTakesUserDefaultAndStoragePermissionsIntoAccountIfUserIsNotAdminDataProvider()
261 {
262 return array(
263 'Only read permissions' => array(
264 array(
265 'addFile' => 0,
266 'readFile' => 1,
267 'writeFile' => 0,
268 'copyFile' => 0,
269 'moveFile' => 0,
270 'renameFile' => 0,
271 'deleteFile' => 0,
272 'addFolder' => 0,
273 'readFolder' => 1,
274 'copyFolder' => 0,
275 'moveFolder' => 0,
276 'renameFolder' => 0,
277 'writeFolder' => 0,
278 'deleteFolder' => 0,
279 'recursivedeleteFolder' => 0,
280 )
281 ),
282 'Uploading allowed' => array(
283 array(
284 'addFile' => 1,
285 'readFile' => 1,
286 'writeFile' => 1,
287 'copyFile' => 1,
288 'moveFile' => 1,
289 'renameFile' => 1,
290 'deleteFile' => 1,
291 'addFolder' => 0,
292 'readFolder' => 1,
293 'copyFolder' => 0,
294 'moveFolder' => 0,
295 'renameFolder' => 0,
296 'writeFolder' => 0,
297 'deleteFolder' => 0,
298 'recursivedeleteFolder' => 0
299 )
300 ),
301 'One value is enough' => array(
302 array(
303 'addFile' => 1,
304 )
305 ),
306 );
307 }
308
309 /**
310 * @param array $userTsConfiguration
311 * @test
312 * @dataProvider getFilePermissionsTakesUserDefaultAndStoragePermissionsIntoAccountIfUserIsNotAdminDataProvider
313 */
314 public function getFilePermissionsTakesUserDefaultPermissionsFromTsConfigIntoAccountIfUserIsNotAdmin(array $userTsConfiguration)
315 {
316 $subject = $this->getMock(BackendUserAuthentication::class, array('isAdmin'));
317
318 $subject
319 ->expects($this->any())
320 ->method('isAdmin')
321 ->will($this->returnValue(false));
322
323 $subject->userTS = array(
324 'permissions.' => array(
325 'file.' => array(
326 'default.' => $userTsConfiguration
327 ),
328 )
329 );
330
331 $expectedPermissions = array_merge($this->defaultFilePermissions, $userTsConfiguration);
332 array_walk(
333 $expectedPermissions,
334 function (&$value) {
335 $value = (bool)$value;
336 }
337 );
338
339 $this->assertEquals($expectedPermissions, $subject->getFilePermissions());
340 }
341
342 /**
343 * @return array
344 */
345 public function getFilePermissionsFromStorageDataProvider()
346 {
347 $defaultPermissions = array(
348 'addFile' => true,
349 'readFile' => true,
350 'writeFile' => true,
351 'copyFile' => true,
352 'moveFile' => true,
353 'renameFile' => true,
354 'deleteFile' => true,
355 'addFolder' => true,
356 'readFolder' => true,
357 'copyFolder' => true,
358 'moveFolder' => true,
359 'renameFolder' => true,
360 'writeFolder' => true,
361 'deleteFolder' => true,
362 'recursivedeleteFolder' => true
363 );
364
365 return array(
366 'Overwrites given storage permissions with default permissions' => array(
367 $defaultPermissions,
368 1,
369 array(
370 'addFile' => 0,
371 'recursivedeleteFolder' =>0
372 ),
373 array(
374 'addFile' => 0,
375 'readFile' => 1,
376 'writeFile' => 1,
377 'copyFile' => 1,
378 'moveFile' => 1,
379 'renameFile' => 1,
380 'deleteFile' => 1,
381 'addFolder' => 1,
382 'readFolder' => 1,
383 'copyFolder' => 1,
384 'moveFolder' => 1,
385 'renameFolder' => 1,
386 'writeFolder' => 1,
387 'deleteFolder' => 1,
388 'recursivedeleteFolder' => 0
389 )
390 ),
391 'Overwrites given storage 0 permissions with default permissions' => array(
392 $defaultPermissions,
393 0,
394 array(
395 'addFile' => 0,
396 'recursivedeleteFolder' =>0
397 ),
398 array(
399 'addFile' => false,
400 'readFile' => true,
401 'writeFile' => true,
402 'copyFile' => true,
403 'moveFile' => true,
404 'renameFile' => true,
405 'deleteFile' => true,
406 'addFolder' => true,
407 'readFolder' => true,
408 'copyFolder' => true,
409 'moveFolder' => true,
410 'renameFolder' => true,
411 'writeFolder' => true,
412 'deleteFolder' => true,
413 'recursivedeleteFolder' => false
414 )
415 ),
416 'Returns default permissions if no storage permissions are found' => array(
417 $defaultPermissions,
418 1,
419 array(),
420 array(
421 'addFile' => true,
422 'readFile' => true,
423 'writeFile' => true,
424 'copyFile' => true,
425 'moveFile' => true,
426 'renameFile' => true,
427 'deleteFile' => true,
428 'addFolder' => true,
429 'readFolder' => true,
430 'copyFolder' => true,
431 'moveFolder' => true,
432 'renameFolder' => true,
433 'writeFolder' => true,
434 'deleteFolder' => true,
435 'recursivedeleteFolder' => true
436 )
437 ),
438 );
439 }
440
441 /**
442 * @param array $defaultPermissions
443 * @param int $storageUid
444 * @param array $storagePermissions
445 * @param array $expectedPermissions
446 * @test
447 * @dataProvider getFilePermissionsFromStorageDataProvider
448 */
449 public function getFilePermissionsFromStorageOverwritesDefaultPermissions(array $defaultPermissions, $storageUid, array $storagePermissions, array $expectedPermissions)
450 {
451 $subject = $this->getMock(BackendUserAuthentication::class, array('isAdmin', 'getFilePermissions'));
452 $storageMock = $this->getMock(\TYPO3\CMS\Core\Resource\ResourceStorage::class, array(), array(), '', false);
453 $storageMock->expects($this->any())->method('getUid')->will($this->returnValue($storageUid));
454
455 $subject
456 ->expects($this->any())
457 ->method('isAdmin')
458 ->will($this->returnValue(false));
459
460 $subject
461 ->expects($this->any())
462 ->method('getFilePermissions')
463 ->will($this->returnValue($defaultPermissions));
464
465 $subject->userTS = array(
466 'permissions.' => array(
467 'file.' => array(
468 'storage.' => array(
469 $storageUid . '.' => $storagePermissions
470 ),
471 ),
472 )
473 );
474
475 $this->assertEquals($expectedPermissions, $subject->getFilePermissionsForStorage($storageMock));
476 }
477
478 /**
479 * @param array $defaultPermissions
480 * @param $storageUid
481 * @param array $storagePermissions
482 * @test
483 * @dataProvider getFilePermissionsFromStorageDataProvider
484 */
485 public function getFilePermissionsFromStorageAlwaysReturnsDefaultPermissionsForAdmins(array $defaultPermissions, $storageUid, array $storagePermissions)
486 {
487 $subject = $this->getMock(BackendUserAuthentication::class, array('isAdmin', 'getFilePermissions'));
488 $storageMock = $this->getMock(\TYPO3\CMS\Core\Resource\ResourceStorage::class, array(), array(), '', false);
489 $storageMock->expects($this->any())->method('getUid')->will($this->returnValue($storageUid));
490
491 $subject
492 ->expects($this->any())
493 ->method('isAdmin')
494 ->will($this->returnValue(true));
495
496 $subject
497 ->expects($this->any())
498 ->method('getFilePermissions')
499 ->will($this->returnValue($defaultPermissions));
500
501 $subject->userTS = array(
502 'permissions.' => array(
503 'file.' => array(
504 'storage.' => array(
505 $storageUid . '.' => $storagePermissions
506 ),
507 ),
508 )
509 );
510
511 $this->assertEquals($defaultPermissions, $subject->getFilePermissionsForStorage($storageMock));
512 }
513
514 /**
515 * @return array
516 */
517 public function getFilePermissionsTakesUserDefaultPermissionsFromRecordIntoAccountIfUserIsNotAdminDataProvider()
518 {
519 return array(
520 'No permission' => array(
521 '',
522 array(
523 'addFile' => false,
524 'readFile' => false,
525 'writeFile' => false,
526 'copyFile' => false,
527 'moveFile' => false,
528 'renameFile' => false,
529 'deleteFile' => false,
530 'addFolder' => false,
531 'readFolder' => false,
532 'copyFolder' => false,
533 'moveFolder' => false,
534 'renameFolder' => false,
535 'writeFolder' => false,
536 'deleteFolder' => false,
537 'recursivedeleteFolder' => false
538 )
539 ),
540 'Standard file permissions' => array(
541 'addFile,readFile,writeFile,copyFile,moveFile,renameFile,deleteFile',
542 array(
543 'addFile' => true,
544 'readFile' => true,
545 'writeFile' => true,
546 'copyFile' => true,
547 'moveFile' => true,
548 'renameFile' => true,
549 'deleteFile' => true,
550 'addFolder' => false,
551 'readFolder' => false,
552 'copyFolder' => false,
553 'moveFolder' => false,
554 'renameFolder' => false,
555 'writeFolder' => false,
556 'deleteFolder' => false,
557 'recursivedeleteFolder' => false
558 )
559 ),
560 'Standard folder permissions' => array(
561 'addFolder,readFolder,moveFolder,renameFolder,writeFolder,deleteFolder',
562 array(
563 'addFile' => false,
564 'readFile' => false,
565 'writeFile' => false,
566 'copyFile' => false,
567 'moveFile' => false,
568 'renameFile' => false,
569 'deleteFile' => false,
570 'addFolder' => true,
571 'readFolder' => true,
572 'writeFolder' => true,
573 'copyFolder' => false,
574 'moveFolder' => true,
575 'renameFolder' => true,
576 'deleteFolder' => true,
577 'recursivedeleteFolder' => false
578 )
579 ),
580 'Copy folder allowed' => array(
581 'readFolder,copyFolder',
582 array(
583 'addFile' => false,
584 'readFile' => false,
585 'writeFile' => false,
586 'copyFile' => false,
587 'moveFile' => false,
588 'renameFile' => false,
589 'deleteFile' => false,
590 'addFolder' => false,
591 'readFolder' => true,
592 'writeFolder' => false,
593 'copyFolder' => true,
594 'moveFolder' => false,
595 'renameFolder' => false,
596 'deleteFolder' => false,
597 'recursivedeleteFolder' => false
598 )
599 ),
600 'Copy folder and remove subfolders allowed' => array(
601 'readFolder,copyFolder,recursivedeleteFolder',
602 array(
603 'addFile' => false,
604 'readFile' => false,
605 'writeFile' => false,
606 'copyFile' => false,
607 'moveFile' => false,
608 'renameFile' => false,
609 'deleteFile' => false,
610 'addFolder' => false,
611 'readFolder' => true,
612 'writeFolder' => false,
613 'copyFolder' => true,
614 'moveFolder' => false,
615 'renameFolder' => false,
616 'deleteFolder' => false,
617 'recursivedeleteFolder' => true
618 )
619 ),
620 );
621 }
622
623 /**
624 * @test
625 * @dataProvider getFilePermissionsTakesUserDefaultPermissionsFromRecordIntoAccountIfUserIsNotAdminDataProvider
626 */
627 public function getFilePermissionsTakesUserDefaultPermissionsFromRecordIntoAccountIfUserIsNotAdmin($permissionValue, $expectedPermissions)
628 {
629 $subject = $this->getMock(BackendUserAuthentication::class, array('isAdmin'));
630
631 $subject
632 ->expects($this->any())
633 ->method('isAdmin')
634 ->will($this->returnValue(false));
635
636 $subject->userTS = array();
637 $subject->groupData['file_permissions'] = $permissionValue;
638 $this->assertEquals($expectedPermissions, $subject->getFilePermissions());
639 }
640
641 /**
642 * @test
643 */
644 public function getFilePermissionsGrantsAllPermissionsToAdminUsers()
645 {
646 $subject = $this->getMock(BackendUserAuthentication::class, array('isAdmin'));
647
648 $subject
649 ->expects($this->any())
650 ->method('isAdmin')
651 ->will($this->returnValue(true));
652
653 $expectedPermissions = array(
654 'addFile' => true,
655 'readFile' => true,
656 'writeFile' => true,
657 'copyFile' => true,
658 'moveFile' => true,
659 'renameFile' => true,
660 'deleteFile' => true,
661 'addFolder' => true,
662 'readFolder' => true,
663 'writeFolder' => true,
664 'copyFolder' => true,
665 'moveFolder' => true,
666 'renameFolder' => true,
667 'deleteFolder' => true,
668 'recursivedeleteFolder' => true
669 );
670
671 $this->assertEquals($expectedPermissions, $subject->getFilePermissions());
672 }
673
674 /**
675 * @test
676 */
677 public function jsConfirmationReturnsTrueIfPassedValueEqualsConfiguration()
678 {
679 $subject = $this->getMock(BackendUserAuthentication::class, ['getTSConfig']);
680 $subject->method('getTSConfig')->with('options.alertPopups')->willReturn(['value' => 1]);
681
682 $this->assertTrue($subject->jsConfirmation(JsConfirmation::TYPE_CHANGE));
683 $this->assertFalse($subject->jsConfirmation(JsConfirmation::COPY_MOVE_PASTE));
684 }
685
686 /**
687 * @test
688 */
689 public function jsConfirmationAllowsSettingMultipleBitsInValue()
690 {
691 $subject = $this->getMock(BackendUserAuthentication::class, ['getTSConfig']);
692 $subject->method('getTSConfig')->with('options.alertPopups')->willReturn(['value' => 3]);
693
694 $this->assertTrue($subject->jsConfirmation(JsConfirmation::TYPE_CHANGE));
695 $this->assertTrue($subject->jsConfirmation(JsConfirmation::COPY_MOVE_PASTE));
696 }
697
698 /**
699 * @test
700 */
701 public function jsConfirmationAlwaysReturnsFalseIfNoConfirmationIsSet()
702 {
703 $subject = $this->getMock(BackendUserAuthentication::class, ['getTSConfig']);
704 $subject->method('getTSConfig')->with('options.alertPopups')->willReturn(['value' => 0]);
705
706 $this->assertFalse($subject->jsConfirmation(JsConfirmation::TYPE_CHANGE));
707 $this->assertFalse($subject->jsConfirmation(JsConfirmation::COPY_MOVE_PASTE));
708 }
709
710 /**
711 * @test
712 */
713 public function jsConfirmationReturnsTrueIfConfigurationIsMissing()
714 {
715 $subject = $this->getMock(BackendUserAuthentication::class, ['getTSConfig']);
716
717 $this->assertTrue($subject->jsConfirmation(JsConfirmation::TYPE_CHANGE));
718 }
719 }