TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
Unit/Resource/ResourceStorageTest.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\Resource;
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 
26 
31 {
35  protected $singletonInstances = [];
36 
40  protected $subject;
41 
42  protected function setUp()
43  {
44  parent::setUp();
45  $this->singletonInstances = GeneralUtility::getSingletonInstances();
47  $fileRepositoryMock = $this->createMock(FileRepository::class);
49  FileRepository::class,
50  $fileRepositoryMock
51  );
52  }
53 
54  protected function tearDown()
55  {
56  GeneralUtility::resetSingletonInstances($this->singletonInstances);
57  parent::tearDown();
58  }
59 
68  protected function prepareSubject(array $configuration, $mockPermissionChecks = false, AbstractDriver $driverObject = null, array $storageRecord = [])
69  {
70  $permissionMethods = ['assureFileAddPermissions', 'checkFolderActionPermission', 'checkFileActionPermission', 'checkUserActionPermission', 'checkFileExtensionPermission', 'isWithinFileMountBoundaries'];
71  $mockedMethods = [];
72  $configuration = $this->convertConfigurationArrayToFlexformXml($configuration);
73  $overruleArray = ['configuration' => $configuration];
74  ArrayUtility::mergeRecursiveWithOverrule($storageRecord, $overruleArray);
75  if ($driverObject == null) {
76  $driverObject = $this->getMockForAbstractClass(AbstractDriver::class, [], '', false);
77  }
78  if ($mockPermissionChecks) {
79  $mockedMethods = $permissionMethods;
80  }
81  $mockedMethods[] = 'getIndexer';
82 
83  $this->subject = $this->getMockBuilder(ResourceStorage::class)
84  ->setMethods($mockedMethods)
85  ->setConstructorArgs([$driverObject, $storageRecord])
86  ->getMock();
87  $this->subject->expects($this->any())->method('getIndexer')->will($this->returnValue($this->createMock(\TYPO3\CMS\Core\Resource\Index\Indexer::class)));
88  if ($mockPermissionChecks) {
89  foreach ($permissionMethods as $method) {
90  $this->subject->expects($this->any())->method($method)->will($this->returnValue(true));
91  }
92  }
93  }
94 
102  protected function convertConfigurationArrayToFlexformXml(array $configuration)
103  {
104  $flexFormArray = ['data' => ['sDEF' => ['lDEF' => []]]];
105  foreach ($configuration as $key => $value) {
106  $flexFormArray['data']['sDEF']['lDEF'][$key] = ['vDEF' => $value];
107  }
108  $configuration = GeneralUtility::array2xml($flexFormArray);
109  return $configuration;
110  }
111 
122  protected function createDriverMock($driverConfiguration, ResourceStorage $storageObject = null, $mockedDriverMethods = [])
123  {
124  $this->initializeVfs();
125 
126  if (!isset($driverConfiguration['basePath'])) {
127  $driverConfiguration['basePath'] = $this->getMountRootUrl();
128  }
129 
130  if ($mockedDriverMethods === null) {
131  $driver = new LocalDriver($driverConfiguration);
132  } else {
133  // We are using the LocalDriver here because PHPUnit can't mock concrete methods in abstract classes, so
134  // when using the AbstractDriver we would be in trouble when wanting to mock away some concrete method
135  $driver = $this->getMockBuilder(LocalDriver::class)
136  ->setMethods($mockedDriverMethods)
137  ->setConstructorArgs([$driverConfiguration])
138  ->getMock();
139  }
140  if ($storageObject !== null) {
141  $storageObject->setDriver($driver);
142  }
143  $driver->setStorageUid(6);
144  $driver->processConfiguration();
145  $driver->initialize();
146  return $driver;
147  }
148 
153  {
154  return [
155  'Permissions evaluated, extension not in allowed list' => [
156  'fileName' => 'foo.txt',
157  'configuration' => ['allow' => 'jpg'],
158  'evaluatePermissions' => true,
159  'isAllowed' => true,
160  ],
161  'Permissions evaluated, extension in deny list' => [
162  'fileName' => 'foo.txt',
163  'configuration' => ['deny' => 'txt'],
164  'evaluatePermissions' => true,
165  'isAllowed' => false,
166  ],
167  'Permissions not evaluated, extension is php' => [
168  'fileName' => 'foo.php',
169  'configuration' => [],
170  'evaluatePermissions' => false,
171  'isAllowed' => false,
172  ],
173  'Permissions evaluated, extension is php' => [
174  'fileName' => 'foo.php',
175  // It is not possible to allow php file extension through configuration
176  'configuration' => ['allow' => 'php'],
177  'evaluatePermissions' => true,
178  'isAllowed' => false,
179  ],
180  ];
181  }
182 
191  public function fileExtensionPermissionIsWorkingCorrectly($fileName, array $configuration, $evaluatePermissions, $isAllowed)
192  {
193  $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']['webspace'] = $configuration;
194  $driverMock = $this->getMockForAbstractClass(AbstractDriver::class, [], '', false);
195  $subject = $this->getAccessibleMock(ResourceStorage::class, ['dummy'], [$driverMock, []]);
196  $subject->_set('evaluatePermissions', $evaluatePermissions);
197  $this->assertSame($isAllowed, $subject->_call('checkFileExtensionPermission', $fileName));
198  }
199 
203  public function capabilitiesDataProvider()
204  {
205  return [
206  'only public' => [
207  [
208  'public' => true,
209  'writable' => false,
210  'browsable' => false
211  ]
212  ],
213  'only writable' => [
214  [
215  'public' => false,
216  'writable' => true,
217  'browsable' => false
218  ]
219  ],
220  'only browsable' => [
221  [
222  'public' => false,
223  'writable' => false,
224  'browsable' => true
225  ]
226  ],
227  'all capabilities' => [
228  [
229  'public' => true,
230  'writable' => true,
231  'browsable' => true
232  ]
233  ],
234  'none' => [
235  [
236  'public' => false,
237  'writable' => false,
238  'browsable' => false
239  ]
240  ]
241  ];
242  }
243 
249  public function capabilitiesOfStorageObjectAreCorrectlySet(array $capabilities)
250  {
251  $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
252  $storageRecord = [
253  'is_public' => $capabilities['public'],
254  'is_writable' => $capabilities['writable'],
255  'is_browsable' => $capabilities['browsable'],
256  'is_online' => true
257  ];
258  $mockedDriver = $this->createDriverMock(
259  [
260  'pathType' => 'relative',
261  'basePath' => 'fileadmin/',
262  ],
263  $this->subject,
264  null
265  );
266  $this->prepareSubject([], false, $mockedDriver, $storageRecord);
267  $this->assertEquals($capabilities['public'], $this->subject->isPublic(), 'Capability "public" is not correctly set.');
268  $this->assertEquals($capabilities['writable'], $this->subject->isWritable(), 'Capability "writable" is not correctly set.');
269  $this->assertEquals($capabilities['browsable'], $this->subject->isBrowsable(), 'Capability "browsable" is not correctly set.');
270  }
271 
277  {
278  $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
279  $this->prepareSubject([]);
280  $this->assertEquals($GLOBALS['TYPO3_CONF_VARS']['SYS']['fal']['defaultFilterCallbacks'], $this->subject->getFileAndFolderNameFilters());
281  }
282 
286  public function addFileFailsIfFileDoesNotExist()
287  {
289  $mockedFolder = $this->createMock(Folder::class);
290  $this->expectException(\InvalidArgumentException::class);
291  $this->expectExceptionCode(1319552745);
292  $this->prepareSubject([]);
293  $this->subject->addFile('/some/random/file', $mockedFolder);
294  }
295 
299  public function getPublicUrlReturnsNullIfStorageIsNotOnline()
300  {
302  $driver = $this->getMockBuilder(LocalDriver::class)
303  ->setConstructorArgs([['basePath' => $this->getMountRootUrl()]])
304  ->getMock();
306  $subject = $this->getMockBuilder(ResourceStorage::class)
307  ->setMethods(['isOnline'])
308  ->setConstructorArgs([$driver, ['configuration' => []]])
309  ->getMock();
310  $subject->expects($this->once())->method('isOnline')->will($this->returnValue(false));
311 
312  $sourceFileIdentifier = '/sourceFile.ext';
313  $sourceFile = $this->getSimpleFileMock($sourceFileIdentifier);
314  $result = $subject->getPublicUrl($sourceFile);
315  $this->assertSame($result, null);
316  }
317 
324  {
325  return [
326  'read action on readable/writable folder' => [
327  'read',
328  ['r' => true, 'w' => true],
329  true
330  ],
331  'read action on unreadable folder' => [
332  'read',
333  ['r' => false, 'w' => true],
334  false
335  ],
336  'write action on read-only folder' => [
337  'write',
338  ['r' => true, 'w' => false],
339  false
340  ]
341  ];
342  }
343 
351  public function checkFolderPermissionsRespectsFilesystemPermissions($action, $permissionsFromDriver, $expectedResult)
352  {
354  $mockedDriver = $this->createMock(LocalDriver::class);
355  $mockedDriver->expects($this->any())->method('getPermissions')->will($this->returnValue($permissionsFromDriver));
357  $mockedFolder = $this->createMock(Folder::class);
358  // Let all other checks pass
360  $subject = $this->getMockBuilder(ResourceStorage::class)
361  ->setMethods(['isWritable', 'isBrowsable', 'checkUserActionPermission'])
362  ->setConstructorArgs([$mockedDriver, []])
363  ->getMock();
364  $subject->expects($this->any())->method('isWritable')->will($this->returnValue(true));
365  $subject->expects($this->any())->method('isBrowsable')->will($this->returnValue(true));
366  $subject->expects($this->any())->method('checkUserActionPermission')->will($this->returnValue(true));
367  $subject->setDriver($mockedDriver);
368 
369  $this->assertSame($expectedResult, $subject->checkFolderActionPermission($action, $mockedFolder));
370  }
371 
376  {
377  $this->prepareSubject([]);
378  $this->assertTrue($this->subject->checkUserActionPermission('read', 'folder'));
379  }
380 
385  {
386  $this->prepareSubject([]);
387  $this->subject->setUserPermissions(['readFolder' => true, 'writeFile' => true]);
388  $this->assertTrue($this->subject->checkUserActionPermission('read', 'folder'));
389  }
390 
392  {
393  return [
394  'all lower cased' => [
395  ['readFolder' => true],
396  'read',
397  'folder'
398  ],
399  'all upper case' => [
400  ['readFolder' => true],
401  'READ',
402  'FOLDER'
403  ],
404  'mixed case' => [
405  ['readFolder' => true],
406  'ReaD',
407  'FoLdEr'
408  ]
409  ];
410  }
411 
419  public function checkUserActionPermissionAcceptsArbitrarilyCasedArguments(array $permissions, $action, $type)
420  {
421  $this->prepareSubject([]);
422  $this->subject->setUserPermissions($permissions);
423  $this->assertTrue($this->subject->checkUserActionPermission($action, $type));
424  }
425 
430  {
431  $this->prepareSubject([]);
432  $this->subject->setEvaluatePermissions(true);
433  $this->subject->setUserPermissions(['readFolder' => false]);
434  $this->assertFalse($this->subject->checkUserActionPermission('read', 'folder'));
435  }
436 
441  {
442  $this->prepareSubject([]);
443  $this->subject->setEvaluatePermissions(true);
444  $this->subject->setUserPermissions(['readFolder' => true]);
445  $this->assertFalse($this->subject->checkUserActionPermission('write', 'folder'));
446  }
447 
452  {
453  $this->prepareSubject([]);
454  $this->subject->setEvaluatePermissions(false);
455  $this->assertFalse($this->subject->getEvaluatePermissions());
456  }
457 
462  {
463  $this->prepareSubject([]);
464  $this->subject->setEvaluatePermissions(true);
465  $this->assertTrue($this->subject->getEvaluatePermissions());
466  }
467 
473  public function setFileContentsUpdatesObjectProperties()
474  {
475  $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
476  $this->initializeVfs();
477  $driverObject = $this->getMockForAbstractClass(AbstractDriver::class, [], '', false);
478  $this->subject = $this->getMockBuilder(ResourceStorage::class)
479  ->setMethods(['getFileIndexRepository', 'checkFileActionPermission'])
480  ->setConstructorArgs([$driverObject, []])
481  ->getMock();
482  $this->subject->expects($this->any())->method('checkFileActionPermission')->will($this->returnValue(true));
483  $fileInfo = [
484  'storage' => 'A',
485  'identifier' => 'B',
486  'mtime' => 'C',
487  'ctime' => 'D',
488  'mimetype' => 'E',
489  'size' => 'F',
490  'name' => 'G',
491  ];
492  $newProperties = [
493  'storage' => $fileInfo['storage'],
494  'identifier' => $fileInfo['identifier'],
495  'tstamp' => $fileInfo['mtime'],
496  'crdate' => $fileInfo['ctime'],
497  'mime_type' => $fileInfo['mimetype'],
498  'size' => $fileInfo['size'],
499  'name' => $fileInfo['name']
500  ];
501  $hash = 'asdfg';
503  $mockedDriver = $this->getMockBuilder(LocalDriver::class)
504  ->setConstructorArgs([['basePath' => $this->getMountRootUrl()]])
505  ->getMock();
506  $mockedDriver->expects($this->once())->method('getFileInfoByIdentifier')->will($this->returnValue($fileInfo));
507  $mockedDriver->expects($this->once())->method('hash')->will($this->returnValue($hash));
508  $this->subject->setDriver($mockedDriver);
509  $indexFileRepositoryMock = $this->createMock(FileIndexRepository::class);
510  $this->subject->expects($this->any())->method('getFileIndexRepository')->will($this->returnValue($indexFileRepositoryMock));
512  $mockedFile = $this->createMock(File::class);
513  $mockedFile->expects($this->any())->method('getIdentifier')->will($this->returnValue($fileInfo['identifier']));
514  // called by indexer because the properties are updated
515  $this->subject->expects($this->any())->method('getFileInfoByIdentifier')->will($this->returnValue($newProperties));
516  $mockedFile->expects($this->any())->method('getStorage')->will($this->returnValue($this->subject));
517  $mockedFile->expects($this->any())->method('getProperties')->will($this->returnValue(array_keys($fileInfo)));
518  $mockedFile->expects($this->any())->method('getUpdatedProperties')->will($this->returnValue(array_keys($newProperties)));
519  // do not update directly; that's up to the indexer
520  $indexFileRepositoryMock->expects($this->never())->method('update');
521  $this->subject->setFileContents($mockedFile, $this->getUniqueId());
522  }
523 
529  public function moveFileCallsDriversMethodsWithCorrectArguments()
530  {
531  $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
532  $localFilePath = '/path/to/localFile';
533  $sourceFileIdentifier = '/sourceFile.ext';
534  $fileInfoDummy = [
535  'storage' => 'A',
536  'identifier' => 'B',
537  'mtime' => 'C',
538  'ctime' => 'D',
539  'mimetype' => 'E',
540  'size' => 'F',
541  'name' => 'G',
542  ];
543  $this->addToMount([
544  'targetFolder' => []
545  ]);
546  $this->initializeVfs();
547  $targetFolder = $this->getSimpleFolderMock('/targetFolder/');
549  $sourceDriver = $this->createMock(LocalDriver::class);
550  $sourceDriver->expects($this->once())->method('deleteFile')->with($this->equalTo($sourceFileIdentifier));
551  $configuration = $this->convertConfigurationArrayToFlexformXml([]);
552  $sourceStorage = new ResourceStorage($sourceDriver, ['configuration' => $configuration]);
553  $sourceFile = $this->getSimpleFileMock($sourceFileIdentifier);
554  $sourceFile->expects($this->once())->method('getForLocalProcessing')->will($this->returnValue($localFilePath));
555  $sourceFile->expects($this->any())->method('getStorage')->will($this->returnValue($sourceStorage));
556  $sourceFile->expects($this->once())->method('getUpdatedProperties')->will($this->returnValue(array_keys($fileInfoDummy)));
557  $sourceFile->expects($this->once())->method('getProperties')->will($this->returnValue($fileInfoDummy));
559  $mockedDriver = $this->getMockBuilder(LocalDriver::class)
560  ->setConstructorArgs([['basePath' => $this->getMountRootUrl()]])
561  ->getMock();
562  $mockedDriver->expects($this->once())->method('getFileInfoByIdentifier')->will($this->returnValue($fileInfoDummy));
563  $mockedDriver->expects($this->once())->method('addFile')->with($localFilePath, '/targetFolder/', $this->equalTo('file.ext'))->will($this->returnValue('/targetFolder/file.ext'));
565  $subject = $this->getMockBuilder(ResourceStorage::class)
566  ->setMethods(['assureFileMovePermissions'])
567  ->setConstructorArgs([$mockedDriver, ['configuration' => $configuration]])
568  ->getMock();
569  $subject->moveFile($sourceFile, $targetFolder, 'file.ext');
570  }
571 
578  {
579  $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
580  $mockedFile = $this->getSimpleFileMock('/mountFolder/file');
581  $this->addToMount([
582  'mountFolder' => [
583  'file' => 'asdfg'
584  ]
585  ]);
586  $mockedDriver = $this->createDriverMock(['basePath' => $this->getMountRootUrl()], null, null);
587  $this->initializeVfs();
588  $this->prepareSubject([], null, $mockedDriver);
589  $this->subject->addFileMount('/mountFolder');
590  $this->assertEquals(1, count($this->subject->getFileMounts()));
591  $this->subject->isWithinFileMountBoundaries($mockedFile);
592  }
593 
599  {
600  $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
601  $mockedParentFolder = $this->getSimpleFolderMock('/someFolder/');
602  $mockedDriver = $this->createDriverMock([]);
603  $mockedDriver->expects($this->once())->method('folderExists')->with($this->equalTo('/someFolder/'))->will($this->returnValue(true));
604  $mockedDriver->expects($this->once())->method('createFolder')->with($this->equalTo('newFolder'))->will($this->returnValue($mockedParentFolder));
605  $this->prepareSubject([], true);
606  $this->subject->setDriver($mockedDriver);
607  $this->subject->createFolder('newFolder', $mockedParentFolder);
608  }
609 
613  public function deleteFolderThrowsExceptionIfFolderIsNotEmptyAndRecursiveDeleteIsDisabled()
614  {
615  $this->expectException(\RuntimeException::class);
616  $this->expectExceptionCode(1325952534);
617 
619  $folderMock = $this->createMock(Folder::class);
621  $mockedDriver = $this->getMockForAbstractClass(AbstractDriver::class);
622  $mockedDriver->expects($this->once())->method('isFolderEmpty')->will($this->returnValue(false));
624  $subject = $this->getAccessibleMock(ResourceStorage::class, ['checkFolderActionPermission'], [], '', false);
625  $subject->expects($this->any())->method('checkFolderActionPermission')->will($this->returnValue(true));
626  $subject->_set('driver', $mockedDriver);
627  $subject->deleteFolder($folderMock, false);
628  }
629 
635  {
636  $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
637  $mockedParentFolder = $this->getSimpleFolderMock('/someFolder/');
638  $this->prepareSubject([], true);
639  $mockedDriver = $this->createDriverMock([], $this->subject);
640  $mockedDriver->expects($this->once())->method('createFolder')->with($this->equalTo('newFolder'), $this->equalTo('/someFolder/'))->will($this->returnValue(true));
641  $mockedDriver->expects($this->once())->method('folderExists')->with($this->equalTo('/someFolder/'))->will($this->returnValue(true));
642  $this->subject->createFolder('newFolder', $mockedParentFolder);
643  }
644 
650  {
651  $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
652  $this->addToMount(['someFolder' => []]);
653  $mockedDriver = $this->createDriverMock(['basePath' => $this->getMountRootUrl()], null, null);
654  $this->prepareSubject([], true, $mockedDriver);
655  $parentFolder = $this->subject->getFolder('/someFolder/');
656  $newFolder = $this->subject->createFolder('subFolder/secondSubfolder', $parentFolder);
657  $this->assertEquals('secondSubfolder', $newFolder->getName());
658  $this->assertFileExists($this->getUrlInMount('/someFolder/subFolder/'));
659  $this->assertFileExists($this->getUrlInMount('/someFolder/subFolder/secondSubfolder/'));
660  }
661 
667  {
668  $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
669  $this->prepareSubject([], true);
670  $mockedDriver = $this->createDriverMock([], $this->subject);
671  $mockedDriver->expects($this->once())->method('getRootLevelFolder')->with()->will($this->returnValue('/'));
672  $mockedDriver->expects($this->once())->method('createFolder')->with($this->equalTo('someFolder'));
673  $this->subject->createFolder('someFolder');
674  }
675 
681  {
682  $this->markTestSkipped('This test does way to much and is mocked incomplete. Skipped for now.');
683  $this->addToMount([
684  'existingFolder' => []
685  ]);
686  $this->initializeVfs();
687  $mockedDriver = $this->createDriverMock(['basePath' => $this->getMountRootUrl()], null, null);
688  $this->prepareSubject([], true, $mockedDriver);
689  $rootFolder = $this->subject->getFolder('/');
690  $newFolder = $this->subject->createFolder('existingFolder/someFolder', $rootFolder);
691  $this->assertEquals('someFolder', $newFolder->getName());
692  $this->assertFileExists($this->getUrlInMount('existingFolder/someFolder'));
693  }
694 
699  {
700  $this->expectException(\InvalidArgumentException::class);
701  $this->expectExceptionCode(1325689164);
702  $mockedParentFolder = $this->getSimpleFolderMock('/someFolder/');
703  $this->prepareSubject([], true);
704  $mockedDriver = $this->createDriverMock([], $this->subject);
705  $mockedDriver->expects($this->once())->method('folderExists')->with($this->equalTo('/someFolder/'))->will($this->returnValue(false));
706  $this->subject->createFolder('newFolder', $mockedParentFolder);
707  }
708 }
prepareSubject(array $configuration, $mockPermissionChecks=false, AbstractDriver $driverObject=null, array $storageRecord=[])
checkUserActionPermissionAcceptsArbitrarilyCasedArguments(array $permissions, $action, $type)
static array2xml(array $array, $NSprefix= '', $level=0, $docTag= 'phparray', $spaceInd=0, array $options=[], array $stackData=[])
createDriverMock($driverConfiguration, ResourceStorage $storageObject=null, $mockedDriverMethods=[])
fileExtensionPermissionIsWorkingCorrectly($fileName, array $configuration, $evaluatePermissions, $isAllowed)
static setSingletonInstance($className, SingletonInterface $instance)
static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
static resetSingletonInstances(array $newSingletonInstances)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
getAccessibleMock($originalClassName, $methods=[], array $arguments=[], $mockClassName= '', $callOriginalConstructor=true, $callOriginalClone=true, $callAutoload=true)