diff --git a/apps/files_sharing/lib/Controller/ShareAPIController.php b/apps/files_sharing/lib/Controller/ShareAPIController.php index bac188290a7ba..c8831009dcab9 100644 --- a/apps/files_sharing/lib/Controller/ShareAPIController.php +++ b/apps/files_sharing/lib/Controller/ShareAPIController.php @@ -78,6 +78,7 @@ class ShareAPIController extends OCSController { private ?Node $lockedNode = null; + /** @var array $trustedServerCache */ private array $trustedServerCache = []; /** @@ -239,6 +240,10 @@ protected function formatShare(IShare $share, ?Node $recipientNode = null): arra $result['expiration'] = $expiration->format('Y-m-d H:i:s'); } + $currentUserPermissions = $recipientNode?->getPermissions() ?? Constants::PERMISSION_ALL; + $userHasEnoughPermissions = ($currentUserPermissions & $share->getPermissions()) === $share->getPermissions(); + $token = $userHasEnoughPermissions ? $share->getToken() : null; + if ($share->getShareType() === IShare::TYPE_USER) { $sharedWith = $this->userManager->get($share->getSharedWith()); $result['share_with'] = $share->getSharedWith(); @@ -264,6 +269,7 @@ protected function formatShare(IShare $share, ?Node $recipientNode = null): arra $result['share_with'] = $share->getSharedWith(); $result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith(); } elseif ($share->getShareType() === IShare::TYPE_LINK) { + $url = $token ? $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $token]) : null; // "share_with" and "share_with_displayname" for passwords of link // shares was deprecated in Nextcloud 15, use "password" instead. @@ -274,23 +280,23 @@ protected function formatShare(IShare $share, ?Node $recipientNode = null): arra $result['send_password_by_talk'] = $share->getSendPasswordByTalk(); - $result['token'] = $share->getToken(); - $result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]); + $result['token'] = $token; + $result['url'] = $url; } elseif ($share->getShareType() === IShare::TYPE_REMOTE) { $result['share_with'] = $share->getSharedWith(); $result['share_with_displayname'] = $this->getCachedFederatedDisplayName($share->getSharedWith()); - $result['token'] = $share->getToken(); + $result['token'] = $token; } elseif ($share->getShareType() === IShare::TYPE_REMOTE_GROUP) { $result['share_with'] = $share->getSharedWith(); $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD'); - $result['token'] = $share->getToken(); + $result['token'] = $token; } elseif ($share->getShareType() === IShare::TYPE_EMAIL) { $result['share_with'] = $share->getSharedWith(); $result['password'] = $share->getPassword(); $result['password_expiration_time'] = $share->getPasswordExpirationTime() !== null ? $share->getPasswordExpirationTime()->format(\DateTime::ATOM) : null; $result['send_password_by_talk'] = $share->getSendPasswordByTalk(); $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL'); - $result['token'] = $share->getToken(); + $result['token'] = $token; } elseif ($share->getShareType() === IShare::TYPE_CIRCLE) { // getSharedWith() returns either "name (type, owner)" or // "name (type, owner) [id]", depending on the Teams app version. diff --git a/apps/files_sharing/lib/ResponseDefinitions.php b/apps/files_sharing/lib/ResponseDefinitions.php index 1585645241759..f23b662260b5f 100644 --- a/apps/files_sharing/lib/ResponseDefinitions.php +++ b/apps/files_sharing/lib/ResponseDefinitions.php @@ -54,7 +54,7 @@ * token: ?string, * uid_file_owner: string, * uid_owner: string, - * url?: string, + * url?: string|null, * } * * @psalm-type Files_SharingDeletedShare = array{ diff --git a/apps/files_sharing/openapi.json b/apps/files_sharing/openapi.json index ba15f60870e53..3ecfe58234afa 100644 --- a/apps/files_sharing/openapi.json +++ b/apps/files_sharing/openapi.json @@ -721,7 +721,8 @@ "type": "string" }, "url": { - "type": "string" + "type": "string", + "nullable": true } } }, diff --git a/build/integration/sharing_features/sharing-v1-part2.feature b/build/integration/sharing_features/sharing-v1-part2.feature index 0c83975fc39b5..fb391878b9b45 100644 --- a/build/integration/sharing_features/sharing-v1-part2.feature +++ b/build/integration/sharing_features/sharing-v1-part2.feature @@ -23,6 +23,36 @@ Feature: sharing And User "user2" should be included in the response And User "user3" should not be included in the response +Scenario: getting all shares of a file with reshares with link share with less permissions + Given user "user0" exists + And user "user1" exists + When as "user0" creating a share with + | path | textfile0.txt | + | shareType | 0 | + | shareWith | user1 | + | permissions | 17 | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + When as "user0" creating a share with + | path | textfile0.txt | + | shareType | 3 | + | permissions | 19 | + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And last link share can be downloaded + When As an "user1" + And sending "GET" to "/apps/files_sharing/api/v1/shares?reshares=true&path=textfile0 (2).txt" + Then the OCS status code should be "100" + And the HTTP status code should be "200" + And User "user1" should not be included in the response + Then the list of returned shares has 1 shares + And share 0 is returned with + | share_type | 3 | + | uid_owner | user0 | + | token | | + | url | | + | permissions | 19 | + Scenario: getting all shares of a file with a received share after revoking the resharing rights Given user "user0" exists And user "user1" exists diff --git a/openapi.json b/openapi.json index b1a8a93dd0818..94522cde73429 100644 --- a/openapi.json +++ b/openapi.json @@ -2852,7 +2852,8 @@ "type": "string" }, "url": { - "type": "string" + "type": "string", + "nullable": true } } },