diff --git a/apps/dav/lib/Connector/Sabre/DavAclPlugin.php b/apps/dav/lib/Connector/Sabre/DavAclPlugin.php index 7fa94d7b90398..f574cec00c6b6 100644 --- a/apps/dav/lib/Connector/Sabre/DavAclPlugin.php +++ b/apps/dav/lib/Connector/Sabre/DavAclPlugin.php @@ -75,6 +75,11 @@ public function checkPrivileges($uri, $privileges, $recursion = self::R_PARENT, } public function propFind(PropFind $propFind, INode $node) { + if ($node instanceof Node) { + // files don't use dav acls + return; + } + // If the node is neither readable nor writable then fail unless its of // the standard user-principal if (!($node instanceof User)) { diff --git a/apps/dav/lib/Connector/Sabre/Directory.php b/apps/dav/lib/Connector/Sabre/Directory.php index c29070fe921fb..20202d8368956 100644 --- a/apps/dav/lib/Connector/Sabre/Directory.php +++ b/apps/dav/lib/Connector/Sabre/Directory.php @@ -35,16 +35,19 @@ use OC\Files\Mount\MoveableMount; use OC\Files\View; use OC\Metadata\FileMetadata; +use OCA\DAV\AppInfo\Application; use OCA\DAV\Connector\Sabre\Exception\FileLocked; use OCA\DAV\Connector\Sabre\Exception\Forbidden; use OCA\DAV\Connector\Sabre\Exception\InvalidPath; -use OCA\DAV\Upload\FutureFile; use OCP\Files\FileInfo; use OCP\Files\Folder; use OCP\Files\ForbiddenException; use OCP\Files\InvalidPathException; use OCP\Files\NotPermittedException; use OCP\Files\StorageNotAvailableException; +use OCP\IL10N; +use OCP\IRequest; +use OCP\L10N\IFactory; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; use Psr\Log\LoggerInterface; @@ -203,7 +206,7 @@ public function createDirectory($name) { * @throws \Sabre\DAV\Exception\NotFound * @throws \Sabre\DAV\Exception\ServiceUnavailable */ - public function getChild($name, $info = null) { + public function getChild($name, $info = null, IRequest $request = null, IL10N $l10n = null) { if (!$this->info->isReadable()) { // avoid detecting files through this way throw new NotFound(); @@ -230,7 +233,7 @@ public function getChild($name, $info = null) { if ($info->getMimeType() === FileInfo::MIMETYPE_FOLDER) { $node = new \OCA\DAV\Connector\Sabre\Directory($this->fileView, $info, $this->tree, $this->shareManager); } else { - $node = new \OCA\DAV\Connector\Sabre\File($this->fileView, $info, $this->shareManager); + $node = new \OCA\DAV\Connector\Sabre\File($this->fileView, $info, $this->shareManager, $request, $l10n); } if ($this->tree) { $this->tree->cacheNode($node); @@ -265,8 +268,11 @@ public function getChildren() { } $nodes = []; + $request = \OC::$server->get(IRequest::class); + $l10nFactory = \OC::$server->get(IFactory::class); + $l10n = $l10nFactory->get(Application::APP_ID); foreach ($folderContent as $info) { - $node = $this->getChild($info->getName(), $info); + $node = $this->getChild($info->getName(), $info, $request, $l10n); $nodes[] = $node; } $this->dirContent = $nodes; @@ -315,20 +321,22 @@ public function delete() { } } + private function getLogger(): LoggerInterface { + return \OC::$server->get(LoggerInterface::class); + } + /** * Returns available diskspace information * * @return array */ public function getQuotaInfo() { - /** @var LoggerInterface $logger */ - $logger = \OC::$server->get(LoggerInterface::class); if ($this->quotaInfo) { return $this->quotaInfo; } $relativePath = $this->fileView->getRelativePath($this->info->getPath()); if ($relativePath === null) { - $logger->warning("error while getting quota as the relative path cannot be found"); + $this->getLogger()->warning("error while getting quota as the relative path cannot be found"); return [0, 0]; } @@ -345,13 +353,13 @@ public function getQuotaInfo() { ]; return $this->quotaInfo; } catch (\OCP\Files\NotFoundException $e) { - $logger->warning("error while getting quota into", ['exception' => $e]); + $this->getLogger()->warning("error while getting quota into", ['exception' => $e]); return [0, 0]; } catch (\OCP\Files\StorageNotAvailableException $e) { - $logger->warning("error while getting quota into", ['exception' => $e]); + $this->getLogger()->warning("error while getting quota into", ['exception' => $e]); return [0, 0]; } catch (NotPermittedException $e) { - $logger->warning("error while getting quota into", ['exception' => $e]); + $this->getLogger()->warning("error while getting quota into", ['exception' => $e]); return [0, 0]; } } diff --git a/apps/dav/lib/Connector/Sabre/File.php b/apps/dav/lib/Connector/Sabre/File.php index a7cafeb4a5e1a..8ce8c843a6628 100644 --- a/apps/dav/lib/Connector/Sabre/File.php +++ b/apps/dav/lib/Connector/Sabre/File.php @@ -63,6 +63,7 @@ use OCP\Files\Storage; use OCP\Files\StorageNotAvailableException; use OCP\IL10N; +use OCP\IRequest; use OCP\L10N\IFactory as IL10NFactory; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; @@ -77,8 +78,7 @@ use Sabre\DAV\IFile; class File extends Node implements IFile { - protected $request; - + protected IRequest $request; protected IL10N $l10n; /** @var array */ @@ -89,21 +89,26 @@ class File extends Node implements IFile { * * @param \OC\Files\View $view * @param \OCP\Files\FileInfo $info - * @param \OCP\Share\IManager $shareManager - * @param \OC\AppFramework\Http\Request $request + * @param ?\OCP\Share\IManager $shareManager + * @param ?IRequest $request + * @param ?IL10N $l10n */ - public function __construct(View $view, FileInfo $info, IManager $shareManager = null, Request $request = null) { + public function __construct(View $view, FileInfo $info, IManager $shareManager = null, IRequest $request = null, IL10N $l10n = null) { parent::__construct($view, $info, $shareManager); - // Querying IL10N directly results in a dependency loop - /** @var IL10NFactory $l10nFactory */ - $l10nFactory = \OC::$server->get(IL10NFactory::class); - $this->l10n = $l10nFactory->get(Application::APP_ID); + if ($l10n) { + $this->l10n = $l10n; + } else { + // Querying IL10N directly results in a dependency loop + /** @var IL10NFactory $l10nFactory */ + $l10nFactory = \OC::$server->get(IL10NFactory::class); + $this->l10n = $l10nFactory->get(Application::APP_ID); + } if (isset($request)) { $this->request = $request; } else { - $this->request = \OC::$server->getRequest(); + $this->request = \OC::$server->get(IRequest::class); } } @@ -149,7 +154,8 @@ public function put($data) { $this->verifyPath(); // chunked handling - if (isset($_SERVER['HTTP_OC_CHUNKED'])) { + $chunkedHeader = $this->request->getHeader('oc-chunked'); + if ($chunkedHeader) { try { return $this->createFileChunked($data); } catch (\Exception $e) { @@ -272,8 +278,9 @@ public function put($data) { if ($result === false) { $expected = -1; - if (isset($_SERVER['CONTENT_LENGTH'])) { - $expected = (int)$_SERVER['CONTENT_LENGTH']; + $lengthHeader = $this->request->getHeader('content-length'); + if ($lengthHeader) { + $expected = (int)$lengthHeader; } if ($expected !== 0) { throw new Exception( @@ -291,8 +298,9 @@ public function put($data) { // if content length is sent by client: // double check if the file was fully received // compare expected and actual size - if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['REQUEST_METHOD'] === 'PUT') { - $expected = (int)$_SERVER['CONTENT_LENGTH']; + $lengthHeader = $this->request->getHeader('content-length'); + if ($lengthHeader && $this->request->getMethod() === 'PUT') { + $expected = (int)$lengthHeader; if ($count !== $expected) { throw new BadRequest( $this->l10n->t( @@ -374,8 +382,9 @@ public function put($data) { } // allow sync clients to send the mtime along in a header - if (isset($this->request->server['HTTP_X_OC_MTIME'])) { - $mtime = $this->sanitizeMtime($this->request->server['HTTP_X_OC_MTIME']); + $mtimeHeader = $this->request->getHeader('x-oc-mtime'); + if ($mtimeHeader !== '') { + $mtime = $this->sanitizeMtime($mtimeHeader); if ($this->fileView->touch($this->path, $mtime)) { $this->header('X-OC-MTime: accepted'); } @@ -386,8 +395,9 @@ public function put($data) { ]; // allow sync clients to send the creation time along in a header - if (isset($this->request->server['HTTP_X_OC_CTIME'])) { - $ctime = $this->sanitizeMtime($this->request->server['HTTP_X_OC_CTIME']); + $ctimeHeader = $this->request->getHeader('x-oc-ctime'); + if ($ctimeHeader) { + $ctime = $this->sanitizeMtime($ctimeHeader); $fileInfoUpdate['creation_time'] = $ctime; $this->header('X-OC-CTime: accepted'); } @@ -400,8 +410,9 @@ public function put($data) { $this->refreshInfo(); - if (isset($this->request->server['HTTP_OC_CHECKSUM'])) { - $checksum = trim($this->request->server['HTTP_OC_CHECKSUM']); + $checksumHeader = $this->request->getHeader('oc-checksum'); + if ($checksumHeader) { + $checksum = trim($checksumHeader); $this->setChecksum($checksum); } elseif ($this->getChecksum() !== null && $this->getChecksum() !== '') { $this->setChecksum(''); @@ -557,7 +568,7 @@ public function getContentType() { $mimeType = $this->info->getMimetype(); // PROPFIND needs to return the correct mime type, for consistency with the web UI - if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') { + if ($this->request->getMethod() === 'PROPFIND') { return $mimeType; } return \OC::$server->getMimeTypeDetector()->getSecureMimeType($mimeType); @@ -599,9 +610,10 @@ private function createFileChunked($data) { $bytesWritten = $chunk_handler->store($info['index'], $data); //detect aborted upload - if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PUT') { - if (isset($_SERVER['CONTENT_LENGTH'])) { - $expected = (int)$_SERVER['CONTENT_LENGTH']; + if ($this->request->getMethod() === 'PUT') { + $lengthHeader = $this->request->getHeader('content-length'); + if ($lengthHeader) { + $expected = (int)$lengthHeader; if ($bytesWritten !== $expected) { $chunk_handler->remove($info['index']); throw new BadRequest( @@ -667,8 +679,9 @@ private function createFileChunked($data) { } // allow sync clients to send the mtime along in a header - if (isset($this->request->server['HTTP_X_OC_MTIME'])) { - $mtime = $this->sanitizeMtime($this->request->server['HTTP_X_OC_MTIME']); + $mtimeHeader = $this->request->getHeader('x-oc-mtime'); + if ($mtimeHeader !== '') { + $mtime = $this->sanitizeMtime($mtimeHeader); if ($targetStorage->touch($targetInternalPath, $mtime)) { $this->header('X-OC-MTime: accepted'); } @@ -684,8 +697,9 @@ private function createFileChunked($data) { // FIXME: should call refreshInfo but can't because $this->path is not the of the final file $info = $this->fileView->getFileInfo($targetPath); - if (isset($this->request->server['HTTP_OC_CHECKSUM'])) { - $checksum = trim($this->request->server['HTTP_OC_CHECKSUM']); + $checksumHeader = $this->request->getHeader('oc-checksum'); + if ($checksumHeader) { + $checksum = trim($checksumHeader); $this->fileView->putFileInfo($targetPath, ['checksum' => $checksum]); } elseif ($info->getChecksum() !== null && $info->getChecksum() !== '') { $this->fileView->putFileInfo($this->path, ['checksum' => '']); diff --git a/apps/dav/tests/unit/Connector/Sabre/FileTest.php b/apps/dav/tests/unit/Connector/Sabre/FileTest.php index 8d72fb13b7875..884245afa3bb2 100644 --- a/apps/dav/tests/unit/Connector/Sabre/FileTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/FileTest.php @@ -73,9 +73,6 @@ class FileTest extends TestCase { protected function setUp(): void { parent::setUp(); - unset($_SERVER['HTTP_OC_CHUNKED']); - unset($_SERVER['CONTENT_LENGTH']); - unset($_SERVER['REQUEST_METHOD']); \OC_Hook::clear(); @@ -91,7 +88,6 @@ protected function setUp(): void { protected function tearDown(): void { $userManager = \OC::$server->getUserManager(); $userManager->get($this->user)->delete(); - unset($_SERVER['HTTP_OC_CHUNKED']); parent::tearDown(); } @@ -271,13 +267,17 @@ function ($path) use ($storage) { ->method('getRelativePath') ->willReturnArgument(0); - $_SERVER['HTTP_OC_CHUNKED'] = true; + $request = new Request([ + 'server' => [ + 'HTTP_OC_CHUNKED' => 'true' + ] + ], $this->requestId, $this->config, null); $info = new \OC\Files\FileInfo('/test.txt-chunking-12345-2-0', $this->getMockStorage(), null, [ 'permissions' => \OCP\Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new \OCA\DAV\Connector\Sabre\File($view, $info, null, $request); // put first chunk $file->acquireLock(ILockingProvider::LOCK_SHARED); @@ -288,7 +288,7 @@ function ($path) use ($storage) { 'permissions' => \OCP\Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new \OCA\DAV\Connector\Sabre\File($view, $info, null, $request); // action $caughtException = null; @@ -421,7 +421,7 @@ public function legalMtimeProvider() { public function testPutSingleFileLegalMtime($requestMtime, $resultMtime): void { $request = new Request([ 'server' => [ - 'HTTP_X_OC_MTIME' => $requestMtime, + 'HTTP_X_OC_MTIME' => (string)$requestMtime, ] ], $this->requestId, $this->config, null); $file = 'foo.txt'; @@ -444,11 +444,11 @@ public function testPutSingleFileLegalMtime($requestMtime, $resultMtime): void { public function testChunkedPutLegalMtime($requestMtime, $resultMtime): void { $request = new Request([ 'server' => [ - 'HTTP_X_OC_MTIME' => $requestMtime, + 'HTTP_X_OC_MTIME' => (string)$requestMtime, + 'HTTP_OC_CHUNKED' => 'true' ] ], $this->requestId, $this->config, null); - $_SERVER['HTTP_OC_CHUNKED'] = true; $file = 'foo.txt'; if ($resultMtime === null) { @@ -467,9 +467,13 @@ public function testChunkedPutLegalMtime($requestMtime, $resultMtime): void { * Test putting a file using chunking */ public function testChunkedPut(): void { - $_SERVER['HTTP_OC_CHUNKED'] = true; - $this->assertNull($this->doPut('/test.txt-chunking-12345-2-0')); - $this->assertNotEmpty($this->doPut('/test.txt-chunking-12345-2-1')); + $request = new Request([ + 'server' => [ + 'HTTP_OC_CHUNKED' => 'true' + ] + ], $this->requestId, $this->config, null); + $this->assertNull($this->doPut('/test.txt-chunking-12345-2-0', null, $request)); + $this->assertNotEmpty($this->doPut('/test.txt-chunking-12345-2-1', null, $request)); } /** @@ -580,9 +584,13 @@ public function testPutSingleFileTriggersHooksDifferentRoot(): void { public function testPutChunkedFileTriggersHooks(): void { HookHelper::setUpHooks(); - $_SERVER['HTTP_OC_CHUNKED'] = true; - $this->assertNull($this->doPut('/foo.txt-chunking-12345-2-0')); - $this->assertNotEmpty($this->doPut('/foo.txt-chunking-12345-2-1')); + $request = new Request([ + 'server' => [ + 'HTTP_OC_CHUNKED' => 'true' + ] + ], $this->requestId, $this->config, null); + $this->assertNull($this->doPut('/foo.txt-chunking-12345-2-0', null, $request)); + $this->assertNotEmpty($this->doPut('/foo.txt-chunking-12345-2-1', null, $request)); $this->assertCount(4, HookHelper::$hookCalls); $this->assertHookCall( @@ -616,9 +624,13 @@ public function testPutOverwriteChunkedFileTriggersHooks(): void { HookHelper::setUpHooks(); - $_SERVER['HTTP_OC_CHUNKED'] = true; - $this->assertNull($this->doPut('/foo.txt-chunking-12345-2-0')); - $this->assertNotEmpty($this->doPut('/foo.txt-chunking-12345-2-1')); + $request = new Request([ + 'server' => [ + 'HTTP_OC_CHUNKED' => 'true' + ] + ], $this->requestId, $this->config, null); + $this->assertNull($this->doPut('/foo.txt-chunking-12345-2-0', null, $request)); + $this->assertNotEmpty($this->doPut('/foo.txt-chunking-12345-2-1', null, $request)); $this->assertCount(4, HookHelper::$hookCalls); $this->assertHookCall( @@ -693,15 +705,19 @@ public function testSimplePutFailsSizeCheck(): void { ->method('filesize') ->willReturn(123456); - $_SERVER['CONTENT_LENGTH'] = 123456; - $_SERVER['REQUEST_METHOD'] = 'PUT'; + $request = new Request([ + 'server' => [ + 'CONTENT_LENGTH' => '123456', + ], + 'method' => 'PUT', + ], $this->requestId, $this->config, null); $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ 'permissions' => \OCP\Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new \OCA\DAV\Connector\Sabre\File($view, $info, null, $request); // action $thrown = false; @@ -764,13 +780,17 @@ public function testChunkedPutFailsFinalRename(): void { // simulate situation where the target file is locked $view->lockFile('/test.txt', ILockingProvider::LOCK_EXCLUSIVE); - $_SERVER['HTTP_OC_CHUNKED'] = true; + $request = new Request([ + 'server' => [ + 'HTTP_OC_CHUNKED' => 'true' + ] + ], $this->requestId, $this->config, null); $info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt-chunking-12345-2-0', $this->getMockStorage(), null, [ 'permissions' => \OCP\Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new \OCA\DAV\Connector\Sabre\File($view, $info, null, $request); $file->acquireLock(ILockingProvider::LOCK_SHARED); $this->assertNull($file->put('test data one')); $file->releaseLock(ILockingProvider::LOCK_SHARED); @@ -779,7 +799,7 @@ public function testChunkedPutFailsFinalRename(): void { 'permissions' => \OCP\Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new \OCA\DAV\Connector\Sabre\File($view, $info, null, $request); // action $thrown = false; @@ -872,15 +892,19 @@ public function testUploadAbort(): void { ->method('filesize') ->willReturn(123456); - $_SERVER['CONTENT_LENGTH'] = 12345; - $_SERVER['REQUEST_METHOD'] = 'PUT'; + $request = new Request([ + 'server' => [ + 'CONTENT_LENGTH' => '123456', + ], + 'method' => 'PUT', + ], $this->requestId, $this->config, null); $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ 'permissions' => \OCP\Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new \OCA\DAV\Connector\Sabre\File($view, $info, null, $request); // action $thrown = false; diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/RequestTestCase.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/RequestTestCase.php index f6aa79eb6c4b6..fc5115fb16693 100644 --- a/apps/dav/tests/unit/Connector/Sabre/RequestTest/RequestTestCase.php +++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/RequestTestCase.php @@ -31,7 +31,9 @@ use OC\Files\View; use OCA\DAV\Connector\Sabre\Server; use OCA\DAV\Connector\Sabre\ServerFactory; +use OCP\IConfig; use OCP\IRequest; +use OCP\IRequestId; use Psr\Log\LoggerInterface; use Sabre\HTTP\Request; use Test\TestCase; @@ -57,8 +59,6 @@ protected function getStream($string) { protected function setUp(): void { parent::setUp(); - unset($_SERVER['HTTP_OC_CHUNKED']); - $this->serverFactory = new ServerFactory( \OC::$server->getConfig(), \OC::$server->get(LoggerInterface::class), @@ -105,20 +105,25 @@ protected function request($view, $user, $password, $method, $url, $body = null, // since sabre catches all exceptions we need to save them and throw them from outside the sabre server - $originalServer = $_SERVER; - + $serverParams = []; if (is_array($headers)) { foreach ($headers as $header => $value) { - $_SERVER['HTTP_' . strtoupper(str_replace('-', '_', $header))] = $value; + $serverParams['HTTP_' . strtoupper(str_replace('-', '_', $header))] = $value; } } + $ncRequest = new \OC\AppFramework\Http\Request([ + 'server' => $serverParams + ], $this->createMock(IRequestId::class), $this->createMock(IConfig::class), null); + + $this->overwriteService(IRequest::class, $ncRequest); $result = $this->makeRequest($server, $request); + $this->restoreService(IRequest::class); + foreach ($exceptionPlugin->getExceptions() as $exception) { throw $exception; } - $_SERVER = $originalServer; return $result; } diff --git a/apps/files_sharing/lib/Cache.php b/apps/files_sharing/lib/Cache.php index b99a511312e4f..594660661cabb 100644 --- a/apps/files_sharing/lib/Cache.php +++ b/apps/files_sharing/lib/Cache.php @@ -41,6 +41,7 @@ use OCP\Files\StorageNotAvailableException; use OCP\ICacheFactory; use OCP\IUserManager; +use OCP\Share\IShare; /** * Metadata cache for shared files @@ -55,15 +56,22 @@ class Cache extends CacheJail { private ?string $ownerDisplayName = null; private $numericId; private DisplayNameCache $displayNameCache; + private IShare $share; /** * @param SharedStorage $storage */ - public function __construct($storage, ICacheEntry $sourceRootInfo, DisplayNameCache $displayNameCache) { + public function __construct( + $storage, + ICacheEntry $sourceRootInfo, + DisplayNameCache $displayNameCache, + IShare $share + ) { $this->storage = $storage; $this->sourceRootInfo = $sourceRootInfo; $this->numericId = $sourceRootInfo->getStorageId(); $this->displayNameCache = $displayNameCache; + $this->share = $share; parent::__construct( null, @@ -150,7 +158,7 @@ protected function formatCacheEntry($entry, $path = null) { try { if (isset($entry['permissions'])) { - $entry['permissions'] &= $this->storage->getShare()->getPermissions(); + $entry['permissions'] &= $this->share->getPermissions(); } else { $entry['permissions'] = $this->storage->getPermissions($entry['path']); } @@ -159,7 +167,7 @@ protected function formatCacheEntry($entry, $path = null) { // (IDE may say the exception is never thrown – false negative) $sharePermissions = 0; } - $entry['uid_owner'] = $this->storage->getOwner(''); + $entry['uid_owner'] = $this->share->getShareOwner(); $entry['displayname_owner'] = $this->getOwnerDisplayName(); if ($path === '') { $entry['is_share_mount_point'] = true; @@ -169,7 +177,7 @@ protected function formatCacheEntry($entry, $path = null) { private function getOwnerDisplayName() { if (!$this->ownerDisplayName) { - $uid = $this->storage->getOwner(''); + $uid = $this->share->getShareOwner(); $this->ownerDisplayName = $this->displayNameCache->getDisplayName($uid) ?? $uid; } return $this->ownerDisplayName; diff --git a/apps/files_sharing/lib/SharedStorage.php b/apps/files_sharing/lib/SharedStorage.php index 2c1ddf9af4ab7..9eaa849a7e31a 100644 --- a/apps/files_sharing/lib/SharedStorage.php +++ b/apps/files_sharing/lib/SharedStorage.php @@ -415,7 +415,8 @@ public function getCache($path = '', $storage = null) { $this->cache = new \OCA\Files_Sharing\Cache( $storage, $sourceRoot, - \OC::$server->get(DisplayNameCache::class) + \OC::$server->get(DisplayNameCache::class), + $this->getShare() ); return $this->cache; } diff --git a/apps/files_sharing/tests/ApiTest.php b/apps/files_sharing/tests/ApiTest.php index d7661297e9e86..3484bb29d941d 100644 --- a/apps/files_sharing/tests/ApiTest.php +++ b/apps/files_sharing/tests/ApiTest.php @@ -36,6 +36,8 @@ namespace OCA\Files_Sharing\Tests; use OC\Files\Cache\Scanner; +use OC\Files\Filesystem; +use OC\Files\SetupManager; use OCA\Files_Sharing\Controller\ShareAPIController; use OCP\App\IAppManager; use OCP\AppFramework\OCS\OCSBadRequestException; @@ -74,6 +76,8 @@ protected function setUp(): void { \OC::$server->getConfig()->setAppValue('core', 'shareapi_exclude_groups', 'no'); \OC::$server->getConfig()->setAppValue('core', 'shareapi_expire_after_n_days', '7'); + Filesystem::getLoader()->removeStorageWrapper('sharing_mask'); + $this->folder = self::TEST_FOLDER_NAME; $this->subfolder = '/subfolder_share_api_test'; $this->subsubfolder = '/subsubfolder_share_api_test'; diff --git a/apps/files_sharing/tests/CapabilitiesTest.php b/apps/files_sharing/tests/CapabilitiesTest.php index fcefc556203ad..294bbba7d9043 100644 --- a/apps/files_sharing/tests/CapabilitiesTest.php +++ b/apps/files_sharing/tests/CapabilitiesTest.php @@ -30,6 +30,7 @@ use OC\KnownUser\KnownUserService; use OC\Share20\Manager; +use OC\Share20\ShareDisableChecker; use OCA\Files_Sharing\Capabilities; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\IRootFolder; @@ -96,7 +97,8 @@ private function getResults(array $map) { $this->createMock(\OC_Defaults::class), $this->createMock(IEventDispatcher::class), $this->createMock(IUserSession::class), - $this->createMock(KnownUserService::class) + $this->createMock(KnownUserService::class), + $this->createMock(ShareDisableChecker::class) ); $cap = new Capabilities($config, $shareManager); $result = $this->getFilesSharingPart($cap->getCapabilities()); diff --git a/apps/files_trashbin/lib/Storage.php b/apps/files_trashbin/lib/Storage.php index f08d8d25a55a9..09b0dcf0a1a9f 100644 --- a/apps/files_trashbin/lib/Storage.php +++ b/apps/files_trashbin/lib/Storage.php @@ -66,7 +66,7 @@ class Storage extends Wrapper { * Storage constructor. * * @param array $parameters - * @param ITrashManager $trashManager + * @param ITrashManager|null $trashManager * @param IUserManager|null $userManager * @param ILogger|null $logger * @param EventDispatcherInterface|null $eventDispatcher @@ -208,19 +208,27 @@ private function doDelete($path, $method) { } /** - * Setup the storate wrapper callback + * Setup the storage wrapper callback */ public static function setupStorage() { - \OC\Files\Filesystem::addStorageWrapper('oc_trashbin', function ($mountPoint, $storage) { - return new \OCA\Files_Trashbin\Storage( - ['storage' => $storage, 'mountPoint' => $mountPoint], - \OC::$server->query(ITrashManager::class), - \OC::$server->getUserManager(), - \OC::$server->getLogger(), - \OC::$server->getEventDispatcher(), - \OC::$server->getLazyRootFolder() - ); - }, 1); + $trashManager = \OC::$server->get(ITrashManager::class); + $userManager = \OC::$server->get(IUserManager::class); + $logger = \OC::$server->get(ILogger::class); + $eventDispatcher = \OC::$server->get(EventDispatcherInterface::class); + $rootFolder = \OC::$server->get(IRootFolder::class); + Filesystem::addStorageWrapper( + 'oc_trashbin', + function (string $mountPoint, IStorage $storage) use ($trashManager, $userManager, $logger, $eventDispatcher, $rootFolder) { + return new Storage( + ['storage' => $storage, 'mountPoint' => $mountPoint], + $trashManager, + $userManager, + $logger, + $eventDispatcher, + $rootFolder, + ); + }, + 1); } public function getMountPoint() { diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 10d8472344233..66b59faddef49 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1581,6 +1581,7 @@ 'OC\\Share20\\PublicShareTemplateFactory' => $baseDir . '/lib/private/Share20/PublicShareTemplateFactory.php', 'OC\\Share20\\Share' => $baseDir . '/lib/private/Share20/Share.php', 'OC\\Share20\\ShareAttributes' => $baseDir . '/lib/private/Share20/ShareAttributes.php', + 'OC\\Share20\\ShareDisableChecker' => $baseDir . '/lib/private/Share20/ShareDisableChecker.php', 'OC\\Share20\\ShareHelper' => $baseDir . '/lib/private/Share20/ShareHelper.php', 'OC\\Share20\\UserRemovedListener' => $baseDir . '/lib/private/Share20/UserRemovedListener.php', 'OC\\Share\\Constants' => $baseDir . '/lib/private/Share/Constants.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 0550670046a0a..f3f1125a7edc2 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1614,6 +1614,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Share20\\PublicShareTemplateFactory' => __DIR__ . '/../../..' . '/lib/private/Share20/PublicShareTemplateFactory.php', 'OC\\Share20\\Share' => __DIR__ . '/../../..' . '/lib/private/Share20/Share.php', 'OC\\Share20\\ShareAttributes' => __DIR__ . '/../../..' . '/lib/private/Share20/ShareAttributes.php', + 'OC\\Share20\\ShareDisableChecker' => __DIR__ . '/../../..' . '/lib/private/Share20/ShareDisableChecker.php', 'OC\\Share20\\ShareHelper' => __DIR__ . '/../../..' . '/lib/private/Share20/ShareHelper.php', 'OC\\Share20\\UserRemovedListener' => __DIR__ . '/../../..' . '/lib/private/Share20/UserRemovedListener.php', 'OC\\Share\\Constants' => __DIR__ . '/../../..' . '/lib/private/Share/Constants.php', diff --git a/lib/private/Files/Cache/Wrapper/CacheWrapper.php b/lib/private/Files/Cache/Wrapper/CacheWrapper.php index f6bb949f96819..4952cb5ccd434 100644 --- a/lib/private/Files/Cache/Wrapper/CacheWrapper.php +++ b/lib/private/Files/Cache/Wrapper/CacheWrapper.php @@ -33,8 +33,10 @@ use OC\Files\Cache\QuerySearchHelper; use OCP\Files\Cache\ICache; use OCP\Files\Cache\ICacheEntry; +use OCP\Files\IMimeTypeLoader; use OCP\Files\Search\ISearchOperator; use OCP\Files\Search\ISearchQuery; +use OCP\IDBConnection; class CacheWrapper extends Cache { /** @@ -47,9 +49,15 @@ class CacheWrapper extends Cache { */ public function __construct($cache) { $this->cache = $cache; - $this->mimetypeLoader = \OC::$server->getMimeTypeLoader(); - $this->connection = \OC::$server->getDatabaseConnection(); - $this->querySearchHelper = \OC::$server->get(QuerySearchHelper::class); + if ($cache instanceof Cache) { + $this->mimetypeLoader = $cache->mimetypeLoader; + $this->connection = $cache->connection; + $this->querySearchHelper = $cache->querySearchHelper; + } else { + $this->mimetypeLoader = \OC::$server->get(IMimeTypeLoader::class); + $this->connection = \OC::$server->get(IDBConnection::class); + $this->querySearchHelper = \OC::$server->get(QuerySearchHelper::class); + } } protected function getCache() { diff --git a/lib/private/Files/FileInfo.php b/lib/private/Files/FileInfo.php index d717778133d1e..012c04bbbfe15 100644 --- a/lib/private/Files/FileInfo.php +++ b/lib/private/Files/FileInfo.php @@ -32,7 +32,9 @@ */ namespace OC\Files; +use OCA\Files_Sharing\ISharedStorage; use OCP\Files\Cache\ICacheEntry; +use OCP\Files\IHomeStorage; use OCP\Files\Mount\IMountPoint; use OCP\IUser; @@ -235,7 +237,7 @@ public function isEncrypted() { } /** - * Return the currently version used for the HMAC in the encryption app + * Return the current version used for the HMAC in the encryption app * * @return int */ @@ -247,11 +249,7 @@ public function getEncryptedVersion() { * @return int */ public function getPermissions() { - $perms = (int) $this->data['permissions']; - if (\OCP\Util::isSharingDisabledForUser() || ($this->isShared() && !\OC\Share\Share::isResharingAllowed())) { - $perms = $perms & ~\OCP\Constants::PERMISSION_SHARE; - } - return $perms; + return (int) $this->data['permissions']; } /** @@ -319,27 +317,13 @@ public function isShareable() { * @return bool */ public function isShared() { - $sid = $this->getStorage()->getId(); - if (!is_null($sid)) { - $sid = explode(':', $sid); - return ($sid[0] === 'shared'); - } - - return false; + $storage = $this->getStorage(); + return $storage->instanceOfStorage(ISharedStorage::class); } public function isMounted() { $storage = $this->getStorage(); - if ($storage->instanceOfStorage('\OCP\Files\IHomeStorage')) { - return false; - } - $sid = $storage->getId(); - if (!is_null($sid)) { - $sid = explode(':', $sid); - return ($sid[0] !== 'home' and $sid[0] !== 'shared'); - } - - return false; + return !($storage->instanceOfStorage(IHomeStorage::class) || $storage->instanceOfStorage(ISharedStorage::class)); } /** diff --git a/lib/private/Files/ObjectStore/HomeObjectStoreStorage.php b/lib/private/Files/ObjectStore/HomeObjectStoreStorage.php index 824adcc1d0e22..b361249ff4771 100644 --- a/lib/private/Files/ObjectStore/HomeObjectStoreStorage.php +++ b/lib/private/Files/ObjectStore/HomeObjectStoreStorage.php @@ -26,6 +26,7 @@ namespace OC\Files\ObjectStore; use OC\User\User; +use OCP\IUser; class HomeObjectStoreStorage extends ObjectStoreStorage implements \OCP\Files\IHomeStorage { /** @@ -61,7 +62,7 @@ public function getOwner($path) { * @param string $path, optional * @return \OC\User\User */ - public function getUser($path = null) { + public function getUser($path = null): IUser { return $this->user; } } diff --git a/lib/private/Files/SetupManager.php b/lib/private/Files/SetupManager.php index 01ce4a1cc5992..916fcfb0a7e63 100644 --- a/lib/private/Files/SetupManager.php +++ b/lib/private/Files/SetupManager.php @@ -34,9 +34,12 @@ use OC\Files\Storage\Wrapper\PermissionsMask; use OC\Files\Storage\Wrapper\Quota; use OC\Lockdown\Filesystem\NullStorage; +use OC\Share\Share; +use OC\Share20\ShareDisableChecker; use OC_App; use OC_Hook; use OC_Util; +use OCA\Files_Sharing\ISharedStorage; use OCP\Constants; use OCP\Diagnostics\IEventLogger; use OCP\EventDispatcher\IEventDispatcher; @@ -64,52 +67,33 @@ class SetupManager { private bool $rootSetup = false; - private IEventLogger $eventLogger; - private MountProviderCollection $mountProviderCollection; - private IMountManager $mountManager; - private IUserManager $userManager; // List of users for which at least one mount is setup private array $setupUsers = []; // List of users for which all mounts are setup private array $setupUsersComplete = []; /** @var array */ private array $setupUserMountProviders = []; - private IEventDispatcher $eventDispatcher; - private IUserMountCache $userMountCache; - private ILockdownManager $lockdownManager; - private IUserSession $userSession; private ICache $cache; - private LoggerInterface $logger; - private IConfig $config; private bool $listeningForProviders; private array $fullSetupRequired = []; private bool $setupBuiltinWrappersDone = false; public function __construct( - IEventLogger $eventLogger, - MountProviderCollection $mountProviderCollection, - IMountManager $mountManager, - IUserManager $userManager, - IEventDispatcher $eventDispatcher, - IUserMountCache $userMountCache, - ILockdownManager $lockdownManager, - IUserSession $userSession, + private IEventLogger $eventLogger, + private MountProviderCollection $mountProviderCollection, + private IMountManager $mountManager, + private IUserManager $userManager, + private IEventDispatcher $eventDispatcher, + private IUserMountCache $userMountCache, + private ILockdownManager $lockdownManager, + private IUserSession $userSession, ICacheFactory $cacheFactory, - LoggerInterface $logger, - IConfig $config + private LoggerInterface $logger, + private IConfig $config, + private ShareDisableChecker $shareDisableChecker, ) { - $this->eventLogger = $eventLogger; - $this->mountProviderCollection = $mountProviderCollection; - $this->mountManager = $mountManager; - $this->userManager = $userManager; - $this->eventDispatcher = $eventDispatcher; - $this->userMountCache = $userMountCache; - $this->lockdownManager = $lockdownManager; - $this->logger = $logger; - $this->userSession = $userSession; $this->cache = $cacheFactory->createDistributed('setupmanager::'); $this->listeningForProviders = false; - $this->config = $config; $this->setupListeners(); } @@ -139,15 +123,23 @@ private function setupBuiltinWrappers() { return $storage; }); - Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, IStorage $storage, IMountPoint $mount) { - if (!$mount->getOption('enable_sharing', true)) { - return new PermissionsMask([ - 'storage' => $storage, - 'mask' => Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE, - ]); + $reSharingEnabled = Share::isResharingAllowed(); + $user = $this->userSession->getUser(); + $sharingEnabledForUser = $user ? !$this->shareDisableChecker->sharingDisabledForUser($user->getUID()) : true; + Filesystem::addStorageWrapper( + 'sharing_mask', + function ($mountPoint, IStorage $storage, IMountPoint $mount) use ($reSharingEnabled, $sharingEnabledForUser) { + $sharingEnabledForMount = $mount->getOption('enable_sharing', true); + $isShared = $storage->instanceOfStorage(ISharedStorage::class); + if (!$sharingEnabledForMount || !$sharingEnabledForUser || (!$reSharingEnabled && $isShared)) { + return new PermissionsMask([ + 'storage' => $storage, + 'mask' => Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE, + ]); + } + return $storage; } - return $storage; - }); + ); // install storage availability wrapper, before most other wrappers Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, IStorage $storage) { @@ -164,7 +156,8 @@ private function setupBuiltinWrappers() { return $storage; }); - Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) { + $quotaIncludeExternal = $this->config->getSystemValue('quota_include_external_storage', false); + Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) use ($quotaIncludeExternal) { // set up quota for home storages, even for other users // which can happen when using sharing @@ -176,7 +169,7 @@ private function setupBuiltinWrappers() { $user = $storage->getUser(); return new Quota(['storage' => $storage, 'quotaCallback' => function () use ($user) { return OC_Util::getUserQuota($user); - }, 'root' => 'files']); + }, 'root' => 'files', 'include_external_storage' => $quotaIncludeExternal]); } } diff --git a/lib/private/Files/SetupManagerFactory.php b/lib/private/Files/SetupManagerFactory.php index 1d9efbd411fbe..8589cbdea42a4 100644 --- a/lib/private/Files/SetupManagerFactory.php +++ b/lib/private/Files/SetupManagerFactory.php @@ -23,6 +23,7 @@ namespace OC\Files; +use OC\Share20\ShareDisableChecker; use OCP\Diagnostics\IEventLogger; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Config\IMountProviderCollection; @@ -36,40 +37,21 @@ use Psr\Log\LoggerInterface; class SetupManagerFactory { - private IEventLogger $eventLogger; - private IMountProviderCollection $mountProviderCollection; - private IUserManager $userManager; - private IEventDispatcher $eventDispatcher; - private IUserMountCache $userMountCache; - private ILockdownManager $lockdownManager; - private IUserSession $userSession; private ?SetupManager $setupManager; - private ICacheFactory $cacheFactory; - private LoggerInterface $logger; - private IConfig $config; public function __construct( - IEventLogger $eventLogger, - IMountProviderCollection $mountProviderCollection, - IUserManager $userManager, - IEventDispatcher $eventDispatcher, - IUserMountCache $userMountCache, - ILockdownManager $lockdownManager, - IUserSession $userSession, - ICacheFactory $cacheFactory, - LoggerInterface $logger, - IConfig $config + private IEventLogger $eventLogger, + private IMountProviderCollection $mountProviderCollection, + private IUserManager $userManager, + private IEventDispatcher $eventDispatcher, + private IUserMountCache $userMountCache, + private ILockdownManager $lockdownManager, + private IUserSession $userSession, + private ICacheFactory $cacheFactory, + private LoggerInterface $logger, + private IConfig $config, + private ShareDisableChecker $shareDisableChecker, ) { - $this->eventLogger = $eventLogger; - $this->mountProviderCollection = $mountProviderCollection; - $this->userManager = $userManager; - $this->eventDispatcher = $eventDispatcher; - $this->userMountCache = $userMountCache; - $this->lockdownManager = $lockdownManager; - $this->userSession = $userSession; - $this->cacheFactory = $cacheFactory; - $this->logger = $logger; - $this->config = $config; $this->setupManager = null; } @@ -86,7 +68,8 @@ public function create(IMountManager $mountManager): SetupManager { $this->userSession, $this->cacheFactory, $this->logger, - $this->config + $this->config, + $this->shareDisableChecker, ); } return $this->setupManager; diff --git a/lib/private/Files/Storage/Home.php b/lib/private/Files/Storage/Home.php index 5427bc425c262..5100b15215b9a 100644 --- a/lib/private/Files/Storage/Home.php +++ b/lib/private/Files/Storage/Home.php @@ -26,6 +26,7 @@ namespace OC\Files\Storage; use OC\Files\Cache\HomePropagator; +use OCP\IUser; /** * Specialized version of Local storage for home directory usage @@ -94,7 +95,7 @@ public function getPropagator($storage = null) { * * @return \OC\User\User owner of this home storage */ - public function getUser() { + public function getUser(): IUser { return $this->user; } diff --git a/lib/private/Files/Storage/Wrapper/Quota.php b/lib/private/Files/Storage/Wrapper/Quota.php index cfb2c455a2ce8..b13aa6f9f04e4 100644 --- a/lib/private/Files/Storage/Wrapper/Quota.php +++ b/lib/private/Files/Storage/Wrapper/Quota.php @@ -45,6 +45,7 @@ class Quota extends Wrapper { protected int|float|null $quota; protected string $sizeRoot; private SystemConfig $config; + private bool $quotaIncludeExternalStorage; /** * @param array $parameters @@ -54,7 +55,7 @@ public function __construct($parameters) { $this->quota = $parameters['quota'] ?? null; $this->quotaCallback = $parameters['quotaCallback'] ?? null; $this->sizeRoot = $parameters['root'] ?? ''; - $this->config = \OC::$server->get(SystemConfig::class); + $this->quotaIncludeExternalStorage = $parameters['include_external_storage'] ?? false; } /** @@ -82,7 +83,7 @@ private function hasQuota(): bool { * @return int|float */ protected function getSize($path, $storage = null) { - if ($this->config->getValue('quota_include_external_storage', false)) { + if ($this->quotaIncludeExternalStorage) { $rootInfo = Filesystem::getFileInfo('', 'ext'); if ($rootInfo) { return $rootInfo->getSize(true); diff --git a/lib/private/Server.php b/lib/private/Server.php index ea30f456e296e..b1f81ef3fd5b3 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -147,6 +147,7 @@ use OC\Security\VerificationToken\VerificationToken; use OC\Session\CryptoWrapper; use OC\Share20\ProviderFactory; +use OC\Share20\ShareDisableChecker; use OC\Share20\ShareHelper; use OC\SystemTag\ManagerFactory as SystemTagManagerFactory; use OC\Tagging\TagMapper; @@ -1308,7 +1309,8 @@ public function __construct($webRoot, \OC\Config $config) { $c->get('ThemingDefaults'), $c->get(IEventDispatcher::class), $c->get(IUserSession::class), - $c->get(KnownUserService::class) + $c->get(KnownUserService::class), + $c->get(ShareDisableChecker::class) ); return $manager; diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index f84ed1671ba71..5e60a950fdb76 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -41,7 +41,6 @@ */ namespace OC\Share20; -use OCP\Cache\CappedMemoryCache; use OC\Files\Mount\MoveableMount; use OC\KnownUser\KnownUserService; use OC\Share20\Exception\ProviderException; @@ -103,8 +102,6 @@ class Manager implements IManager { private $userManager; /** @var IRootFolder */ private $rootFolder; - /** @var CappedMemoryCache */ - private $sharingDisabledForUsersCache; /** @var EventDispatcherInterface */ private $legacyDispatcher; /** @var LegacyHooks */ @@ -121,6 +118,7 @@ class Manager implements IManager { private $userSession; /** @var KnownUserService */ private $knownUserService; + private ShareDisableChecker $shareDisableChecker; public function __construct( LoggerInterface $logger, @@ -140,7 +138,8 @@ public function __construct( \OC_Defaults $defaults, IEventDispatcher $dispatcher, IUserSession $userSession, - KnownUserService $knownUserService + KnownUserService $knownUserService, + ShareDisableChecker $shareDisableChecker ) { $this->logger = $logger; $this->config = $config; @@ -154,7 +153,6 @@ public function __construct( $this->userManager = $userManager; $this->rootFolder = $rootFolder; $this->legacyDispatcher = $legacyDispatcher; - $this->sharingDisabledForUsersCache = new CappedMemoryCache(); // The constructor of LegacyHooks registers the listeners of share events // do not remove if those are not properly migrated $this->legacyHooks = new LegacyHooks($this->legacyDispatcher); @@ -164,6 +162,7 @@ public function __construct( $this->dispatcher = $dispatcher; $this->userSession = $userSession; $this->knownUserService = $knownUserService; + $this->shareDisableChecker = $shareDisableChecker; } /** @@ -2016,37 +2015,7 @@ public function currentUserCanEnumerateTargetUser(?IUser $currentUser, IUser $ta * @return bool */ public function sharingDisabledForUser($userId) { - if ($userId === null) { - return false; - } - - if (isset($this->sharingDisabledForUsersCache[$userId])) { - return $this->sharingDisabledForUsersCache[$userId]; - } - - if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') { - $groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', ''); - $excludedGroups = json_decode($groupsList); - if (is_null($excludedGroups)) { - $excludedGroups = explode(',', $groupsList); - $newValue = json_encode($excludedGroups); - $this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue); - } - $user = $this->userManager->get($userId); - $usersGroups = $this->groupManager->getUserGroupIds($user); - if (!empty($usersGroups)) { - $remainingGroups = array_diff($usersGroups, $excludedGroups); - // if the user is only in groups which are disabled for sharing then - // sharing is also disabled for the user - if (empty($remainingGroups)) { - $this->sharingDisabledForUsersCache[$userId] = true; - return true; - } - } - } - - $this->sharingDisabledForUsersCache[$userId] = false; - return false; + return $this->shareDisableChecker->sharingDisabledForUser($userId); } /** diff --git a/lib/private/Share20/ShareDisableChecker.php b/lib/private/Share20/ShareDisableChecker.php new file mode 100644 index 0000000000000..9d0c2b8c2b480 --- /dev/null +++ b/lib/private/Share20/ShareDisableChecker.php @@ -0,0 +1,65 @@ +sharingDisabledForUsersCache = new CappedMemoryCache(); + } + + + /** + * @param ?string $userId + * @return bool + */ + public function sharingDisabledForUser(?string $userId) { + if ($userId === null) { + return false; + } + + if (isset($this->sharingDisabledForUsersCache[$userId])) { + return $this->sharingDisabledForUsersCache[$userId]; + } + + if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') { + $groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', ''); + $excludedGroups = json_decode($groupsList); + if (is_null($excludedGroups)) { + $excludedGroups = explode(',', $groupsList); + $newValue = json_encode($excludedGroups); + $this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue); + } + $user = $this->userManager->get($userId); + if (!$user) { + return false; + } + $usersGroups = $this->groupManager->getUserGroupIds($user); + if (!empty($usersGroups)) { + $remainingGroups = array_diff($usersGroups, $excludedGroups); + // if the user is only in groups which are disabled for sharing then + // sharing is also disabled for the user + if (empty($remainingGroups)) { + $this->sharingDisabledForUsersCache[$userId] = true; + return true; + } + } + } + + $this->sharingDisabledForUsersCache[$userId] = false; + return false; + } +} diff --git a/lib/private/legacy/OC_Helper.php b/lib/private/legacy/OC_Helper.php index 8d708118b964b..843128ab14837 100644 --- a/lib/private/legacy/OC_Helper.php +++ b/lib/private/legacy/OC_Helper.php @@ -57,6 +57,8 @@ */ class OC_Helper { private static $templateManager; + private static ?ICacheFactory $cacheFactory = null; + private static ?bool $quotaIncludeExternalStorage = null; /** * Make a human file size @@ -462,12 +464,15 @@ public static function findBinaryPath(string $program): ?string { * @throws \OCP\Files\NotFoundException */ public static function getStorageInfo($path, $rootInfo = null, $includeMountPoints = true, $useCache = true) { - /** @var ICacheFactory $cacheFactory */ - $cacheFactory = \OC::$server->get(ICacheFactory::class); - $memcache = $cacheFactory->createLocal('storage_info'); + if (!self::$cacheFactory) { + self::$cacheFactory = \OC::$server->get(ICacheFactory::class); + } + $memcache = self::$cacheFactory->createLocal('storage_info'); // return storage info without adding mount points - $includeExtStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false); + if (self::$quotaIncludeExternalStorage === null) { + self::$quotaIncludeExternalStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false); + } $view = Filesystem::getView(); if (!$view) { @@ -484,7 +489,7 @@ public static function getStorageInfo($path, $rootInfo = null, $includeMountPoin } if (!$rootInfo) { - $rootInfo = \OC\Files\Filesystem::getFileInfo($path, $includeExtStorage ? 'ext' : false); + $rootInfo = \OC\Files\Filesystem::getFileInfo($path, self::$quotaIncludeExternalStorage ? 'ext' : false); } if (!$rootInfo instanceof \OCP\Files\FileInfo) { throw new \OCP\Files\NotFoundException(); @@ -498,9 +503,9 @@ public static function getStorageInfo($path, $rootInfo = null, $includeMountPoin $storage = $mount->getStorage(); $sourceStorage = $storage; if ($storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) { - $includeExtStorage = false; + self::$quotaIncludeExternalStorage = false; } - if ($includeExtStorage) { + if (self::$quotaIncludeExternalStorage) { if ($storage->instanceOfStorage('\OC\Files\Storage\Home') || $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage') ) { diff --git a/lib/public/Files/DavUtil.php b/lib/public/Files/DavUtil.php index 2e7efdccd071b..b0e730b0f8eef 100644 --- a/lib/public/Files/DavUtil.php +++ b/lib/public/Files/DavUtil.php @@ -59,23 +59,24 @@ public static function getDavFileId(int $id): string { * @since 25.0.0 */ public static function getDavPermissions(FileInfo $info): string { + $permissions = $info->getPermissions(); $p = ''; if ($info->isShared()) { $p .= 'S'; } - if ($info->isShareable()) { + if ($permissions & Constants::PERMISSION_SHARE) { $p .= 'R'; } if ($info->isMounted()) { $p .= 'M'; } - if ($info->isReadable()) { + if ($permissions & Constants::PERMISSION_READ) { $p .= 'G'; } - if ($info->isDeletable()) { + if ($permissions & Constants::PERMISSION_DELETE) { $p .= 'D'; } - if ($info->isUpdateable()) { + if ($permissions & Constants::PERMISSION_UPDATE) { $p .= 'NV'; // Renameable, Movable } @@ -86,7 +87,7 @@ public static function getDavPermissions(FileInfo $info): string { $rootEntry = $storage->getCache()->get(''); $isWritable = $rootEntry->getPermissions() & Constants::PERMISSION_UPDATE; } else { - $isWritable = $info->isUpdateable(); + $isWritable = $permissions & Constants::PERMISSION_UPDATE; } if ($info->getType() === FileInfo::TYPE_FILE) { @@ -94,7 +95,7 @@ public static function getDavPermissions(FileInfo $info): string { $p .= 'W'; } } else { - if ($info->isCreatable()) { + if ($permissions & Constants::PERMISSION_CREATE) { $p .= 'CK'; } } diff --git a/lib/public/Files/IHomeStorage.php b/lib/public/Files/IHomeStorage.php index 7eb3ffc4a24ec..1fea80f2d87c3 100644 --- a/lib/public/Files/IHomeStorage.php +++ b/lib/public/Files/IHomeStorage.php @@ -27,6 +27,7 @@ namespace OCP\Files; use OCP\Files\Storage\IStorage; +use OCP\IUser; /** * Interface IHomeStorage @@ -34,4 +35,11 @@ * @since 7.0.0 */ interface IHomeStorage extends IStorage { + /** + * Get the user for this home storage + * + * @return IUser + * @since 28.0.0 + */ + public function getUser(): IUser; } diff --git a/tests/lib/Files/ViewTest.php b/tests/lib/Files/ViewTest.php index 7a449c4893bea..d859b249b75da 100644 --- a/tests/lib/Files/ViewTest.php +++ b/tests/lib/Files/ViewTest.php @@ -7,6 +7,7 @@ namespace Test\Files; +use OC\Share20\ShareDisableChecker; use OCP\Cache\CappedMemoryCache; use OC\Files\Cache\Watcher; use OC\Files\Filesystem; @@ -22,6 +23,7 @@ use OCP\Files\GenericFileException; use OCP\Files\Mount\IMountManager; use OCP\Files\Storage\IStorage; +use OCP\IDBConnection; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; use OCP\Share\IShare; @@ -295,7 +297,7 @@ public function sharingDisabledPermissionProvider() { */ public function testRemoveSharePermissionWhenSharingDisabledForUser($excludeGroups, $excludeGroupsList, $expectedShareable) { // Reset sharing disabled for users cache - self::invokePrivate(\OC::$server->getShareManager(), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]); + self::invokePrivate(\OC::$server->get(ShareDisableChecker::class), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]); $config = \OC::$server->getConfig(); $oldExcludeGroupsFlag = $config->getAppValue('core', 'shareapi_exclude_groups', 'no'); @@ -320,7 +322,7 @@ public function testRemoveSharePermissionWhenSharingDisabledForUser($excludeGrou $config->setAppValue('core', 'shareapi_exclude_groups_list', $oldExcludeGroupsList); // Reset sharing disabled for users cache - self::invokePrivate(\OC::$server->getShareManager(), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]); + self::invokePrivate(\OC::$server->get(ShareDisableChecker::class), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]); } public function testCacheIncompleteFolder() { @@ -1591,7 +1593,7 @@ private function createTestMovableMountPoints($mountPoints) { ->getMock(); $storage->method('getId')->willReturn('non-null-id'); $storage->method('getStorageCache')->willReturnCallback(function () use ($storage) { - return new \OC\Files\Cache\Storage($storage); + return new \OC\Files\Cache\Storage($storage, true, \OC::$server->get(IDBConnection::class)); }); $mounts[] = $this->getMockBuilder(TestMoveableMountPoint::class) diff --git a/tests/lib/HelperStorageTest.php b/tests/lib/HelperStorageTest.php index d3f480502b2d8..0643c4b68a49f 100644 --- a/tests/lib/HelperStorageTest.php +++ b/tests/lib/HelperStorageTest.php @@ -10,6 +10,7 @@ use OC\Files\Storage\Temporary; use OCP\Files\Mount\IMountManager; +use OCP\IConfig; use Test\Traits\UserTrait; /** @@ -26,12 +27,14 @@ class HelperStorageTest extends \Test\TestCase { private $storageMock; /** @var \OC\Files\Storage\Storage */ private $storage; + private bool $savedQuotaIncludeExternalStorage; protected function setUp(): void { parent::setUp(); $this->user = $this->getUniqueID('user_'); $this->createUser($this->user, $this->user); + $this->savedQuotaIncludeExternalStorage = $this->getIncludeExternalStorage(); \OC\Files\Filesystem::tearDown(); \OC_User::setUserId($this->user); @@ -45,6 +48,7 @@ protected function setUp(): void { } protected function tearDown(): void { + $this->setIncludeExternalStorage($this->savedQuotaIncludeExternalStorage); $this->user = null; if ($this->storageMock) { @@ -91,6 +95,19 @@ public function testGetStorageInfo() { $this->assertEquals(5, $storageInfo['used']); $this->assertEquals(17, $storageInfo['total']); } + private function getIncludeExternalStorage(): bool { + $class = new \ReflectionClass(\OC_Helper::class); + $prop = $class->getProperty('quotaIncludeExternalStorage'); + $prop->setAccessible(true); + return $prop->getValue(null) ?? false; + } + + private function setIncludeExternalStorage(bool $include) { + $class = new \ReflectionClass(\OC_Helper::class); + $prop = $class->getProperty('quotaIncludeExternalStorage'); + $prop->setAccessible(true); + $prop->setValue(null, $include); + } /** * Test getting the storage info, ignoring extra mount points @@ -104,8 +121,7 @@ public function testGetStorageInfoExcludingExtStorage() { $extStorage->file_put_contents('extfile.txt', 'abcdefghijklmnopq'); $extStorage->getScanner()->scan(''); // update root size - $config = \OC::$server->getConfig(); - $config->setSystemValue('quota_include_external_storage', false); + $this->setIncludeExternalStorage(false); \OC\Files\Filesystem::mount($extStorage, [], '/' . $this->user . '/files/ext'); @@ -129,10 +145,9 @@ public function testGetStorageInfoIncludingExtStorage() { \OC\Files\Filesystem::mount($extStorage, [], '/' . $this->user . '/files/ext'); - $config = \OC::$server->getConfig(); - $oldConfig = $config->getSystemValue('quota_include_external_storage', false); - $config->setSystemValue('quota_include_external_storage', 'true'); + $this->setIncludeExternalStorage(true); + $config = \OC::$server->get(IConfig::class); $config->setUserValue($this->user, 'files', 'quota', '25'); $storageInfo = \OC_Helper::getStorageInfo(''); @@ -140,7 +155,6 @@ public function testGetStorageInfoIncludingExtStorage() { $this->assertEquals(22, $storageInfo['used']); $this->assertEquals(25, $storageInfo['total']); - $config->setSystemValue('quota_include_external_storage', $oldConfig); $config->setUserValue($this->user, 'files', 'quota', 'default'); } @@ -161,15 +175,12 @@ public function testGetStorageInfoIncludingExtStorageWithNoUserQuota() { \OC\Files\Filesystem::mount($extStorage, [], '/' . $this->user . '/files/ext'); $config = \OC::$server->getConfig(); - $oldConfig = $config->getSystemValue('quota_include_external_storage', false); - $config->setSystemValue('quota_include_external_storage', 'true'); + $this->setIncludeExternalStorage(true); $storageInfo = \OC_Helper::getStorageInfo(''); $this->assertEquals(12, $storageInfo['free'], '12 bytes free in home storage'); $this->assertEquals(22, $storageInfo['used'], '5 bytes of home storage and 17 bytes of the temporary storage are used'); $this->assertEquals(34, $storageInfo['total'], '5 bytes used and 12 bytes free in home storage as well as 17 bytes used in temporary storage'); - - $config->setSystemValue('quota_include_external_storage', $oldConfig); } diff --git a/tests/lib/Share20/ManagerTest.php b/tests/lib/Share20/ManagerTest.php index 36680b7b926b5..1cce8bbd2928f 100644 --- a/tests/lib/Share20/ManagerTest.php +++ b/tests/lib/Share20/ManagerTest.php @@ -27,6 +27,7 @@ use OC\Share20\Exception; use OC\Share20\Manager; use OC\Share20\Share; +use OC\Share20\ShareDisableChecker; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\File; @@ -110,6 +111,8 @@ class ManagerTest extends \Test\TestCase { protected $userSession; /** @var KnownUserService|MockObject */ protected $knownUserService; + /** @var ShareDisableChecker|MockObject */ + protected $shareDisabledChecker; protected function setUp(): void { $this->logger = $this->createMock(LoggerInterface::class); @@ -128,6 +131,8 @@ protected function setUp(): void { $this->userSession = $this->createMock(IUserSession::class); $this->knownUserService = $this->createMock(KnownUserService::class); + $this->shareDisabledChecker = new ShareDisableChecker($this->config, $this->userManager, $this->groupManager); + $this->l10nFactory = $this->createMock(IFactory::class); $this->l = $this->createMock(IL10N::class); $this->l->method('t') @@ -159,7 +164,8 @@ protected function setUp(): void { $this->defaults, $this->dispatcher, $this->userSession, - $this->knownUserService + $this->knownUserService, + $this->shareDisabledChecker ); $this->defaultProvider = $this->createMock(DefaultShareProvider::class); @@ -190,7 +196,8 @@ private function createManagerMock() { $this->defaults, $this->dispatcher, $this->userSession, - $this->knownUserService + $this->knownUserService, + $this->shareDisabledChecker, ]); } @@ -2739,7 +2746,8 @@ public function testGetShareByToken() { $this->defaults, $this->dispatcher, $this->userSession, - $this->knownUserService + $this->knownUserService, + $this->shareDisabledChecker, ); $share = $this->createMock(IShare::class); @@ -2785,7 +2793,8 @@ public function testGetShareByTokenRoom() { $this->defaults, $this->dispatcher, $this->userSession, - $this->knownUserService + $this->knownUserService, + $this->shareDisabledChecker, ); $share = $this->createMock(IShare::class); @@ -2838,7 +2847,8 @@ public function testGetShareByTokenWithException() { $this->defaults, $this->dispatcher, $this->userSession, - $this->knownUserService + $this->knownUserService, + $this->shareDisabledChecker, ); $share = $this->createMock(IShare::class); @@ -4180,7 +4190,8 @@ public function testShareProviderExists($shareType, $expected) { $this->defaults, $this->dispatcher, $this->userSession, - $this->knownUserService + $this->knownUserService, + $this->shareDisabledChecker, ); $this->assertSame($expected, $manager->shareProviderExists($shareType) @@ -4215,7 +4226,8 @@ public function testGetSharesInFolder() { $this->defaults, $this->dispatcher, $this->userSession, - $this->knownUserService + $this->knownUserService, + $this->shareDisabledChecker, ); $factory->setProvider($this->defaultProvider); @@ -4281,7 +4293,8 @@ public function testGetAccessList() { $this->defaults, $this->dispatcher, $this->userSession, - $this->knownUserService + $this->knownUserService, + $this->shareDisabledChecker, ); $factory->setProvider($this->defaultProvider); @@ -4399,7 +4412,8 @@ public function testGetAccessListWithCurrentAccess() { $this->defaults, $this->dispatcher, $this->userSession, - $this->knownUserService + $this->knownUserService, + $this->shareDisabledChecker, ); $factory->setProvider($this->defaultProvider); @@ -4526,7 +4540,8 @@ public function testGetAllShares() { $this->defaults, $this->dispatcher, $this->userSession, - $this->knownUserService + $this->knownUserService, + $this->shareDisabledChecker ); $factory->setProvider($this->defaultProvider);