diff options
author | Robin Appelman <robin@icewind.nl> | 2024-11-28 19:08:27 +0100 |
---|---|---|
committer | Andy Scherzinger <info@andy-scherzinger.de> | 2025-02-05 09:08:58 +0100 |
commit | 68829ad47c15ae819e5d87cc4cc7d8fc8f220e12 (patch) | |
tree | 7199665a139801be8bebca0f62c90d51a56f35bf | |
parent | 6118649d548ab4caf4efda7740ee813506b8ff84 (diff) | |
download | nextcloud-server-68829ad47c15ae819e5d87cc4cc7d8fc8f220e12.tar.gz nextcloud-server-68829ad47c15ae819e5d87cc4cc7d8fc8f220e12.zip |
fix: improve checks for moving shares/storages into other mounts
Signed-off-by: Robin Appelman <robin@icewind.nl>
-rw-r--r-- | lib/private/Files/View.php | 66 | ||||
-rw-r--r-- | tests/lib/Files/ViewTest.php | 51 |
2 files changed, 93 insertions, 24 deletions
diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php index 738e1442b67..37842ce02aa 100644 --- a/lib/private/Files/View.php +++ b/lib/private/Files/View.php @@ -24,6 +24,7 @@ use OCP\Files\ForbiddenException; use OCP\Files\InvalidCharacterInPathException; use OCP\Files\InvalidDirectoryException; use OCP\Files\InvalidPathException; +use OCP\Files\Mount\IMountManager; use OCP\Files\Mount\IMountPoint; use OCP\Files\NotFoundException; use OCP\Files\ReservedWordException; @@ -700,6 +701,9 @@ class View { throw new ForbiddenException("Moving a folder into a child folder is forbidden", false); } + /** @var IMountManager $mountManager */ + $mountManager = \OC::$server->get(IMountManager::class); + $targetParts = explode('/', $absolutePath2); $targetUser = $targetParts[1] ?? null; $result = false; @@ -757,24 +761,25 @@ class View { try { $this->changeLock($target, ILockingProvider::LOCK_EXCLUSIVE, true); + $movedMounts = $mountManager->findIn($this->getAbsolutePath($source)); + if ($internalPath1 === '') { - if ($mount1 instanceof MoveableMount) { - $sourceParentMount = $this->getMount(dirname($source)); - if ($sourceParentMount === $mount2 && $this->targetIsNotShared($targetUser, $absolutePath2)) { - /** - * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1 - */ - $sourceMountPoint = $mount1->getMountPoint(); - $result = $mount1->moveMount($absolutePath2); - $manager->moveMount($sourceMountPoint, $mount1->getMountPoint()); - } else { - $result = false; - } - } else { - $result = false; - } + $sourceParentMount = $this->getMount(dirname($source)); + $movedMounts[] = $mount1; + $this->validateMountMove($movedMounts, $sourceParentMount, $mount2, !$this->targetIsNotShared($targetUser, $absolutePath2)); + + /** + * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1 + */ + $sourceMountPoint = $mount1->getMountPoint(); + $result = $mount1->moveMount($absolutePath2); + $manager->moveMount($sourceMountPoint, $mount1->getMountPoint()); + // moving a file/folder within the same mount point } elseif ($storage1 === $storage2) { + if (count($movedMounts) > 0) { + $this->validateMountMove($movedMounts, $mount1, $mount2, !$this->targetIsNotShared($targetUser, $absolutePath2)); + } if ($storage1) { $result = $storage1->rename($internalPath1, $internalPath2); } else { @@ -782,6 +787,9 @@ class View { } // moving a file/folder between storages (from $storage1 to $storage2) } else { + if (count($movedMounts) > 0) { + $this->validateMountMove($movedMounts, $mount1, $mount2, !$this->targetIsNotShared($targetUser, $absolutePath2)); + } $result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2); } @@ -831,6 +839,34 @@ class View { return $result; } + private function validateMountMove(array $mounts, IMountPoint $sourceMount, IMountPoint $targetMount, bool $targetIsShared): void { + $targetType = 'storage'; + if ($targetMount instanceof SharedMount) { + $targetType = 'share'; + } + $targetPath = rtrim($targetMount->getMountPoint(), '/'); + + foreach ($mounts as $mount) { + $sourcePath = rtrim($mount->getMountPoint(), '/'); + $sourceType = 'storage'; + if ($mount instanceof SharedMount) { + $sourceType = 'share'; + } + + if (!$mount instanceof MoveableMount) { + throw new ForbiddenException("Storage {$sourcePath} cannot be moved", false); + } + + if ($targetIsShared) { + throw new ForbiddenException("Moving a $sourceType ($sourcePath) into shared folder is not allowed", false); + } + + if ($sourceMount !== $targetMount) { + throw new ForbiddenException("Moving a $sourceType ($sourcePath) into another $targetType ($targetPath) is not allowed", false); + } + } + } + /** * Copy a file/folder from the source path to target path * diff --git a/tests/lib/Files/ViewTest.php b/tests/lib/Files/ViewTest.php index 2dfbf61641a..f20c9f05a22 100644 --- a/tests/lib/Files/ViewTest.php +++ b/tests/lib/Files/ViewTest.php @@ -20,6 +20,7 @@ use OCP\Cache\CappedMemoryCache; use OCP\Constants; use OCP\Files\Config\IMountProvider; use OCP\Files\FileInfo; +use OCP\Files\ForbiddenException; use OCP\Files\GenericFileException; use OCP\Files\Mount\IMountManager; use OCP\Files\Storage\IStorage; @@ -1639,10 +1640,27 @@ class ViewTest extends \Test\TestCase { $this->assertTrue($view->rename('mount2', 'sub/moved_mount'), 'Can move a mount point into a subdirectory'); } - /** - * Test that moving a mount point into another is forbidden - */ - public function testMoveMountPointIntoAnother() { + public function testMoveMountPointOverwrite(): void { + self::loginAsUser($this->user); + + [$mount1, $mount2] = $this->createTestMovableMountPoints([ + $this->user . '/files/mount1', + $this->user . '/files/mount2', + ]); + + $mount1->expects($this->never()) + ->method('moveMount'); + + $mount2->expects($this->never()) + ->method('moveMount'); + + $view = new View('/' . $this->user . '/files/'); + + $this->expectException(ForbiddenException::class); + $view->rename('mount1', 'mount2'); + } + + public function testMoveMountPointIntoMount(): void { self::loginAsUser($this->user); [$mount1, $mount2] = $this->createTestMovableMountPoints([ @@ -1658,8 +1676,8 @@ class ViewTest extends \Test\TestCase { $view = new View('/' . $this->user . '/files/'); - $this->assertFalse($view->rename('mount1', 'mount2'), 'Cannot overwrite another mount point'); - $this->assertFalse($view->rename('mount1', 'mount2/sub'), 'Cannot move a mount point into another'); + $this->expectException(ForbiddenException::class); + $view->rename('mount1', 'mount2/sub'); } /** @@ -1701,9 +1719,24 @@ class ViewTest extends \Test\TestCase { ->setNode($shareDir); $shareManager->createShare($share); - $this->assertFalse($view->rename('mount1', 'shareddir'), 'Cannot overwrite shared folder'); - $this->assertFalse($view->rename('mount1', 'shareddir/sub'), 'Cannot move mount point into shared folder'); - $this->assertFalse($view->rename('mount1', 'shareddir/sub/sub2'), 'Cannot move mount point into shared subfolder'); + try { + $view->rename('mount1', 'shareddir'); + $this->fail('Cannot overwrite shared folder'); + } catch (ForbiddenException $e) { + + } + try { + $view->rename('mount1', 'shareddir/sub'); + $this->fail('Cannot move mount point into shared folder'); + } catch (ForbiddenException $e) { + + } + try { + $view->rename('mount1', 'shareddir/sub/sub2'); + $this->fail('Cannot move mount point into shared subfolder'); + } catch (ForbiddenException $e) { + + } $this->assertTrue($view->rename('mount2', 'shareddir notshared/sub'), 'Can move mount point into a similarly named but non-shared folder'); $shareManager->deleteShare($share); |