Skip to content

Commit 4490c4e

Browse files
Merge pull request #58467 from nextcloud/backport/56967/stable31
[stable31] fix(files_sharing): make legacy `downloadShare` endpoint compatible with legacy behavior
2 parents a17ee2b + 7c6f496 commit 4490c4e

2 files changed

Lines changed: 52 additions & 21 deletions

File tree

apps/files_sharing/lib/Controller/ShareController.php

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use OCA\Files_Sharing\Event\ShareLinkAccessedEvent;
1414
use OCP\Accounts\IAccountManager;
1515
use OCP\AppFramework\AuthPublicShareController;
16+
use OCP\AppFramework\Http;
1617
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
1718
use OCP\AppFramework\Http\Attribute\OpenAPI;
1819
use OCP\AppFramework\Http\Attribute\PublicPage;
@@ -28,6 +29,7 @@
2829
use OCP\Files\Folder;
2930
use OCP\Files\IRootFolder;
3031
use OCP\Files\NotFoundException;
32+
use OCP\Files\NotPermittedException;
3133
use OCP\HintException;
3234
use OCP\IConfig;
3335
use OCP\IL10N;
@@ -359,49 +361,75 @@ public function downloadShare($token, $files = null, $path = '') {
359361
$share = $this->shareManager->getShareByToken($token);
360362

361363
if (!($share->getPermissions() & Constants::PERMISSION_READ)) {
362-
return new DataResponse('Share has no read permission');
364+
return new DataResponse('Share has no read permission', Http::STATUS_FORBIDDEN);
363365
}
364366

365367
$attributes = $share->getAttributes();
366368
if ($attributes?->getAttribute('permissions', 'download') === false) {
367-
return new DataResponse('Share has no download permission');
369+
return new DataResponse('Share has no download permission', Http::STATUS_FORBIDDEN);
368370
}
369371

370372
if (!$this->validateShare($share)) {
371373
throw new NotFoundException();
372374
}
373375

376+
if ($share->getHideDownload()) {
377+
// download API does not work if hidden - use the DAV endpoint for previews
378+
throw new NotFoundException();
379+
}
380+
374381
$node = $share->getNode();
375-
if ($node instanceof Folder) {
376-
// Directory share
382+
if ($path !== '') {
383+
if (!$node instanceof Folder) {
384+
return new NotFoundResponse();
385+
}
377386

378-
// Try to get the path
379-
if ($path !== '') {
387+
try {
388+
$node = $node->get($path);
389+
} catch (NotFoundException|NotPermittedException) {
390+
$this->emitAccessShareHook($share, 404, 'Share not found');
391+
$this->emitShareAccessEvent($share, self::SHARE_DOWNLOAD, 404, 'Share not found');
392+
return new NotFoundResponse();
393+
}
394+
}
395+
396+
if ($files !== null) {
397+
if (!$node instanceof Folder) {
398+
return new NotFoundResponse();
399+
}
400+
401+
$filesParam = json_decode($files, true);
402+
if (!is_array($filesParam)) {
380403
try {
381-
$node = $node->get($path);
382-
} catch (NotFoundException $e) {
404+
// legacy wise this allows also passing the filename
405+
$node = $node->get($files);
406+
$files = null;
407+
} catch (NotFoundException|NotPermittedException) {
383408
$this->emitAccessShareHook($share, 404, 'Share not found');
384409
$this->emitShareAccessEvent($share, self::SHARE_DOWNLOAD, 404, 'Share not found');
385410
return new NotFoundResponse();
386411
}
387412
}
388-
389-
if ($node instanceof Folder) {
390-
if ($files === null || $files === '') {
391-
if ($share->getHideDownload()) {
392-
throw new NotFoundException('Downloading a folder');
393-
}
394-
}
395-
}
396413
}
397414

398415
$this->emitAccessShareHook($share);
399416
$this->emitShareAccessEvent($share, self::SHARE_DOWNLOAD);
400417

401-
$davUrl = '/public.php/dav/files/' . $token . '/?accept=zip';
418+
$davPath = '';
419+
if ($node !== $share->getNode()) {
420+
$davPath = substr($node->getPath(), strlen($share->getNode()->getPath()));
421+
}
422+
423+
$params = [];
402424
if ($files !== null) {
403-
$davUrl .= '&files=' . $files;
425+
$params['files'] = $files;
404426
}
427+
if ($node instanceof Folder) {
428+
$params['accept'] = 'zip';
429+
}
430+
431+
$davUrl = '/public.php/dav/files/' . $token . $davPath;
432+
$davUrl .= '?' . http_build_query($params);
405433
return new RedirectResponse($this->urlGenerator->getAbsoluteURL($davUrl));
406434
}
407435
}

apps/files_sharing/tests/Controller/ShareControllerTest.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use OCP\Accounts\IAccountManager;
1818
use OCP\Accounts\IAccountProperty;
1919
use OCP\Activity\IManager;
20+
use OCP\AppFramework\Http;
2021
use OCP\AppFramework\Http\ContentSecurityPolicy;
2122
use OCP\AppFramework\Http\DataResponse;
2223
use OCP\AppFramework\Http\Template\ExternalShareMenuAction;
@@ -688,7 +689,9 @@ public function testShowShareInvalid(): void {
688689
->with('token')
689690
->willReturn($share);
690691

691-
$this->userManager->method('get')->with('ownerUID')->willReturn($owner);
692+
$this->userManager->method('get')
693+
->with('ownerUID')
694+
->willReturn($owner);
692695

693696
$this->shareController->showShare();
694697
}
@@ -709,7 +712,7 @@ public function testDownloadShareWithCreateOnlyShare(): void {
709712

710713
// Test with a password protected share and no authentication
711714
$response = $this->shareController->downloadShare('validtoken');
712-
$expectedResponse = new DataResponse('Share has no read permission');
715+
$expectedResponse = new DataResponse('Share has no read permission', Http::STATUS_FORBIDDEN);
713716
$this->assertEquals($expectedResponse, $response);
714717
}
715718

@@ -737,7 +740,7 @@ public function testDownloadShareWithoutDownloadPermission(): void {
737740

738741
// Test with a password protected share and no authentication
739742
$response = $this->shareController->downloadShare('validtoken');
740-
$expectedResponse = new DataResponse('Share has no download permission');
743+
$expectedResponse = new DataResponse('Share has no download permission', Http::STATUS_FORBIDDEN);
741744
$this->assertEquals($expectedResponse, $response);
742745
}
743746

0 commit comments

Comments
 (0)