Skip to content

Commit 150e157

Browse files
Merge pull request #61497 from nextcloud/fix/viewonlyplugin/allow-copy-move-same-storage
fix(ViewOnlyPlugin): Allow COPY and MOVE operations within the same storage
2 parents 769015e + 865ee69 commit 150e157

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;
@@ -80,30 +81,37 @@ public function checkViewOnly(RequestInterface $request): bool {
8081
}
8182

8283
$storage = $node->getStorage();
83-
8484
if (!$storage->instanceOfStorage(ISharedStorage::class)) {
8585
return true;
8686
}
8787

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

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

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

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

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

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

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

161160
$share->expects($this->once())
162161
->method('canSeeContent')
163-
->willReturn($allowViewWithoutDownload);
162+
->willReturn($allowViewWithoutDownload && ($attrEnabled ?? true));
164163

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

0 commit comments

Comments
 (0)