diff --git a/apps/dav/appinfo/v1/publicwebdav.php b/apps/dav/appinfo/v1/publicwebdav.php index 7b576496cc89f..316a84d9767ee 100644 --- a/apps/dav/appinfo/v1/publicwebdav.php +++ b/apps/dav/appinfo/v1/publicwebdav.php @@ -6,7 +6,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ use OC\Files\Filesystem; -use OC\Files\Storage\Wrapper\PermissionsMask; +use OC\Files\Storage\Wrapper\DirPermissionsMask; use OC\Files\View; use OCA\DAV\Connector\LegacyPublicAuth; use OCA\DAV\Connector\Sabre\ServerFactory; @@ -98,7 +98,11 @@ function (\Sabre\DAV\Server $server) use ( // FIXME: should not add storage wrappers outside of preSetup, need to find a better way $previousLog = Filesystem::logWarningWhenAddingStorageWrapper(false); Filesystem::addStorageWrapper('sharePermissions', function ($mountPoint, $storage) use ($share) { - return new PermissionsMask(['storage' => $storage, 'mask' => $share->getPermissions() | Constants::PERMISSION_SHARE]); + return new DirPermissionsMask([ + 'storage' => $storage, + 'mask' => $share->getPermissions() | Constants::PERMISSION_SHARE, + 'path' => 'files' + ]); }); Filesystem::addStorageWrapper('shareOwner', function ($mountPoint, $storage) use ($share) { return new PublicOwnerWrapper(['storage' => $storage, 'owner' => $share->getShareOwner()]); diff --git a/apps/files_external/lib/Lib/SessionStorageWrapper.php b/apps/files_external/lib/Lib/SessionStorageWrapper.php index 8754041b2fa39..17d0f5dbfb3ed 100644 --- a/apps/files_external/lib/Lib/SessionStorageWrapper.php +++ b/apps/files_external/lib/Lib/SessionStorageWrapper.php @@ -9,13 +9,14 @@ use OC\Files\Storage\Wrapper\PermissionsMask; use OCP\Constants; +use OCP\Files\Storage\IStorage; /** * Wrap Storage in PermissionsMask for session ephemeral use */ class SessionStorageWrapper extends PermissionsMask { /** - * @param array $parameters ['storage' => $storage] + * @param array{storage: IStorage, ...} $parameters */ public function __construct(array $parameters) { // disable sharing permission diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index ba1487488fda9..3817e8ba9ce90 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1725,6 +1725,7 @@ 'OC\\Files\\Cache\\StorageGlobal' => $baseDir . '/lib/private/Files/Cache/StorageGlobal.php', 'OC\\Files\\Cache\\Updater' => $baseDir . '/lib/private/Files/Cache/Updater.php', 'OC\\Files\\Cache\\Watcher' => $baseDir . '/lib/private/Files/Cache/Watcher.php', + 'OC\\Files\\Cache\\Wrapper\\CacheDirPermissionsMask' => $baseDir . '/lib/private/Files/Cache/Wrapper/CacheDirPermissionsMask.php', 'OC\\Files\\Cache\\Wrapper\\CacheJail' => $baseDir . '/lib/private/Files/Cache/Wrapper/CacheJail.php', 'OC\\Files\\Cache\\Wrapper\\CachePermissionsMask' => $baseDir . '/lib/private/Files/Cache/Wrapper/CachePermissionsMask.php', 'OC\\Files\\Cache\\Wrapper\\CacheWrapper' => $baseDir . '/lib/private/Files/Cache/Wrapper/CacheWrapper.php', @@ -1810,6 +1811,7 @@ 'OC\\Files\\Storage\\StorageFactory' => $baseDir . '/lib/private/Files/Storage/StorageFactory.php', 'OC\\Files\\Storage\\Temporary' => $baseDir . '/lib/private/Files/Storage/Temporary.php', 'OC\\Files\\Storage\\Wrapper\\Availability' => $baseDir . '/lib/private/Files/Storage/Wrapper/Availability.php', + 'OC\\Files\\Storage\\Wrapper\\DirPermissionsMask' => $baseDir . '/lib/private/Files/Storage/Wrapper/DirPermissionsMask.php', 'OC\\Files\\Storage\\Wrapper\\Encoding' => $baseDir . '/lib/private/Files/Storage/Wrapper/Encoding.php', 'OC\\Files\\Storage\\Wrapper\\EncodingDirectoryWrapper' => $baseDir . '/lib/private/Files/Storage/Wrapper/EncodingDirectoryWrapper.php', 'OC\\Files\\Storage\\Wrapper\\Encryption' => $baseDir . '/lib/private/Files/Storage/Wrapper/Encryption.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 993dbfc52e930..a7883e54f7dab 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1766,6 +1766,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Files\\Cache\\StorageGlobal' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/StorageGlobal.php', 'OC\\Files\\Cache\\Updater' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Updater.php', 'OC\\Files\\Cache\\Watcher' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Watcher.php', + 'OC\\Files\\Cache\\Wrapper\\CacheDirPermissionsMask' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Wrapper/CacheDirPermissionsMask.php', 'OC\\Files\\Cache\\Wrapper\\CacheJail' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Wrapper/CacheJail.php', 'OC\\Files\\Cache\\Wrapper\\CachePermissionsMask' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Wrapper/CachePermissionsMask.php', 'OC\\Files\\Cache\\Wrapper\\CacheWrapper' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Wrapper/CacheWrapper.php', @@ -1851,6 +1852,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Files\\Storage\\StorageFactory' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/StorageFactory.php', 'OC\\Files\\Storage\\Temporary' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Temporary.php', 'OC\\Files\\Storage\\Wrapper\\Availability' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Availability.php', + 'OC\\Files\\Storage\\Wrapper\\DirPermissionsMask' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/DirPermissionsMask.php', 'OC\\Files\\Storage\\Wrapper\\Encoding' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Encoding.php', 'OC\\Files\\Storage\\Wrapper\\EncodingDirectoryWrapper' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/EncodingDirectoryWrapper.php', 'OC\\Files\\Storage\\Wrapper\\Encryption' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Encryption.php', diff --git a/lib/private/Files/Cache/Wrapper/CacheDirPermissionsMask.php b/lib/private/Files/Cache/Wrapper/CacheDirPermissionsMask.php new file mode 100644 index 0000000000000..38fe5a88c419c --- /dev/null +++ b/lib/private/Files/Cache/Wrapper/CacheDirPermissionsMask.php @@ -0,0 +1,36 @@ +checkPath; + if ($checkPath($entry['path'])) { + return parent::formatCacheEntry($entry); + } + + return $entry; + } +} diff --git a/lib/private/Files/Storage/Wrapper/DirPermissionsMask.php b/lib/private/Files/Storage/Wrapper/DirPermissionsMask.php new file mode 100644 index 0000000000000..14b6441f98b67 --- /dev/null +++ b/lib/private/Files/Storage/Wrapper/DirPermissionsMask.php @@ -0,0 +1,193 @@ +path = rtrim((string)$parameters['path'], '/'); + $this->pathLength = strlen((string)$parameters['path']); + } + + protected function checkPath(string $path): bool { + return $path === $this->path || substr($path, 0, $this->pathLength + 1) === $this->path . '/'; + } + + public function isUpdatable($path): bool { + if ($this->checkPath($path)) { + return parent::isUpdatable($path); + } + + return $this->storage->isUpdatable($path); + } + + public function isCreatable($path): bool { + if ($this->checkPath($path)) { + return parent::isCreatable($path); + } + + return $this->storage->isCreatable($path); + } + + public function isDeletable($path): bool { + if ($this->checkPath($path)) { + return parent::isDeletable($path); + } + + return $this->storage->isDeletable($path); + } + + public function isSharable($path): bool { + if ($this->checkPath($path)) { + return parent::isSharable($path); + } + + return $this->storage->isSharable($path); + } + + public function getPermissions($path): int { + if ($this->checkPath($path)) { + return parent::getPermissions($path); + } + + return $this->storage->getPermissions($path); + } + + public function rename($source, $target): bool { + if (!$this->isUpdatable($source)) { + return false; + } + + if ($this->file_exists($target)) { + if ($this->isUpdatable($target)) { + return $this->storage->rename($source, $target); + } + } else { + $parent = dirname($target); + if ($parent === '.') { + $parent = ''; + } + + if ($this->isCreatable($parent)) { + return $this->storage->rename($source, $target); + } + } + + return false; + } + + public function copy($source, $target): bool { + if (!$this->isReadable($source)) { + return false; + } + + if ($this->file_exists($target)) { + if ($this->isUpdatable($target)) { + return $this->storage->copy($source, $target); + } + } else { + $parent = dirname($target); + if ($parent === '.') { + $parent = ''; + } + + if ($this->isCreatable($parent)) { + return $this->storage->copy($source, $target); + } + } + + return false; + } + + public function touch($path, $mtime = null): bool { + if ($this->checkPath($path)) { + return parent::touch($path); + } + + return $this->storage->touch($path); + } + + public function mkdir($path): bool { + // Always allow creating the path of the dir mask. + if ($path !== $this->path && $this->checkPath($path)) { + return parent::mkdir($path); + } + + return $this->storage->mkdir($path); + } + + public function rmdir($path): bool { + if ($this->checkPath($path)) { + return parent::rmdir($path); + } + + return $this->storage->rmdir($path); + } + + public function unlink($path): bool { + if ($this->checkPath($path)) { + return parent::unlink($path); + } + + return $this->storage->unlink($path); + } + + public function file_put_contents($path, $data): int|float|false { + if ($this->checkPath($path)) { + return parent::file_put_contents($path, $data); + } + + return $this->storage->file_put_contents($path, $data); + } + + public function fopen($path, $mode) { + if ($this->checkPath($path)) { + return parent::fopen($path, $mode); + } + + return $this->storage->fopen($path, $mode); + } + + public function getCache($path = '', $storage = null): ICache { + if (!$storage) { + $storage = $this; + } + + $sourceCache = $this->storage->getCache($path, $storage); + return new CacheDirPermissionsMask($sourceCache, $this->mask, $this->checkPath(...)); + } +} diff --git a/lib/private/Files/Storage/Wrapper/PermissionsMask.php b/lib/private/Files/Storage/Wrapper/PermissionsMask.php index 692824d00bdd5..d14a484bc05bb 100644 --- a/lib/private/Files/Storage/Wrapper/PermissionsMask.php +++ b/lib/private/Files/Storage/Wrapper/PermissionsMask.php @@ -8,6 +8,7 @@ namespace OC\Files\Storage\Wrapper; use OC\Files\Cache\Wrapper\CachePermissionsMask; +use OC\Files\Storage\Storage; use OCP\Constants; use OCP\Files\Storage\IStorage; @@ -22,10 +23,10 @@ class PermissionsMask extends Wrapper { /** * @var int the permissions bits we want to keep */ - private $mask; + protected readonly int $mask; /** - * @param array $parameters ['storage' => $storage, 'mask' => $mask] + * @param array{storage: Storage, mask: int, ...} $parameters * * $storage: The storage the permissions mask should be applied on * $mask: The permission bits that should be kept, a combination of the \OCP\Constant::PERMISSION_ constants