Skip to content

Commit be89374

Browse files
committed
fix: improve checks for moving shares/storages into other mounts
Signed-off-by: Robin Appelman <robin@icewind.nl>
1 parent dd101dd commit be89374

File tree

2 files changed

+93
-24
lines changed

2 files changed

+93
-24
lines changed

lib/private/Files/View.php

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use OCP\Files\InvalidCharacterInPathException;
2525
use OCP\Files\InvalidDirectoryException;
2626
use OCP\Files\InvalidPathException;
27+
use OCP\Files\Mount\IMountManager;
2728
use OCP\Files\Mount\IMountPoint;
2829
use OCP\Files\NotFoundException;
2930
use OCP\Files\ReservedWordException;
@@ -701,6 +702,9 @@ public function rename($source, $target) {
701702
throw new ForbiddenException('Moving a folder into a child folder is forbidden', false);
702703
}
703704

705+
/** @var IMountManager $mountManager */
706+
$mountManager = \OC::$server->get(IMountManager::class);
707+
704708
$targetParts = explode('/', $absolutePath2);
705709
$targetUser = $targetParts[1] ?? null;
706710
$result = false;
@@ -758,31 +762,35 @@ public function rename($source, $target) {
758762
try {
759763
$this->changeLock($target, ILockingProvider::LOCK_EXCLUSIVE, true);
760764

765+
$movedMounts = $mountManager->findIn($this->getAbsolutePath($source));
766+
761767
if ($internalPath1 === '') {
762-
if ($mount1 instanceof MoveableMount) {
763-
$sourceParentMount = $this->getMount(dirname($source));
764-
if ($sourceParentMount === $mount2 && $this->targetIsNotShared($targetUser, $absolutePath2)) {
765-
/**
766-
* @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1
767-
*/
768-
$sourceMountPoint = $mount1->getMountPoint();
769-
$result = $mount1->moveMount($absolutePath2);
770-
$manager->moveMount($sourceMountPoint, $mount1->getMountPoint());
771-
} else {
772-
$result = false;
773-
}
774-
} else {
775-
$result = false;
776-
}
768+
$sourceParentMount = $this->getMount(dirname($source));
769+
$movedMounts[] = $mount1;
770+
$this->validateMountMove($movedMounts, $sourceParentMount, $mount2, !$this->targetIsNotShared($targetUser, $absolutePath2));
771+
772+
/**
773+
* @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1
774+
*/
775+
$sourceMountPoint = $mount1->getMountPoint();
776+
$result = $mount1->moveMount($absolutePath2);
777+
$manager->moveMount($sourceMountPoint, $mount1->getMountPoint());
778+
777779
// moving a file/folder within the same mount point
778780
} elseif ($storage1 === $storage2) {
781+
if (count($movedMounts) > 0) {
782+
$this->validateMountMove($movedMounts, $mount1, $mount2, !$this->targetIsNotShared($targetUser, $absolutePath2));
783+
}
779784
if ($storage1) {
780785
$result = $storage1->rename($internalPath1, $internalPath2);
781786
} else {
782787
$result = false;
783788
}
784789
// moving a file/folder between storages (from $storage1 to $storage2)
785790
} else {
791+
if (count($movedMounts) > 0) {
792+
$this->validateMountMove($movedMounts, $mount1, $mount2, !$this->targetIsNotShared($targetUser, $absolutePath2));
793+
}
786794
$result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
787795
}
788796

@@ -832,6 +840,34 @@ public function rename($source, $target) {
832840
return $result;
833841
}
834842

843+
private function validateMountMove(array $mounts, IMountPoint $sourceMount, IMountPoint $targetMount, bool $targetIsShared): void {
844+
$targetType = 'storage';
845+
if ($targetMount instanceof SharedMount) {
846+
$targetType = 'share';
847+
}
848+
$targetPath = rtrim($targetMount->getMountPoint(), '/');
849+
850+
foreach ($mounts as $mount) {
851+
$sourcePath = rtrim($mount->getMountPoint(), '/');
852+
$sourceType = 'storage';
853+
if ($mount instanceof SharedMount) {
854+
$sourceType = 'share';
855+
}
856+
857+
if (!$mount instanceof MoveableMount) {
858+
throw new ForbiddenException("Storage {$sourcePath} cannot be moved", false);
859+
}
860+
861+
if ($targetIsShared) {
862+
throw new ForbiddenException("Moving a $sourceType ($sourcePath) into shared folder is not allowed", false);
863+
}
864+
865+
if ($sourceMount !== $targetMount) {
866+
throw new ForbiddenException("Moving a $sourceType ($sourcePath) into another $targetType ($targetPath) is not allowed", false);
867+
}
868+
}
869+
}
870+
835871
/**
836872
* Copy a file/folder from the source path to target path
837873
*

tests/lib/Files/ViewTest.php

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use OCP\Constants;
2222
use OCP\Files\Config\IMountProvider;
2323
use OCP\Files\FileInfo;
24+
use OCP\Files\ForbiddenException;
2425
use OCP\Files\GenericFileException;
2526
use OCP\Files\Mount\IMountManager;
2627
use OCP\Files\Storage\IStorage;
@@ -1641,10 +1642,27 @@ public function testMountPointMove(): void {
16411642
$this->assertTrue($view->rename('mount2', 'sub/moved_mount'), 'Can move a mount point into a subdirectory');
16421643
}
16431644

1644-
/**
1645-
* Test that moving a mount point into another is forbidden
1646-
*/
1647-
public function testMoveMountPointIntoAnother(): void {
1645+
public function testMoveMountPointOverwrite(): void {
1646+
self::loginAsUser($this->user);
1647+
1648+
[$mount1, $mount2] = $this->createTestMovableMountPoints([
1649+
$this->user . '/files/mount1',
1650+
$this->user . '/files/mount2',
1651+
]);
1652+
1653+
$mount1->expects($this->never())
1654+
->method('moveMount');
1655+
1656+
$mount2->expects($this->never())
1657+
->method('moveMount');
1658+
1659+
$view = new View('/' . $this->user . '/files/');
1660+
1661+
$this->expectException(ForbiddenException::class);
1662+
$view->rename('mount1', 'mount2');
1663+
}
1664+
1665+
public function testMoveMountPointIntoMount(): void {
16481666
self::loginAsUser($this->user);
16491667

16501668
[$mount1, $mount2] = $this->createTestMovableMountPoints([
@@ -1660,8 +1678,8 @@ public function testMoveMountPointIntoAnother(): void {
16601678

16611679
$view = new View('/' . $this->user . '/files/');
16621680

1663-
$this->assertFalse($view->rename('mount1', 'mount2'), 'Cannot overwrite another mount point');
1664-
$this->assertFalse($view->rename('mount1', 'mount2/sub'), 'Cannot move a mount point into another');
1681+
$this->expectException(ForbiddenException::class);
1682+
$view->rename('mount1', 'mount2/sub');
16651683
}
16661684

16671685
/**
@@ -1703,9 +1721,24 @@ public function testMoveMountPointIntoSharedFolder(): void {
17031721
->setNode($shareDir);
17041722
$shareManager->createShare($share);
17051723

1706-
$this->assertFalse($view->rename('mount1', 'shareddir'), 'Cannot overwrite shared folder');
1707-
$this->assertFalse($view->rename('mount1', 'shareddir/sub'), 'Cannot move mount point into shared folder');
1708-
$this->assertFalse($view->rename('mount1', 'shareddir/sub/sub2'), 'Cannot move mount point into shared subfolder');
1724+
try {
1725+
$view->rename('mount1', 'shareddir');
1726+
$this->fail('Cannot overwrite shared folder');
1727+
} catch (ForbiddenException $e) {
1728+
1729+
}
1730+
try {
1731+
$view->rename('mount1', 'shareddir/sub');
1732+
$this->fail('Cannot move mount point into shared folder');
1733+
} catch (ForbiddenException $e) {
1734+
1735+
}
1736+
try {
1737+
$view->rename('mount1', 'shareddir/sub/sub2');
1738+
$this->fail('Cannot move mount point into shared subfolder');
1739+
} catch (ForbiddenException $e) {
1740+
1741+
}
17091742
$this->assertTrue($view->rename('mount2', 'shareddir notshared/sub'), 'Can move mount point into a similarly named but non-shared folder');
17101743

17111744
$shareManager->deleteShare($share);

0 commit comments

Comments
 (0)