Skip to content

Commit 1f79d54

Browse files
Merge pull request #62159 from nextcloud/backport/61497/stable33
[stable33] fix(ViewOnlyPlugin): Allow COPY and MOVE operations within the same storage
2 parents 42b4f6d + 43db205 commit 1f79d54

2 files changed

Lines changed: 32 additions & 25 deletions

File tree

apps/dav/lib/DAV/ViewOnlyPlugin.php

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
namespace OCA\DAV\DAV;
1010

11+
use OCA\DAV\Connector\Sabre\Directory;
1112
use OCA\DAV\Connector\Sabre\Exception\Forbidden;
1213
use OCA\DAV\Connector\Sabre\File as DavFile;
1314
use OCA\Files_Versions\Sabre\VersionFile;
@@ -79,30 +80,37 @@ public function checkViewOnly(RequestInterface $request): bool {
7980
}
8081

8182
$storage = $node->getStorage();
82-
8383
if (!$storage->instanceOfStorage(ISharedStorage::class)) {
8484
return true;
8585
}
8686

87-
// Extract extra permissions
8887
/** @var ISharedStorage $storage */
8988
$share = $storage->getShare();
90-
$attributes = $share->getAttributes();
91-
if ($attributes === null) {
92-
return true;
93-
}
94-
95-
// We have two options here, if download is disabled, but viewing is allowed,
96-
// we still allow the GET request to return the file content.
97-
$canDownload = $attributes->getAttribute('permissions', 'download');
98-
if (!$share->canSeeContent()) {
99-
throw new Forbidden('Access to this shared resource has been denied because its download permission is disabled.');
100-
}
89+
switch ($request->getMethod()) {
90+
case 'GET':
91+
// If download is disabled, but viewing is allowed, we still allow the GET method to return the file content.
92+
if (!$share->canSeeContent()) {
93+
throw new Forbidden('Access to this shared resource has been denied because its download permission is disabled.');
94+
}
95+
break;
96+
case 'COPY':
97+
case 'MOVE':
98+
$destinationPath = $this->server->getCopyAndMoveInfo($request)['destination'];
99+
$destinationParentPath = dirname($destinationPath);
100+
if ($destinationParentPath === '.') {
101+
$destinationParentPath = '';
102+
}
103+
$destinationParent = $this->server->tree->getNodeForPath($destinationParentPath);
104+
// Copy and move operations within the same storage are allowed, because the destination has the same restrictions.
105+
if (($destinationParent instanceof Directory) && $destinationParent->getNode()->getStorage()->getId() === $storage->getId()) {
106+
break;
107+
}
101108

102-
// If download is disabled, we disable the COPY and MOVE methods even if the
103-
// shareapi_allow_view_without_download is set to true.
104-
if ($request->getMethod() !== 'GET' && ($canDownload !== null && !$canDownload)) {
105-
throw new Forbidden('Access to this shared resource has been denied because its download permission is disabled.');
109+
// If download is disabled, we disable the COPY and MOVE methods even if the shareapi_allow_view_without_download is set to true.
110+
if (!$share->canDownload()) {
111+
throw new Forbidden('Access to this shared resource has been denied because its download permission is disabled.');
112+
}
113+
break;
106114
}
107115
} catch (NotFound $e) {
108116
// File not found

apps/dav/tests/unit/DAV/ViewOnlyPluginTest.php

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
use OCP\Files\Storage\ISharedStorage;
2020
use OCP\Files\Storage\IStorage;
2121
use OCP\IUser;
22-
use OCP\Share\IAttributes;
2322
use OCP\Share\IShare;
2423
use PHPUnit\Framework\MockObject\MockObject;
2524
use Sabre\DAV\Server;
@@ -135,6 +134,11 @@ public function testCanGet(bool $isVersion, ?bool $attrEnabled, bool $expectCanD
135134
$davNode->method('getNode')->willReturn($nodeInfo);
136135
}
137136

137+
$this->request
138+
->expects($this->once())
139+
->method('getMethod')
140+
->willReturn('GET');
141+
138142
$this->request->expects($this->once())->method('getPath')->willReturn($davPath);
139143

140144
$this->tree->expects($this->once())
@@ -150,16 +154,11 @@ public function testCanGet(bool $isVersion, ?bool $attrEnabled, bool $expectCanD
150154
$storage->method('instanceOfStorage')->with(ISharedStorage::class)->willReturn(true);
151155
$storage->method('getShare')->willReturn($share);
152156

153-
$extAttr = $this->createMock(IAttributes::class);
154-
$share->method('getAttributes')->willReturn($extAttr);
155-
$extAttr->expects($this->once())
156-
->method('getAttribute')
157-
->with('permissions', 'download')
158-
->willReturn($attrEnabled);
157+
$share->method('canDownload')->willReturn($attrEnabled ?? true);
159158

160159
$share->expects($this->once())
161160
->method('canSeeContent')
162-
->willReturn($allowViewWithoutDownload);
161+
->willReturn($allowViewWithoutDownload && ($attrEnabled ?? true));
163162

164163
if (!$expectCanDownloadFile) {
165164
$this->expectException(Forbidden::class);

0 commit comments

Comments
 (0)