Skip to content

Commit

Permalink
Merge pull request #46987 from nextcloud/fix/dav-public
Browse files Browse the repository at this point in the history
fix(dav): Ensure share properties are also set on public remote endpoint
  • Loading branch information
susnux authored Aug 12, 2024
2 parents 1a7acf0 + 4bbcbc5 commit b34edf2
Show file tree
Hide file tree
Showing 18 changed files with 179 additions and 52 deletions.
7 changes: 7 additions & 0 deletions apps/dav/appinfo/v2/publicremote.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use OC\Files\Storage\Wrapper\PermissionsMask;
use OC\Files\View;
use OCA\DAV\Storage\PublicOwnerWrapper;
use OCA\DAV\Storage\PublicShareWrapper;
use OCA\FederatedFileSharing\FederatedShareProvider;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Mount\IMountManager;
Expand Down Expand Up @@ -98,6 +99,12 @@
return new PublicOwnerWrapper(['storage' => $storage, 'owner' => $share->getShareOwner()]);
});

// Ensure that also private shares have the `getShare` method
/** @psalm-suppress MissingClosureParamType */
Filesystem::addStorageWrapper('getShare', function ($mountPoint, $storage) use ($share) {
return new PublicShareWrapper(['storage' => $storage, 'share' => $share]);
}, 0);

/** @psalm-suppress InternalMethod */
Filesystem::logWarningWhenAddingStorageWrapper($previousLog);

Expand Down
1 change: 1 addition & 0 deletions apps/dav/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@
'OCA\\DAV\\SetupChecks\\NeedsSystemAddressBookSync' => $baseDir . '/../lib/SetupChecks/NeedsSystemAddressBookSync.php',
'OCA\\DAV\\SetupChecks\\WebdavEndpoint' => $baseDir . '/../lib/SetupChecks/WebdavEndpoint.php',
'OCA\\DAV\\Storage\\PublicOwnerWrapper' => $baseDir . '/../lib/Storage/PublicOwnerWrapper.php',
'OCA\\DAV\\Storage\\PublicShareWrapper' => $baseDir . '/../lib/Storage/PublicShareWrapper.php',
'OCA\\DAV\\SystemTag\\SystemTagList' => $baseDir . '/../lib/SystemTag/SystemTagList.php',
'OCA\\DAV\\SystemTag\\SystemTagMappingNode' => $baseDir . '/../lib/SystemTag/SystemTagMappingNode.php',
'OCA\\DAV\\SystemTag\\SystemTagNode' => $baseDir . '/../lib/SystemTag/SystemTagNode.php',
Expand Down
1 change: 1 addition & 0 deletions apps/dav/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\SetupChecks\\NeedsSystemAddressBookSync' => __DIR__ . '/..' . '/../lib/SetupChecks/NeedsSystemAddressBookSync.php',
'OCA\\DAV\\SetupChecks\\WebdavEndpoint' => __DIR__ . '/..' . '/../lib/SetupChecks/WebdavEndpoint.php',
'OCA\\DAV\\Storage\\PublicOwnerWrapper' => __DIR__ . '/..' . '/../lib/Storage/PublicOwnerWrapper.php',
'OCA\\DAV\\Storage\\PublicShareWrapper' => __DIR__ . '/..' . '/../lib/Storage/PublicShareWrapper.php',
'OCA\\DAV\\SystemTag\\SystemTagList' => __DIR__ . '/..' . '/../lib/SystemTag/SystemTagList.php',
'OCA\\DAV\\SystemTag\\SystemTagMappingNode' => __DIR__ . '/..' . '/../lib/SystemTag/SystemTagMappingNode.php',
'OCA\\DAV\\SystemTag\\SystemTagNode' => __DIR__ . '/..' . '/../lib/SystemTag/SystemTagNode.php',
Expand Down
9 changes: 3 additions & 6 deletions apps/dav/lib/Connector/Sabre/FilesPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -345,17 +345,14 @@ public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node)
return $node->getNode()->getInternalPath() === '' ? 'true' : 'false';
});

