diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 24c5f79574a967db784ba770185bf2fb6e0d5357..a6f4e9c600aa8f858b24f0d7a090d6c9d01a6db6 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -26,6 +26,8 @@ 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; use OCA\EcloudAccounts\Listeners\PasswordUpdatedListener; @@ -39,10 +41,12 @@ 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; use OCP\User\Events\UserChangedEvent; +use OCP\Util; class Application extends App implements IBootstrap { public const APP_ID = 'ecloud-accounts'; @@ -52,6 +56,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 +74,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 0000000000000000000000000000000000000000..8233a60ef91386830cf3f543f180ca8de1050b50 --- /dev/null +++ b/lib/Filesystem/CacheWrapper.php @@ -0,0 +1,47 @@ +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', false); + } catch (ForbiddenException) { + $entry['permissions'] &= $this->mask; + } + } + return $entry; + } + + public function insert($file, $data) { + throw new \Exception('User data cache insert is disabled.'); + } + + public function update($id, $data) { + throw new \Exception('User data cache update is disabled.'); + } + + public function remove($fileId) { + throw new \Exception('User data cache removal is disabled.'); + } +} diff --git a/lib/Filesystem/StorageWrapper.php b/lib/Filesystem/StorageWrapper.php new file mode 100644 index 0000000000000000000000000000000000000000..f35a55105be582163fcfdbfbe44eab87292767be --- /dev/null +++ b/lib/Filesystem/StorageWrapper.php @@ -0,0 +1,260 @@ +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); + } + + /** + * 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); + } +}