TYPO3 CMS  TYPO3_8-7
BackendUserAuthenticationTest.php
Go to the documentation of this file.
1 <?php
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 
30 
34 class BackendUserAuthenticationTest extends UnitTestCase
35 {
40  // File permissions
41  'addFile' => false,
42  'readFile' => false,
43  'writeFile' => false,
44  'copyFile' => false,
45  'moveFile' => false,
46  'renameFile' => false,
47  'deleteFile' => false,
48  // Folder permissions
49  'addFolder' => false,
50  'readFolder' => false,
51  'writeFolder' => false,
52  'copyFolder' => false,
53  'moveFolder' => false,
54  'renameFolder' => false,
55  'deleteFolder' => false,
56  'recursivedeleteFolder' => false
57  ];
58 
62  protected function tearDown()
63  {
65  parent::tearDown();
66  }
67 
69  // Tests concerning the form protection
71 
74  public function logoffCleansFormProtectionIfBackendUserIsLoggedIn()
75  {
77  $connection = $this->prophesize(Connection::class);
78  $connection->delete('sys_lockedrecords', Argument::cetera())->willReturn(1);
79 
81  $connectionPool = $this->prophesize(ConnectionPool::class);
82  $connectionPool->getConnectionForTable(Argument::cetera())->willReturn($connection->reveal());
83 
84  GeneralUtility::addInstance(ConnectionPool::class, $connectionPool->reveal());
85 
87  $connection = $this->prophesize(Connection::class);
88  $connection->delete('be_sessions', Argument::cetera())->willReturn(1);
89 
91  $connectionPool = $this->prophesize(ConnectionPool::class);
92  $connectionPool->getConnectionForTable(Argument::cetera())->willReturn($connection->reveal());
93 
94  GeneralUtility::addInstance(ConnectionPool::class, $connectionPool->reveal());
95 
97  $formProtection = $this->prophesize(\TYPO3\CMS\Core\FormProtection\BackendFormProtection::class);
98  $formProtection->clean()->shouldBeCalled();
99 
101  'default',
102  $formProtection->reveal()
103  );
104 
105  $GLOBALS['BE_USER'] = $this->getMockBuilder(BackendUserAuthentication::class)->getMock();
106  $GLOBALS['BE_USER']->user = ['uid' => 4711];
107 
109  $subject = $this->getMockBuilder(BackendUserAuthentication::class)
110  ->setMethods(['dummy'])
111  ->disableOriginalConstructor()
112  ->getMock();
113 
114  $subject->logoff();
115  }
116 
120  public function getTSConfigDataProvider()
121  {
122  $completeConfiguration = [
123  'value' => 'oneValue',
124  'value.' => ['oneProperty' => 'oneValue'],
125  'permissions.' => [
126  'file.' => [
127  'default.' => ['readAction' => '1'],
128  '1.' => ['writeAction' => '1'],
129  '0.' => ['readAction' => '0'],
130  ],
131  ]
132  ];
133 
134  return [
135  'single level string' => [
136  $completeConfiguration,
137  'permissions',
138  [
139  'value' => null,
140  'properties' =>
141  [
142  'file.' => [
143  'default.' => ['readAction' => '1'],
144  '1.' => ['writeAction' => '1'],
145  '0.' => ['readAction' => '0'],
146  ],
147  ],
148  ],
149  ],
150  'two levels string' => [
151  $completeConfiguration,
152  'permissions.file',
153  [
154  'value' => null,
155  'properties' =>
156  [
157  'default.' => ['readAction' => '1'],
158  '1.' => ['writeAction' => '1'],
159  '0.' => ['readAction' => '0'],
160  ],
161  ],
162  ],
163  'three levels string' => [
164  $completeConfiguration,
165  'permissions.file.default',
166  [
167  'value' => null,
168  'properties' =>
169  ['readAction' => '1'],
170  ],
171  ],
172  'three levels string with integer property' => [
173  $completeConfiguration,
174  'permissions.file.1',
175  [
176  'value' => null,
177  'properties' => ['writeAction' => '1'],
178  ],
179  ],
180  'three levels string with integer zero property' => [
181  $completeConfiguration,
182  'permissions.file.0',
183  [
184  'value' => null,
185  'properties' => ['readAction' => '0'],
186  ],
187  ],
188  'four levels string with integer zero property, value, no properties' => [
189  $completeConfiguration,
190  'permissions.file.0.readAction',
191  [
192  'value' => '0',
193  'properties' => null,
194  ],
195  ],
196  'four levels string with integer property, value, no properties' => [
197  $completeConfiguration,
198  'permissions.file.1.writeAction',
199  [
200  'value' => '1',
201  'properties' => null,
202  ],
203  ],
204  'one level, not existent string' => [
205  $completeConfiguration,
206  'foo',
207  [
208  'value' => null,
209  'properties' => null,
210  ],
211  ],
212  'two level, not existent string' => [
213  $completeConfiguration,
214  'foo.bar',
215  [
216  'value' => null,
217  'properties' => null,
218  ],
219  ],
220  'two level, where second level does not exist' => [
221  $completeConfiguration,
222  'permissions.bar',
223  [
224  'value' => null,
225  'properties' => null,
226  ],
227  ],
228  'three level, where third level does not exist' => [
229  $completeConfiguration,
230  'permissions.file.foo',
231  [
232  'value' => null,
233  'properties' => null,
234  ],
235  ],
236  'three level, where second and third level does not exist' => [
237  $completeConfiguration,
238  'permissions.foo.bar',
239  [
240  'value' => null,
241  'properties' => null,
242  ],
243  ],
244  'value and properties' => [
245  $completeConfiguration,
246  'value',
247  [
248  'value' => 'oneValue',
249  'properties' => ['oneProperty' => 'oneValue'],
250  ],
251  ],
252  ];
253  }
254 
262  public function getTSConfigReturnsCorrectArrayForGivenObjectString(array $completeConfiguration, $objectString, array $expectedConfiguration)
263  {
265  $subject = $this->getMockBuilder(BackendUserAuthentication::class)
266  ->setMethods(['dummy'])
267  ->disableOriginalConstructor()
268  ->getMock();
269  $subject->userTS = $completeConfiguration;
270 
271  $actualConfiguration = $subject->getTSConfig($objectString);
272  $this->assertSame($expectedConfiguration, $actualConfiguration);
273  }
274 
279  {
280  return [
281  'Only read permissions' => [
282  [
283  'addFile' => 0,
284  'readFile' => 1,
285  'writeFile' => 0,
286  'copyFile' => 0,
287  'moveFile' => 0,
288  'renameFile' => 0,
289  'deleteFile' => 0,
290  'addFolder' => 0,
291  'readFolder' => 1,
292  'copyFolder' => 0,
293  'moveFolder' => 0,
294  'renameFolder' => 0,
295  'writeFolder' => 0,
296  'deleteFolder' => 0,
297  'recursivedeleteFolder' => 0,
298  ]
299  ],
300  'Uploading allowed' => [
301  [
302  'addFile' => 1,
303  'readFile' => 1,
304  'writeFile' => 1,
305  'copyFile' => 1,
306  'moveFile' => 1,
307  'renameFile' => 1,
308  'deleteFile' => 1,
309  'addFolder' => 0,
310  'readFolder' => 1,
311  'copyFolder' => 0,
312  'moveFolder' => 0,
313  'renameFolder' => 0,
314  'writeFolder' => 0,
315  'deleteFolder' => 0,
316  'recursivedeleteFolder' => 0
317  ]
318  ],
319  'One value is enough' => [
320  [
321  'addFile' => 1,
322  ]
323  ],
324  ];
325  }
326 
332  public function getFilePermissionsTakesUserDefaultPermissionsFromTsConfigIntoAccountIfUserIsNotAdmin(array $userTsConfiguration)
333  {
335  $subject = $this->getMockBuilder(BackendUserAuthentication::class)
336  ->setMethods(['isAdmin'])
337  ->getMock();
338 
339  $subject
340  ->expects($this->any())
341  ->method('isAdmin')
342  ->will($this->returnValue(false));
343 
344  $subject->userTS = [
345  'permissions.' => [
346  'file.' => [
347  'default.' => $userTsConfiguration
348  ],
349  ]
350  ];
351 
352  $expectedPermissions = array_merge($this->defaultFilePermissions, $userTsConfiguration);
353  array_walk(
354  $expectedPermissions,
355  function (&$value) {
356  $value = (bool)$value;
357  }
358  );
359 
360  $this->assertEquals($expectedPermissions, $subject->getFilePermissions());
361  }
362 
367  {
368  $defaultPermissions = [
369  'addFile' => true,
370  'readFile' => true,
371  'writeFile' => true,
372  'copyFile' => true,
373  'moveFile' => true,
374  'renameFile' => true,
375  'deleteFile' => true,
376  'addFolder' => true,
377  'readFolder' => true,
378  'copyFolder' => true,
379  'moveFolder' => true,
380  'renameFolder' => true,
381  'writeFolder' => true,
382  'deleteFolder' => true,
383  'recursivedeleteFolder' => true
384  ];
385 
386  return [
387  'Overwrites given storage permissions with default permissions' => [
388  $defaultPermissions,
389  1,
390  [
391  'addFile' => 0,
392  'recursivedeleteFolder' =>0
393  ],
394  [
395  'addFile' => 0,
396  'readFile' => 1,
397  'writeFile' => 1,
398  'copyFile' => 1,
399  'moveFile' => 1,
400  'renameFile' => 1,
401  'deleteFile' => 1,
402  'addFolder' => 1,
403  'readFolder' => 1,
404  'copyFolder' => 1,
405  'moveFolder' => 1,
406  'renameFolder' => 1,
407  'writeFolder' => 1,
408  'deleteFolder' => 1,
409  'recursivedeleteFolder' => 0
410  ]
411  ],
412  'Overwrites given storage 0 permissions with default permissions' => [
413  $defaultPermissions,
414  0,
415  [
416  'addFile' => 0,
417  'recursivedeleteFolder' =>0
418  ],
419  [
420  'addFile' => false,
421  'readFile' => true,
422  'writeFile' => true,
423  'copyFile' => true,
424  'moveFile' => true,
425  'renameFile' => true,
426  'deleteFile' => true,
427  'addFolder' => true,
428  'readFolder' => true,
429  'copyFolder' => true,
430  'moveFolder' => true,
431  'renameFolder' => true,
432  'writeFolder' => true,
433  'deleteFolder' => true,
434  'recursivedeleteFolder' => false
435  ]
436  ],
437  'Returns default permissions if no storage permissions are found' => [
438  $defaultPermissions,
439  1,
440  [],
441  [
442  'addFile' => true,
443  'readFile' => true,
444  'writeFile' => true,
445  'copyFile' => true,
446  'moveFile' => true,
447  'renameFile' => true,
448  'deleteFile' => true,
449  'addFolder' => true,
450  'readFolder' => true,
451  'copyFolder' => true,
452  'moveFolder' => true,
453  'renameFolder' => true,
454  'writeFolder' => true,
455  'deleteFolder' => true,
456  'recursivedeleteFolder' => true
457  ]
458  ],
459  ];
460  }
461 
470  public function getFilePermissionsFromStorageOverwritesDefaultPermissions(array $defaultPermissions, $storageUid, array $storagePermissions, array $expectedPermissions)
471  {
473  $subject = $this->getMockBuilder(BackendUserAuthentication::class)
474  ->setMethods(['isAdmin', 'getFilePermissions'])
475  ->getMock();
476  $storageMock = $this->createMock(ResourceStorage::class);
477  $storageMock->expects($this->any())->method('getUid')->will($this->returnValue($storageUid));
478 
479  $subject
480  ->expects($this->any())
481  ->method('isAdmin')
482  ->will($this->returnValue(false));
483 
484  $subject
485  ->expects($this->any())
486  ->method('getFilePermissions')
487  ->will($this->returnValue($defaultPermissions));
488 
489  $subject->userTS = [
490  'permissions.' => [
491  'file.' => [
492  'storage.' => [
493  $storageUid . '.' => $storagePermissions
494  ],
495  ],
496  ]
497  ];
498 
499  $this->assertEquals($expectedPermissions, $subject->getFilePermissionsForStorage($storageMock));
500  }
501 
509  public function getFilePermissionsFromStorageAlwaysReturnsDefaultPermissionsForAdmins(array $defaultPermissions, $storageUid, array $storagePermissions)
510  {
512  $subject = $this->getMockBuilder(BackendUserAuthentication::class)
513  ->setMethods(['isAdmin', 'getFilePermissions'])
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->userTS = [
529  'permissions.' => [
530  'file.' => [
531  'storage.' => [
532  $storageUid . '.' => $storagePermissions
533  ],
534  ],
535  ]
536  ];
537 
538  $this->assertEquals($defaultPermissions, $subject->getFilePermissionsForStorage($storageMock));
539  }
540 
545  {
546  return [
547  'No permission' => [
548  '',
549  [
550  'addFile' => false,
551  'readFile' => false,
552  'writeFile' => false,
553  'copyFile' => false,
554  'moveFile' => false,
555  'renameFile' => false,
556  'deleteFile' => false,
557  'addFolder' => false,
558  'readFolder' => false,
559  'copyFolder' => false,
560  'moveFolder' => false,
561  'renameFolder' => false,
562  'writeFolder' => false,
563  'deleteFolder' => false,
564  'recursivedeleteFolder' => false
565  ]
566  ],
567  'Standard file permissions' => [
568  'addFile,readFile,writeFile,copyFile,moveFile,renameFile,deleteFile',
569  [
570  'addFile' => true,
571  'readFile' => true,
572  'writeFile' => true,
573  'copyFile' => true,
574  'moveFile' => true,
575  'renameFile' => true,
576  'deleteFile' => true,
577  'addFolder' => false,
578  'readFolder' => false,
579  'copyFolder' => false,
580  'moveFolder' => false,
581  'renameFolder' => false,
582  'writeFolder' => false,
583  'deleteFolder' => false,
584  'recursivedeleteFolder' => false
585  ]
586  ],
587  'Standard folder permissions' => [
588  'addFolder,readFolder,moveFolder,renameFolder,writeFolder,deleteFolder',
589  [
590  'addFile' => false,
591  'readFile' => false,
592  'writeFile' => false,
593  'copyFile' => false,
594  'moveFile' => false,
595  'renameFile' => false,
596  'deleteFile' => false,
597  'addFolder' => true,
598  'readFolder' => true,
599  'writeFolder' => true,
600  'copyFolder' => false,
601  'moveFolder' => true,
602  'renameFolder' => true,
603  'deleteFolder' => true,
604  'recursivedeleteFolder' => false
605  ]
606  ],
607  'Copy folder allowed' => [
608  'readFolder,copyFolder',
609  [
610  'addFile' => false,
611  'readFile' => false,
612  'writeFile' => false,
613  'copyFile' => false,
614  'moveFile' => false,
615  'renameFile' => false,
616  'deleteFile' => false,
617  'addFolder' => false,
618  'readFolder' => true,
619  'writeFolder' => false,
620  'copyFolder' => true,
621  'moveFolder' => false,
622  'renameFolder' => false,
623  'deleteFolder' => false,
624  'recursivedeleteFolder' => false
625  ]
626  ],
627  'Copy folder and remove subfolders allowed' => [
628  'readFolder,copyFolder,recursivedeleteFolder',
629  [
630  'addFile' => false,
631  'readFile' => false,
632  'writeFile' => false,
633  'copyFile' => false,
634  'moveFile' => false,
635  'renameFile' => false,
636  'deleteFile' => false,
637  'addFolder' => false,
638  'readFolder' => true,
639  'writeFolder' => false,
640  'copyFolder' => true,
641  'moveFolder' => false,
642  'renameFolder' => false,
643  'deleteFolder' => false,
644  'recursivedeleteFolder' => true
645  ]
646  ],
647  ];
648  }
649 
658  public function getFilePermissionsTakesUserDefaultPermissionsFromRecordIntoAccountIfUserIsNotAdmin(string $permissionValue, array $expectedPermissions)
659  {
661  $subject = $this->getMockBuilder(BackendUserAuthentication::class)
662  ->setMethods(['isAdmin'])
663  ->getMock();
664 
665  $subject
666  ->expects($this->any())
667  ->method('isAdmin')
668  ->will($this->returnValue(false));
669 
670  $subject->userTS = [];
671  $subject->groupData['file_permissions'] = $permissionValue;
672  $this->assertEquals($expectedPermissions, $subject->getFilePermissions());
673  }
674 
678  public function getFilePermissionsGrantsAllPermissionsToAdminUsers()
679  {
681  $subject = $this->getMockBuilder(BackendUserAuthentication::class)
682  ->setMethods(['isAdmin'])
683  ->getMock();
684 
685  $subject
686  ->expects($this->any())
687  ->method('isAdmin')
688  ->will($this->returnValue(true));
689 
690  $expectedPermissions = [
691  'addFile' => true,
692  'readFile' => true,
693  'writeFile' => true,
694  'copyFile' => true,
695  'moveFile' => true,
696  'renameFile' => true,
697  'deleteFile' => true,
698  'addFolder' => true,
699  'readFolder' => true,
700  'writeFolder' => true,
701  'copyFolder' => true,
702  'moveFolder' => true,
703  'renameFolder' => true,
704  'deleteFolder' => true,
705  'recursivedeleteFolder' => true
706  ];
707 
708  $this->assertEquals($expectedPermissions, $subject->getFilePermissions());
709  }
710 
714  public function jsConfirmationReturnsTrueIfPassedValueEqualsConfiguration()
715  {
717  $subject = $this->getMockBuilder(BackendUserAuthentication::class)
718  ->setMethods(['getTSConfig'])
719  ->getMock();
720  $subject->method('getTSConfig')->with('options.alertPopups')->willReturn(['value' => 1]);
721 
722  $this->assertTrue($subject->jsConfirmation(JsConfirmation::TYPE_CHANGE));
723  $this->assertFalse($subject->jsConfirmation(JsConfirmation::COPY_MOVE_PASTE));
724  }
725 
729  public function jsConfirmationAllowsSettingMultipleBitsInValue()
730  {
732  $subject = $this->getMockBuilder(BackendUserAuthentication::class)
733  ->setMethods(['getTSConfig'])
734  ->getMock();
735  $subject->method('getTSConfig')->with('options.alertPopups')->willReturn(['value' => 3]);
736 
737  $this->assertTrue($subject->jsConfirmation(JsConfirmation::TYPE_CHANGE));
738  $this->assertTrue($subject->jsConfirmation(JsConfirmation::COPY_MOVE_PASTE));
739  }
740 
752  public function jsConfirmationAllowsUnsettingBitsInValue($jsConfirmation, $typeChangeAllowed, $copyMovePasteAllowed, $deleteAllowed, $feEditAllowed, $otherAllowed)
753  {
754  $subject = $this->getMockBuilder(BackendUserAuthentication::class)
755  ->setMethods(['getTSConfig'])
756  ->getMock();
757  $subject->method('getTSConfig')->with('options.alertPopups')->willReturn(['value' => $jsConfirmation]);
758 
759  $this->assertEquals($typeChangeAllowed, $subject->jsConfirmation(JsConfirmation::TYPE_CHANGE));
760  $this->assertEquals($copyMovePasteAllowed, $subject->jsConfirmation(JsConfirmation::COPY_MOVE_PASTE));
761  $this->assertEquals($deleteAllowed, $subject->jsConfirmation(JsConfirmation::DELETE));
762  $this->assertEquals($feEditAllowed, $subject->jsConfirmation(JsConfirmation::FE_EDIT));
763  $this->assertEquals($otherAllowed, $subject->jsConfirmation(JsConfirmation::OTHER));
764  }
765 
770  {
771  return [
772  'All except "type change" and "copy/move/paste"' => [
773  252,
774  false,
775  false,
776  true,
777  true,
778  true,
779  ],
780  'All except "other"' => [
781  127,
782  true,
783  true,
784  true,
785  true,
786  false,
787  ],
788  ];
789  }
790 
794  public function jsConfirmationAlwaysReturnsFalseIfNoConfirmationIsSet()
795  {
797  $subject = $this->getMockBuilder(BackendUserAuthentication::class)
798  ->setMethods(['getTSConfig'])
799  ->getMock();
800  $subject->method('getTSConfig')->with('options.alertPopups')->willReturn(['value' => 0]);
801 
802  $this->assertFalse($subject->jsConfirmation(JsConfirmation::TYPE_CHANGE));
803  $this->assertFalse($subject->jsConfirmation(JsConfirmation::COPY_MOVE_PASTE));
804  }
805 
809  public function jsConfirmationReturnsTrueIfConfigurationIsMissing()
810  {
812  $subject = $this->getMockBuilder(BackendUserAuthentication::class)
813  ->setMethods(['getTSConfig'])
814  ->getMock();
815 
816  $this->assertTrue($subject->jsConfirmation(JsConfirmation::TYPE_CHANGE));
817  }
818 
830  {
831  return [
832  'for admin' => [
833  1,
834  true,
835  '',
836  ' 1=1'
837  ],
838  'for admin with groups' => [
839  11,
840  true,
841  '1,2',
842  ' 1=1'
843  ],
844  'for user' => [
845  2,
846  false,
847  '',
848  ' ((`pages`.`perms_everybody` & 2 = 2) OR' .
849  ' ((`pages`.`perms_userid` = 123) AND (`pages`.`perms_user` & 2 = 2)))'
850  ],
851  'for user with groups' => [
852  8,
853  false,
854  '1,2',
855  ' ((`pages`.`perms_everybody` & 8 = 8) OR' .
856  ' ((`pages`.`perms_userid` = 123) AND (`pages`.`perms_user` & 8 = 8))' .
857  ' OR ((`pages`.`perms_groupid` IN (1, 2)) AND (`pages`.`perms_group` & 8 = 8)))'
858  ],
859  ];
860  }
861 
870  public function getPagePermissionsClauseWithValidUser(int $perms, bool $admin, string $groups, string $expected)
871  {
872  // We only need to setup the mocking for the non-admin cases
873  // If this setup is done for admin cases the FIFO behavior
874  // of GeneralUtility::addInstance will influence other tests
875  // as the ConnectionPool is never used!
876  if (!$admin) {
878  $connectionProphecy = $this->prophesize(Connection::class);
879  $connectionProphecy->getDatabasePlatform()->willReturn(new MockPlatform());
880  $connectionProphecy->quoteIdentifier(Argument::cetera())->will(function ($args) {
881  return '`' . str_replace('.', '`.`', $args[0]) . '`';
882  });
883 
885  $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
886  $queryBuilderProphecy->expr()->willReturn(
887  new ExpressionBuilder($connectionProphecy->reveal())
888  );
889 
891  $databaseProphecy = $this->prophesize(ConnectionPool::class);
892  $databaseProphecy->getQueryBuilderForTable('pages')->willReturn($queryBuilderProphecy->reveal());
893  // Shift previously added instance
894  GeneralUtility::makeInstance(ConnectionPool::class);
895  GeneralUtility::addInstance(ConnectionPool::class, $databaseProphecy->reveal());
896  }
897 
899  $subject = $this->getMockBuilder(BackendUserAuthentication::class)
900  ->setMethods(['isAdmin'])
901  ->getMock();
902  $subject->expects($this->any())
903  ->method('isAdmin')
904  ->will($this->returnValue($admin));
905 
906  $subject->user = ['uid' => 123];
907  $subject->groupList = $groups;
908 
909  $this->assertEquals($expected, $subject->getPagePermsClause($perms));
910  }
911 }
static addInstance($className, $instance)
static makeInstance($className,... $constructorArguments)
static set($classNameOrType, AbstractFormProtection $instance)
jsConfirmationAllowsUnsettingBitsInValue($jsConfirmation, $typeChangeAllowed, $copyMovePasteAllowed, $deleteAllowed, $feEditAllowed, $otherAllowed)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']