From a52c3fb46d6f8ab7e4f564fba986a264205f99fa Mon Sep 17 00:00:00 2001 From: Akhil Date: Fri, 11 Oct 2024 17:50:17 +0530 Subject: [PATCH 1/3] Storage wrapper to disable filesystem fully --- lib/AppInfo/Application.php | 27 ++++ lib/Filesystem/CacheWrapper.php | 34 ++++ lib/Filesystem/StorageWrapper.php | 261 ++++++++++++++++++++++++++++++ 3 files changed, 322 insertions(+) create mode 100644 lib/Filesystem/CacheWrapper.php create mode 100644 lib/Filesystem/StorageWrapper.php diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 24c5f795..ca2bbcfa 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -43,6 +43,8 @@ use OCP\IUserManager; use OCP\User\Events\BeforeUserDeletedEvent; use OCP\User\Events\PasswordUpdatedEvent; use OCP\User\Events\UserChangedEvent; +use OCP\Util; +use OCA\EcloudAccounts\Filesystem\StorageWrapper; class Application extends App implements IBootstrap { public const APP_ID = 'ecloud-accounts'; @@ -52,6 +54,7 @@ class Application extends App implements IBootstrap { } public function register(IRegistrationContext $context): void { + Util::connectHook('OC_Filesystem', 'preSetup', $this, 'addStorageWrapper'); $context->registerEventListener(BeforeTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class); $context->registerEventListener(BeforeUserDeletedEvent::class, BeforeUserDeletedListener::class); $context->registerEventListener(UserChangedEvent::class, UserChangedListener::class); @@ -69,4 +72,28 @@ class Application extends App implements IBootstrap { ); }); } + + /** + * @internal + */ + public function addStorageWrapper(): void { + Filesystem::addStorageWrapper('ecloud-accounts', [$this, 'addStorageWrapperCallback'], -10); + } + + /** + * @internal + * @param $mountPoint + * @param IStorage $storage + * @return StorageWrapper|IStorage + */ + public function addStorageWrapperCallback($mountPoint, IStorage $storage) { + if (!OC::$CLI && $mountPoint !== '/') { + return new StorageWrapper([ + 'storage' => $storage, + 'mountPoint' => $mountPoint, + ]); + } + + return $storage; + } } diff --git a/lib/Filesystem/CacheWrapper.php b/lib/Filesystem/CacheWrapper.php new file mode 100644 index 00000000..7fb78421 --- /dev/null +++ b/lib/Filesystem/CacheWrapper.php @@ -0,0 +1,34 @@ +mask = Constants::PERMISSION_ALL + & ~Constants::PERMISSION_READ + & ~Constants::PERMISSION_CREATE + & ~Constants::PERMISSION_UPDATE + & ~Constants::PERMISSION_DELETE; + } + + protected function formatCacheEntry($entry) { + if (isset($entry['path']) && isset($entry['permissions'])) { + try { + throw new ForbiddenException('Access denied'); + } catch (ForbiddenException) { + $entry['permissions'] &= $this->mask; + } + } + return $entry; + } +} diff --git a/lib/Filesystem/StorageWrapper.php b/lib/Filesystem/StorageWrapper.php new file mode 100644 index 00000000..88a70136 --- /dev/null +++ b/lib/Filesystem/StorageWrapper.php @@ -0,0 +1,261 @@ +checkFileAccess($path, true); + } + + /** + * see http://php.net/manual/en/function.rmdir.php + * + * @param string $path + * @return bool + * @throws ForbiddenException + */ + public function rmdir($path) { + $this->checkFileAccess($path, true); + } + + /** + * 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; + } + } + + /** + * 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; + } + } + + /** + * 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; + } + } + + /** + * 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 getPermissions($path) { + try { + $this->checkFileAccess($path); + } catch (ForbiddenException $e) { + return $this->mask; + } + } + + /** + * see http://php.net/manual/en/function.file_get_contents.php + * + * @param string $path + * @return string + * @throws ForbiddenException + */ + public function file_get_contents($path) { + $this->checkFileAccess($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); + } + + /** + * see http://php.net/manual/en/function.unlink.php + * + * @param string $path + * @return bool + * @throws ForbiddenException + */ + public function unlink($path) { + $this->checkFileAccess($path); + } + + /** + * 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); + } + + /** + * 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); + } + + /** + * 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); + } + + /** + * 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); + } + + /** + * 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) { + if (!$storage) { + $storage = $this; + } + $cache = $this->storage->getCache($path, $storage); + return new CacheWrapper($cache, $storage, $this->operation); + } + + /** + * 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); + } + + /** + * @param IStorage $sourceStorage + * @param string $sourceInternalPath + * @param string $targetInternalPath + * @return bool + * @throws ForbiddenException + */ + public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + $this->checkFileAccess($targetInternalPath); + } + + /** + * @param IStorage $sourceStorage + * @param string $sourceInternalPath + * @param string $targetInternalPath + * @return bool + * @throws ForbiddenException + */ + public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + $this->checkFileAccess($targetInternalPath); + } + + /** + * @throws ForbiddenException + */ + public function writeStream(string $path, $stream, ?int $size = null): int { + $this->checkFileAccess($path); + } +} -- GitLab From 7a42c1bc6ce9984baae3a9e9f28e5e8eb8432bf9 Mon Sep 17 00:00:00 2001 From: Akhil Date: Fri, 11 Oct 2024 18:45:19 +0530 Subject: [PATCH 2/3] Run linter --- lib/AppInfo/Application.php | 2 +- lib/Filesystem/CacheWrapper.php | 16 ++++++++++++++++ lib/Filesystem/StorageWrapper.php | 1 - 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index ca2bbcfa..ede0b972 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -26,6 +26,7 @@ declare(strict_types=1); namespace OCA\EcloudAccounts\AppInfo; +use OCA\EcloudAccounts\Filesystem\StorageWrapper; use OCA\EcloudAccounts\Listeners\BeforeTemplateRenderedListener; use OCA\EcloudAccounts\Listeners\BeforeUserDeletedListener; use OCA\EcloudAccounts\Listeners\PasswordUpdatedListener; @@ -44,7 +45,6 @@ use OCP\User\Events\BeforeUserDeletedEvent; use OCP\User\Events\PasswordUpdatedEvent; use OCP\User\Events\UserChangedEvent; use OCP\Util; -use OCA\EcloudAccounts\Filesystem\StorageWrapper; class Application extends App implements IBootstrap { public const APP_ID = 'ecloud-accounts'; diff --git a/lib/Filesystem/CacheWrapper.php b/lib/Filesystem/CacheWrapper.php index 7fb78421..79af6ca8 100644 --- a/lib/Filesystem/CacheWrapper.php +++ b/lib/Filesystem/CacheWrapper.php @@ -31,4 +31,20 @@ class CacheWrapper extends Wrapper { } return $entry; } + + public function get($fileId) { + throw new OCP\Files\Cache\CacheEntryNotFoundException('User data cache access is disabled.'); + } + + public function insert($file, $data) { + throw new \OCP\Files\Cache\CacheUpdateForbiddenException('User data cache insert is disabled.'); + } + + public function update($id, $data) { + throw new \OCP\Files\Cache\CacheUpdateForbiddenException('User data cache update is disabled.'); + } + + public function remove($fileId) { + throw new \OCP\Files\Cache\CacheUpdateForbiddenException('User data cache removal is disabled.'); + } } diff --git a/lib/Filesystem/StorageWrapper.php b/lib/Filesystem/StorageWrapper.php index 88a70136..99292b37 100644 --- a/lib/Filesystem/StorageWrapper.php +++ b/lib/Filesystem/StorageWrapper.php @@ -10,7 +10,6 @@ use OC\Files\Storage\Wrapper\Wrapper; use OCP\Files\ForbiddenException; use OCP\Files\Storage\IStorage; use OCP\Files\Storage\IWriteStreamStorage; -use OCA\EcloudAccounts\Filesystem\CacheWrapper; class StorageWrapper extends Wrapper implements IWriteStreamStorage { /** -- GitLab From 0d1d77be1dab859406805a0b1d8e749fc6f471c7 Mon Sep 17 00:00:00 2001 From: Akhil Date: Fri, 11 Oct 2024 19:16:28 +0530 Subject: [PATCH 3/3] Fix imports --- lib/AppInfo/Application.php | 4 +++- lib/Filesystem/CacheWrapper.php | 13 +++++-------- lib/Filesystem/StorageWrapper.php | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index ede0b972..a6f4e9c6 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -26,6 +26,7 @@ declare(strict_types=1); namespace OCA\EcloudAccounts\AppInfo; +use OC\Files\Filesystem; use OCA\EcloudAccounts\Filesystem\StorageWrapper; use OCA\EcloudAccounts\Listeners\BeforeTemplateRenderedListener; use OCA\EcloudAccounts\Listeners\BeforeUserDeletedListener; @@ -40,6 +41,7 @@ use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent; +use OCP\Files\Storage\IStorage; use OCP\IUserManager; use OCP\User\Events\BeforeUserDeletedEvent; use OCP\User\Events\PasswordUpdatedEvent; @@ -87,7 +89,7 @@ class Application extends App implements IBootstrap { * @return StorageWrapper|IStorage */ public function addStorageWrapperCallback($mountPoint, IStorage $storage) { - if (!OC::$CLI && $mountPoint !== '/') { + if (!\OC::$CLI && $mountPoint !== '/') { return new StorageWrapper([ 'storage' => $storage, 'mountPoint' => $mountPoint, diff --git a/lib/Filesystem/CacheWrapper.php b/lib/Filesystem/CacheWrapper.php index 79af6ca8..8233a60e 100644 --- a/lib/Filesystem/CacheWrapper.php +++ b/lib/Filesystem/CacheWrapper.php @@ -6,6 +6,7 @@ namespace OCA\EcloudAccounts\Filesystem; use OC\Files\Cache\Wrapper\CacheWrapper as Wrapper; use OCP\Constants; +use OCP\Files\Cache\ICache; use OCP\Files\ForbiddenException; class CacheWrapper extends Wrapper { @@ -24,7 +25,7 @@ class CacheWrapper extends Wrapper { protected function formatCacheEntry($entry) { if (isset($entry['path']) && isset($entry['permissions'])) { try { - throw new ForbiddenException('Access denied'); + throw new ForbiddenException('Access denied', false); } catch (ForbiddenException) { $entry['permissions'] &= $this->mask; } @@ -32,19 +33,15 @@ class CacheWrapper extends Wrapper { return $entry; } - public function get($fileId) { - throw new OCP\Files\Cache\CacheEntryNotFoundException('User data cache access is disabled.'); - } - public function insert($file, $data) { - throw new \OCP\Files\Cache\CacheUpdateForbiddenException('User data cache insert is disabled.'); + throw new \Exception('User data cache insert is disabled.'); } public function update($id, $data) { - throw new \OCP\Files\Cache\CacheUpdateForbiddenException('User data cache update is disabled.'); + throw new \Exception('User data cache update is disabled.'); } public function remove($fileId) { - throw new \OCP\Files\Cache\CacheUpdateForbiddenException('User data cache removal is disabled.'); + throw new \Exception('User data cache removal is disabled.'); } } diff --git a/lib/Filesystem/StorageWrapper.php b/lib/Filesystem/StorageWrapper.php index 99292b37..f35a5510 100644 --- a/lib/Filesystem/StorageWrapper.php +++ b/lib/Filesystem/StorageWrapper.php @@ -213,7 +213,7 @@ class StorageWrapper extends Wrapper implements IWriteStreamStorage { $storage = $this; } $cache = $this->storage->getCache($path, $storage); - return new CacheWrapper($cache, $storage, $this->operation); + return new CacheWrapper($cache, $storage); } /** -- GitLab