$propFind->handle(self::SHARE_NOTE, function () use ($node, $httpRequest): ?string {
$propFind->handle(self::SHARE_NOTE, function () use ($node): ?string {
$user = $this->userSession->getUser();
if ($user === null) {
return null;
}
return $node->getNoteFromShare(
$user->getUID()
$user?->getUID()
);
});

$propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function () use ($node) {
$propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function () {
return $this->config->getSystemValue('data-fingerprint', '');
});
$propFind->handle(self::CREATIONDATE_PROPERTYNAME, function () use ($node) {
Expand Down
52 changes: 24 additions & 28 deletions apps/dav/lib/Connector/Sabre/Node.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
use OCP\Files\DavUtil;
use OCP\Files\FileInfo;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Files\Storage\ISharedStorage;
use OCP\Files\StorageNotAvailableException;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager;
Expand Down Expand Up @@ -261,8 +263,8 @@ public function getSharePermissions($user) {
$storage = null;
}

if ($storage && $storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
/** @var \OCA\Files_Sharing\SharedStorage $storage */
if ($storage && $storage->instanceOfStorage(ISharedStorage::class)) {
/** @var ISharedStorage $storage */
$permissions = (int)$storage->getShare()->getPermissions();
} else {
$permissions = $this->info->getPermissions();
Expand Down Expand Up @@ -298,16 +300,15 @@ public function getSharePermissions($user) {
* @return array
*/
public function getShareAttributes(): array {
$attributes = [];

try {
$storage = $this->info->getStorage();
} catch (StorageNotAvailableException $e) {
$storage = null;
$storage = $this->node->getStorage();
} catch (NotFoundException $e) {
return [];
}

if ($storage && $storage->instanceOfStorage(\OCA\Files_Sharing\SharedStorage::class)) {
/** @var \OCA\Files_Sharing\SharedStorage $storage */
$attributes = [];
if ($storage->instanceOfStorage(ISharedStorage::class)) {
/** @var ISharedStorage $storage */
$attributes = $storage->getShare()->getAttributes();
if ($attributes === null) {
return [];
Expand All @@ -319,29 +320,24 @@ public function getShareAttributes(): array {
return $attributes;
}

/**
* @param string $user
* @return string
*/
public function getNoteFromShare($user) {
if ($user === null) {
return '';
public function getNoteFromShare(?string $user): ?string {
try {
$storage = $this->node->getStorage();
} catch (NotFoundException) {
return null;
}

// Retrieve note from the share object already loaded into
// memory, to avoid additional database queries.
$storage = $this->getNode()->getStorage();
if (!$storage->instanceOfStorage(\OCA\Files_Sharing\SharedStorage::class)) {
return '';
if ($storage->instanceOfStorage(ISharedStorage::class)) {
/** @var ISharedStorage $storage */
$share = $storage->getShare();
if ($user === $share->getShareOwner()) {
// Note is only for recipient not the owner
return null;
}
return $share->getNote();
}
/** @var \OCA\Files_Sharing\SharedStorage $storage */

$share = $storage->getShare();
$note = $share->getNote();
if ($share->getShareOwner() !== $user) {
return $note;
}
return '';
return null;
}

/**
Expand Down
5 changes: 3 additions & 2 deletions apps/dav/lib/DAV/ViewOnlyPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use OCA\Files_Versions\Sabre\VersionFile;
use OCP\Files\Folder;
use OCP\Files\NotFoundException;
use OCP\Files\Storage\ISharedStorage;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\Server;
use Sabre\DAV\ServerPlugin;
Expand Down Expand Up @@ -81,11 +82,11 @@ public function checkViewOnly(RequestInterface $request): bool {

$storage = $node->getStorage();

if (!$storage->instanceOfStorage(\OCA\Files_Sharing\SharedStorage::class)) {
if (!$storage->instanceOfStorage(ISharedStorage::class)) {
return true;
}
// Extract extra permissions
/** @var \OCA\Files_Sharing\SharedStorage $storage */
/** @var ISharedStorage $storage */
$share = $storage->getShare();

$attributes = $share->getAttributes();
Expand Down
39 changes: 39 additions & 0 deletions apps/dav/lib/Storage/PublicShareWrapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\Storage;

use OC\Files\Storage\Wrapper\Wrapper;
use OCP\Files\Storage\ISharedStorage;
use OCP\Share\IShare;

class PublicShareWrapper extends Wrapper implements ISharedStorage {

private IShare $share;

/**
* @param array $arguments ['storage' => $storage, 'share' => $share]
*
* $storage: The storage the permissions mask should be applied on
* $share: The share to use in case no share is found
*/
public function __construct($arguments) {
parent::__construct($arguments);
$this->share = $arguments['share'];
}

public function getShare(): IShare {
$storage = parent::getWrapperStorage();
if (method_exists($storage, 'getShare')) {
/** @var ISharedStorage $storage */
return $storage->getShare();
}

return $this->share;
}
}
14 changes: 10 additions & 4 deletions apps/dav/tests/unit/Connector/Sabre/NodeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

use OC\Files\FileInfo;
use OC\Files\Mount\MountPoint;
use OC\Files\Node\Folder;
use OC\Files\View;
use OC\Share20\ShareAttributes;
use OCA\Files_Sharing\SharedMount;
Expand All @@ -21,6 +22,7 @@
use OCP\ICache;
use OCP\Share\IManager;
use OCP\Share\IShare;
use PHPUnit\Framework\MockObject\MockObject;

/**
* Class NodeTest
Expand Down Expand Up @@ -201,14 +203,16 @@ public function testShareAttributes(): void {

$share->expects($this->once())->method('getAttributes')->willReturn($attributes);

$info = $this->getMockBuilder(FileInfo::class)
/** @var Folder&MockObject $info */
$info = $this->getMockBuilder(Folder::class)
->disableOriginalConstructor()
->setMethods(['getStorage', 'getType'])
->onlyMethods(['getStorage', 'getType'])
->getMock();

$info->method('getStorage')->willReturn($storage);
$info->method('getType')->willReturn(FileInfo::TYPE_FOLDER);

/** @var View&MockObject $view */
$view = $this->getMockBuilder(View::class)
->disableOriginalConstructor()
->getMock();
Expand All @@ -225,14 +229,16 @@ public function testShareAttributesNonShare(): void {

$shareManager = $this->getMockBuilder(IManager::class)->disableOriginalConstructor()->getMock();

$info = $this->getMockBuilder(FileInfo::class)
/** @var Folder&MockObject */
$info = $this->getMockBuilder(Folder::class)
->disableOriginalConstructor()
->setMethods(['getStorage', 'getType'])
->onlyMethods(['getStorage', 'getType'])
->getMock();

$info->method('getStorage')->willReturn($storage);
$info->method('getType')->willReturn(FileInfo::TYPE_FOLDER);

/** @var View&MockObject */
$view = $this->getMockBuilder(View::class)
->disableOriginalConstructor()
->getMock();
Expand Down
5 changes: 3 additions & 2 deletions apps/dav/tests/unit/DAV/ViewOnlyPluginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use OCA\Files_Versions\Versions\IVersion;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\Storage\ISharedStorage;
use OCP\Files\Storage\IStorage;
use OCP\IUser;
use OCP\Share\IAttributes;
Expand Down Expand Up @@ -65,7 +66,7 @@ public function testCanGetNonShared(): void {

$storage = $this->createMock(IStorage::class);
$file->method('getStorage')->willReturn($storage);
$storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(false);
$storage->method('instanceOfStorage')->with(ISharedStorage::class)->willReturn(false);

$this->assertTrue($this->plugin->checkViewOnly($this->request));
}
Expand Down Expand Up @@ -140,7 +141,7 @@ public function testCanGet(bool $isVersion, ?bool $attrEnabled, bool $expectCanD
$nodeInfo->expects($this->once())
->method('getStorage')
->willReturn($storage);
$storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
$storage->method('instanceOfStorage')->with(ISharedStorage::class)->willReturn(true);
$storage->method('getShare')->willReturn($share);

$extAttr = $this->createMock(IAttributes::class);
Expand Down
3 changes: 3 additions & 0 deletions apps/files_sharing/lib/ISharedStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@

use OCP\Files\Storage\IStorage;

/**
* @deprecated 30.0.0 use `\OCP\Files\Storage\ISharedStorage` instead
*/
interface ISharedStorage extends IStorage {
}
5 changes: 3 additions & 2 deletions apps/files_sharing/lib/SharedStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@
use OC\Files\Storage\Wrapper\Wrapper;
use OC\User\NoUserException;
use OCA\Files_External\Config\ConfigAdapter;
use OCA\Files_Sharing\ISharedStorage as LegacyISharedStorage;
use OCP\Constants;
use OCP\Files\Cache\ICacheEntry;
use OCP\Files\Config\IUserMountCache;
use OCP\Files\Folder;
use OCP\Files\IHomeStorage;
use OCP\Files\IRootFolder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\Files\Storage\IDisableEncryptionStorage;
use OCP\Files\Storage\ISharedStorage;
use OCP\Files\Storage\IStorage;
use OCP\Lock\ILockingProvider;
use OCP\Share\IShare;
Expand All @@ -35,7 +36,7 @@
/**
* Convert target path to source path and pass the function call to the correct storage provider
*/
class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements ISharedStorage, IDisableEncryptionStorage {
class SharedStorage extends \OC\Files\Storage\Wrapper\Jail implements LegacyISharedStorage, ISharedStorage, IDisableEncryptionStorage {
/** @var \OCP\Share\IShare */
private $superShare;

Expand Down
7 changes: 3 additions & 4 deletions apps/files_versions/lib/Versions/LegacyVersionsBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
use Exception;
use OC\Files\View;
use OCA\DAV\Connector\Sabre\Exception\Forbidden;
use OCA\Files_Sharing\ISharedStorage;
use OCA\Files_Sharing\SharedStorage;
use OCA\Files_Versions\Db\VersionEntity;
use OCA\Files_Versions\Db\VersionsMapper;
use OCA\Files_Versions\Storage;
Expand All @@ -24,6 +22,7 @@
use OCP\Files\IRootFolder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\Files\Storage\ISharedStorage;
use OCP\Files\Storage\IStorage;
use OCP\IUser;
use OCP\IUserManager;
Expand All @@ -48,7 +47,7 @@ public function useBackendForStorage(IStorage $storage): bool {
public function getVersionsForFile(IUser $user, FileInfo $file): array {
$storage = $file->getStorage();

if ($storage->instanceOfStorage(SharedStorage::class)) {
if ($storage->instanceOfStorage(ISharedStorage::class)) {
$owner = $storage->getOwner('');
$user = $this->userManager->get($owner);

Expand Down Expand Up @@ -192,7 +191,7 @@ public function getVersionFile(IUser $user, FileInfo $sourceFile, $revision): Fi

// Shared files have their versions in the owners root folder so we need to obtain them from there
if ($storage->instanceOfStorage(ISharedStorage::class) && $owner) {
/** @var SharedStorage $storage */
/** @var ISharedStorage $storage */
$userFolder = $this->rootFolder->getUserFolder($owner->getUID());
$user = $owner;
$ownerPathInStorage = $sourceFile->getInternalPath();
Expand Down
22 changes: 22 additions & 0 deletions build/integration/dav_features/dav-v2-public.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: AGPL-3.0-or-later
Feature: dav-v2-public
Background:
Given using api version "1"

Scenario: See note to recipient in public shares
Given using new dav path
And As an "admin"
And user "user0" exists
And user "user1" exists
And As an "user1"
And user "user1" created a folder "/testshare"
And as "user1" creating a share with
| path | testshare |
| shareType | 3 |
| permissions | 1 |
| note | Hello |
And As an "user0"
Given using new public dav path
When Requesting share note on dav endpoint
Then the single response should contain a property "{http://nextcloud.org/ns}note" with value "Hello"
Loading

0 comments on commit b34edf2

Please sign in to comment.