diff --git a/lib/Filesystem/CacheWrapper.php b/lib/Filesystem/CacheWrapper.php index da6e32537a1603509f851e699e253113ae32d3fc..21bc60efd9f6bd387bcf15d161d77e9a55ff4a04 100644 --- a/lib/Filesystem/CacheWrapper.php +++ b/lib/Filesystem/CacheWrapper.php @@ -8,50 +8,94 @@ use OC\Files\Cache\Wrapper\CacheWrapper as Wrapper; use OCP\Constants; use OCP\Files\Cache\ICache; use OCP\Files\Cache\ICacheEntry; -use OCP\Files\ForbiddenException; use OCP\Files\Search\ISearchQuery; class CacheWrapper extends Wrapper { + private string $excludedFolder = 'files/Recovery'; + public function __construct( ICache $cache ) { parent::__construct($cache); - $this->mask = Constants::PERMISSION_ALL - & ~Constants::PERMISSION_READ - & ~Constants::PERMISSION_CREATE - & ~Constants::PERMISSION_UPDATE - & ~Constants::PERMISSION_DELETE; + $this->mask = Constants::PERMISSION_READ; } + /** + * Format the cache entry to check access and adjust permissions. + */ protected function formatCacheEntry($entry) { if (isset($entry['path']) && isset($entry['permissions'])) { - try { - throw new ForbiddenException('Access denied', false); - } catch (ForbiddenException) { - $entry['permissions'] &= $this->mask; + // Only restrict permissions for files in the "Recovery" folder + if ($this->isExcludedPath($entry['path'])) { + try { + throw new \OC\ServiceUnavailableException('Service unavailable'); + } catch (\OC\ServiceUnavailableException $e) { + $entry['permissions'] &= $this->mask; + } } } return $entry; } + /** + * Prevent inserting into the cache for "Recovery" folder. + */ public function insert($file, $data) { - throw new \Exception('User data cache insert is disabled.'); + if ($this->isExcludedPath($file)) { + throw new \OC\ServiceUnavailableException('Service unavailable'); + } + return parent::insert($file, $data); // Normal insert for other paths } + /** + * Prevent updating cache for files in the "Recovery" folder. + */ public function update($id, $data) { - throw new \Exception('User data cache update is disabled.'); + if (isset($data['path']) && $data['path'] !== null && $this->isExcludedPath($data['path'])) { + throw new \OC\ServiceUnavailableException('Service unavailable'); + } + return parent::update($id, $data); // Normal update for other paths } + /** + * Prevent removal from cache for files in the "Recovery" folder. + */ public function remove($fileId) { - throw new \Exception('User data cache removal is disabled.'); + $filePath = $this->storage->getPath($fileId); + if ($this->isExcludedPath($filePath)) { + throw new \OC\ServiceUnavailableException('Service unavailable'); + } + return parent::remove($fileId); // Normal removal for other paths } + /** + * Exclude specific folder and its files from search results. + */ public function searchQuery(ISearchQuery $searchQuery) { - return []; + $results = parent::searchQuery($searchQuery); + return array_filter($results, function ($entry) { + return isset($entry['path']) && !$this->isExcludedPath($entry['path']); + }); } + /** + * Filter out "Recovery" folder from cache search results. + */ public function getCacheEntryFromSearchResult(ICacheEntry $rawEntry): ?ICacheEntry { - return null; + if ($this->isExcludedPath($rawEntry->getPath())) { + return null; + } + return parent::getCacheEntryFromSearchResult($rawEntry); + } + + /** + * Check if a path is within the excluded folder (e.g., "Recovery"). + */ + private function isExcludedPath(string $path): bool { + if (empty($path)) { + return false; + } + return strpos($path, $this->excludedFolder) === 0; } } diff --git a/lib/Filesystem/StorageWrapper.php b/lib/Filesystem/StorageWrapper.php index f35a55105be582163fcfdbfbe44eab87292767be..db5523853622cb875f7a3120fab775ab7f14a9b2 100644 --- a/lib/Filesystem/StorageWrapper.php +++ b/lib/Filesystem/StorageWrapper.php @@ -1,214 +1,147 @@ mask = Constants::PERMISSION_READ; } /** * @throws ForbiddenException */ - protected function checkFileAccess(string $path, bool $isDir = false): void { - throw new ForbiddenException('Access denied', false); - } + protected function checkFileAccess(string $path, ?bool $isDir = null, bool $requestToCheckModifiable = false): void { + if ($this->isRecoveryFolder($path, $requestToCheckModifiable)) { + // Block access to the "Recovery" folder + throw new \OC\ServiceUnavailableException('Service unavailable'); + // throw new StorageNotAvailableException('Service unavailable'); + } - /* - * Storage wrapper methods - */ + // If you need additional access checks for other folders, you can add here. + } /** - * see http://php.net/manual/en/function.mkdir.php + * Check if the path refers to the "Recovery" folder. * * @param string $path * @return bool - * @throws ForbiddenException */ - public function mkdir($path) { - $this->checkFileAccess($path, true); + private function isRecoveryFolder(string $path, bool $requestToCheckModifiable = false): bool { + // Ensure the path matches exactly or starts with "files/Recovery" + if($requestToCheckModifiable) { + return (strpos($path, '/' . self::RECOVERY_FOLDER) === 0 || strpos($path, self::RECOVERY_FOLDER) === 0); + } + + return strpos($path, '/' . self::RECOVERY_FOLDER) === 0; } - /** - * see http://php.net/manual/en/function.rmdir.php - * - * @param string $path - * @return bool - * @throws ForbiddenException + /* + * Storage wrapper methods */ - public function rmdir($path) { + + public function mkdir($path): bool { $this->checkFileAccess($path, true); + return $this->storage->mkdir($path); } - /** - * check if a file can be created in $path - * - * @param string $path - * @return bool - */ - public function isCreatable($path) { - try { - $this->checkFileAccess($path); - } catch (ForbiddenException $e) { - return false; - } + public function rmdir($path): bool { + $this->checkFileAccess($path, true); + return $this->storage->rmdir($path); } - /** - * check if a file can be read - * - * @param string $path - * @return bool - */ - public function isReadable($path) { - try { - $this->checkFileAccess($path); - } catch (ForbiddenException $e) { - return false; - } + public function isCreatable($path): bool { + $this->checkFileAccess($path, requestToCheckModifiable:true); + return $this->storage->isCreatable($path); } - /** - * check if a file can be written to - * - * @param string $path - * @return bool - */ - public function isUpdatable($path) { - try { - $this->checkFileAccess($path); - } catch (ForbiddenException $e) { - return false; - } + public function isReadable($path): bool { + $this->checkFileAccess($path); + return $this->storage->isReadable($path); } - /** - * check if a file can be deleted - * - * @param string $path - * @return bool - */ - public function isDeletable($path) { - try { - $this->checkFileAccess($path); - } catch (ForbiddenException $e) { - return false; - } + public function isUpdatable($path): bool { + $this->checkFileAccess($path, requestToCheckModifiable:true); + return $this->storage->isUpdatable($path); } - public function getPermissions($path) { - try { - $this->checkFileAccess($path); - } catch (ForbiddenException $e) { - return $this->mask; - } + public function isDeletable($path): bool { + $this->checkFileAccess($path, requestToCheckModifiable:true); + return $this->storage->isDeletable($path); } - /** - * see http://php.net/manual/en/function.file_get_contents.php - * - * @param string $path - * @return string - * @throws ForbiddenException - */ - public function file_get_contents($path) { + public function getPermissions($path): int { $this->checkFileAccess($path); + return $this->storage->getPermissions($path); } - /** - * see http://php.net/manual/en/function.file_put_contents.php - * - * @param string $path - * @param string $data - * @return bool - * @throws ForbiddenException - */ - public function file_put_contents($path, $data) { - $this->checkFileAccess($path); + public function file_get_contents($path): string|false { + $this->checkFileAccess($path, false); + return $this->storage->file_get_contents($path); } - /** - * see http://php.net/manual/en/function.unlink.php - * - * @param string $path - * @return bool - * @throws ForbiddenException - */ - public function unlink($path) { - $this->checkFileAccess($path); + public function file_put_contents($path, $data): int|float|false { + $this->checkFileAccess($path, false); + return $this->storage->file_put_contents($path, $data); } - /** - * see http://php.net/manual/en/function.rename.php - * - * @param string $path1 - * @param string $path2 - * @return bool - * @throws ForbiddenException - */ - public function rename($path1, $path2) { - $this->checkFileAccess($path1); - $this->checkFileAccess($path2); + public function unlink($path): bool { + $this->checkFileAccess($path, false); + return $this->storage->unlink($path); } - /** - * see http://php.net/manual/en/function.copy.php - * - * @param string $path1 - * @param string $path2 - * @return bool - * @throws ForbiddenException - */ - public function copy($path1, $path2) { - $this->checkFileAccess($path1); - $this->checkFileAccess($path2); + public function rename($source, $target): bool { + $isDir = $this->is_dir($source); + $this->checkFileAccess($source, $isDir); + $this->checkFileAccess($target, $isDir); + return $this->storage->rename($source, $target); + } + + public function copy($source, $target): bool { + $isDir = $this->is_dir($source); + $this->checkFileAccess($source, $isDir); + $this->checkFileAccess($target, $isDir); + return $this->storage->copy($source, $target); } - /** - * see http://php.net/manual/en/function.fopen.php - * - * @param string $path - * @param string $mode - * @return resource - * @throws ForbiddenException - */ public function fopen($path, $mode) { - $this->checkFileAccess($path); + $this->checkFileAccess($path, false); + return $this->storage->fopen($path, $mode); } - /** - * see http://php.net/manual/en/function.touch.php - * If the backend does not support the operation, false should be returned - * - * @param string $path - * @param int $mtime - * @return bool - * @throws ForbiddenException - */ - public function touch($path, $mtime = null) { - $this->checkFileAccess($path); + public function touch($path, $mtime = null): bool { + $this->checkFileAccess($path, false); + return $this->storage->touch($path, $mtime); } /** - * get a cache instance for the storage + * Get a cache instance for the storage * * @param string $path * @param Storage (optional) the storage to pass to the cache * @return Cache */ - public function getCache($path = '', $storage = null) { + public function getCache($path = '', $storage = null): ICache { if (!$storage) { $storage = $this; } @@ -216,45 +149,30 @@ class StorageWrapper extends Wrapper implements IWriteStreamStorage { return new CacheWrapper($cache, $storage); } - /** - * A custom storage implementation can return an url for direct download of a give file. - * - * For now the returned array can hold the parameter url - in future more attributes might follow. - * - * @param string $path - * @return array - * @throws ForbiddenException - */ - public function getDirectDownload($path) { - $this->checkFileAccess($path); + public function getDirectDownload($path): array|false { + + $this->checkFileAccess($path, false); + return $this->storage->getDirectDownload($path); } - /** - * @param IStorage $sourceStorage - * @param string $sourceInternalPath - * @param string $targetInternalPath - * @return bool - * @throws ForbiddenException - */ - public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath): bool { + if ($sourceStorage === $this) { + return $this->copy($sourceInternalPath, $targetInternalPath); + } $this->checkFileAccess($targetInternalPath); + return $this->storage->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); } - /** - * @param IStorage $sourceStorage - * @param string $sourceInternalPath - * @param string $targetInternalPath - * @return bool - * @throws ForbiddenException - */ - public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath): bool { + if ($sourceStorage === $this) { + return $this->rename($sourceInternalPath, $targetInternalPath); + } $this->checkFileAccess($targetInternalPath); + return $this->storage->moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); } - /** - * @throws ForbiddenException - */ public function writeStream(string $path, $stream, ?int $size = null): int { - $this->checkFileAccess($path); + $this->checkFileAccess($path, false); + return $this->storage->writeStream($path, $stream, $size); } }