From 0e217e42b07e9f80cb95938300cf3943267e2e8b Mon Sep 17 00:00:00 2001 From: skjnldsv Date: Thu, 22 Aug 2024 10:02:37 +0200 Subject: [PATCH] fix(files_sharing): adjust permissions from custom edit and delete check methods Signed-off-by: skjnldsv [skip ci] --- .../lib/Controller/ShareAPIController.php | 17 ++++ .../Controller/ShareAPIControllerTest.php | 42 ++++++++++ .../sharing_features/sharing-v1-part4.feature | 83 +++++++++++++++++++ 3 files changed, 142 insertions(+) diff --git a/apps/files_sharing/lib/Controller/ShareAPIController.php b/apps/files_sharing/lib/Controller/ShareAPIController.php index 1c642dcd9524a..50889c13ce342 100644 --- a/apps/files_sharing/lib/Controller/ShareAPIController.php +++ b/apps/files_sharing/lib/Controller/ShareAPIController.php @@ -188,6 +188,23 @@ protected function formatShare(IShare $share, ?Node $recipientNode = null): arra if ($isOwnShare) { $result['item_permissions'] = $node->getPermissions(); } + + // If we're on the recipient side, the node permissions + // are bound to the share permissions. So we need to + // adjust the permissions to the share permissions if necessary. + if (!$isOwnShare) { + $result['item_permissions'] = $share->getPermissions(); + + // For some reason, single files share are forbidden to have the delete permission + // since we have custom methods to check those, let's adjust straight away. + // DAV permissions does not have that issue though. + if ($this->canDeleteShare($share) || $this->canDeleteShareFromSelf($share)) { + $result['item_permissions'] |= Constants::PERMISSION_DELETE; + } + if ($this->canEditShare($share)) { + $result['item_permissions'] |= Constants::PERMISSION_UPDATE; + } + } // See MOUNT_ROOT_PROPERTYNAME dav property $result['is-mount-root'] = $node->getInternalPath() === ''; diff --git a/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php b/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php index b9b3e6fdecf80..426626372fd65 100644 --- a/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php +++ b/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php @@ -3775,6 +3775,12 @@ public function dataFormatShare() { $folder->method('getStorage')->willReturn($storage); $fileWithPreview->method('getStorage')->willReturn($storage); + + $mountPoint = $this->getMockBuilder(IMountPoint::class)->getMock(); + $mountPoint->method('getMountType')->willReturn(''); + $file->method('getMountPoint')->willReturn($mountPoint); + $folder->method('getMountPoint')->willReturn($mountPoint); + $owner = $this->getMockBuilder(IUser::class)->getMock(); $owner->method('getDisplayName')->willReturn('ownerDN'); $initiator = $this->getMockBuilder(IUser::class)->getMock(); @@ -3929,6 +3935,8 @@ public function dataFormatShare() { 'can_delete' => false, 'item_size' => 123456, 'item_mtime' => 1234567890, + 'is-mount-root' => false, + 'mount-type' => '', 'attributes' => null, ], $share, [], false ]; @@ -3980,6 +3988,8 @@ public function dataFormatShare() { 'can_delete' => true, 'item_size' => 123456, 'item_mtime' => 1234567890, + 'is-mount-root' => false, + 'mount-type' => '', 'attributes' => null, ], $share, [], false ]; @@ -4032,6 +4042,8 @@ public function dataFormatShare() { 'can_delete' => false, 'item_size' => 123456, 'item_mtime' => 1234567890, + 'is-mount-root' => false, + 'mount-type' => '', 'attributes' => null, ], $share, [], false ]; @@ -4081,6 +4093,8 @@ public function dataFormatShare() { 'can_delete' => false, 'item_size' => 123456, 'item_mtime' => 1234567890, + 'is-mount-root' => false, + 'mount-type' => '', 'attributes' => null, ], $share, [], false ]; @@ -4137,6 +4151,8 @@ public function dataFormatShare() { 'can_delete' => false, 'item_size' => 123456, 'item_mtime' => 1234567890, + 'is-mount-root' => false, + 'mount-type' => '', 'attributes' => null, ], $share, [], false ]; @@ -4193,6 +4209,8 @@ public function dataFormatShare() { 'can_delete' => false, 'item_size' => 123456, 'item_mtime' => 1234567890, + 'is-mount-root' => false, + 'mount-type' => '', 'attributes' => null, ], $share, [], false ]; @@ -4243,6 +4261,8 @@ public function dataFormatShare() { 'can_delete' => false, 'item_size' => 123456, 'item_mtime' => 1234567890, + 'is-mount-root' => false, + 'mount-type' => '', 'attributes' => null, ], $share, [], false ]; @@ -4293,6 +4313,8 @@ public function dataFormatShare() { 'can_delete' => false, 'item_size' => 123456, 'item_mtime' => 1234567890, + 'is-mount-root' => false, + 'mount-type' => '', 'attributes' => null, ], $share, [], false ]; @@ -4346,6 +4368,8 @@ public function dataFormatShare() { 'can_delete' => false, 'item_size' => 123456, 'item_mtime' => 1234567890, + 'is-mount-root' => false, + 'mount-type' => '', 'attributes' => null, ], $share, [], false ]; @@ -4396,6 +4420,8 @@ public function dataFormatShare() { 'can_delete' => false, 'item_size' => 123456, 'item_mtime' => 1234567890, + 'is-mount-root' => false, + 'mount-type' => '', 'attributes' => null, ], $share, [], false ]; @@ -4446,6 +4472,8 @@ public function dataFormatShare() { 'can_delete' => false, 'item_size' => 123456, 'item_mtime' => 1234567890, + 'is-mount-root' => false, + 'mount-type' => '', 'attributes' => null, ], $share, [], false ]; @@ -4513,6 +4541,8 @@ public function dataFormatShare() { 'password_expiration_time' => null, 'item_size' => 123456, 'item_mtime' => 1234567890, + 'is-mount-root' => false, + 'mount-type' => '', 'attributes' => null, ], $share, [], false ]; @@ -4566,6 +4596,8 @@ public function dataFormatShare() { 'password_expiration_time' => null, 'item_size' => 123456, 'item_mtime' => 1234567890, + 'is-mount-root' => false, + 'mount-type' => '', 'attributes' => null, ], $share, [], false ]; @@ -4617,6 +4649,8 @@ public function dataFormatShare() { 'can_delete' => true, 'item_size' => 123456, 'item_mtime' => 1234567890, + 'is-mount-root' => false, + 'mount-type' => '', 'attributes' => null, ], $share, [], false ]; @@ -4720,6 +4754,10 @@ public function dataFormatRoomShare() { $file->method('getSize')->willReturn(123456); $file->method('getMTime')->willReturn(1234567890); + $mountPoint = $this->getMockBuilder(IMountPoint::class)->getMock(); + $mountPoint->method('getMountType')->willReturn(''); + $file->method('getMountPoint')->willReturn($mountPoint); + $cache = $this->getMockBuilder('OCP\Files\Cache\ICache')->getMock(); $cache->method('getNumericStorageId')->willReturn(100); $storage = $this->createMock(Storage::class); @@ -4775,6 +4813,8 @@ public function dataFormatRoomShare() { 'can_delete' => false, 'item_size' => 123456, 'item_mtime' => 1234567890, + 'is-mount-root' => false, + 'mount-type' => '', 'attributes' => null, ], $share, false, [] ]; @@ -4824,6 +4864,8 @@ public function dataFormatRoomShare() { 'can_delete' => false, 'item_size' => 123456, 'item_mtime' => 1234567890, + 'is-mount-root' => false, + 'mount-type' => '', 'attributes' => null, ], $share, true, [ 'share_with_displayname' => 'recipientRoomName' diff --git a/build/integration/sharing_features/sharing-v1-part4.feature b/build/integration/sharing_features/sharing-v1-part4.feature index ae4e69f262279..b180ad0807271 100644 --- a/build/integration/sharing_features/sharing-v1-part4.feature +++ b/build/integration/sharing_features/sharing-v1-part4.feature @@ -39,3 +39,86 @@ Scenario: Creating a new share of a file you own shows the file permissions And the HTTP status code should be "200" And Share fields of last share match with | item_permissions | 27 | + +Scenario: Receiving a share of a file gives no create permission + Given user "user0" exists + And user "user1" exists + And As an "user0" + And parameter "shareapi_default_permissions" of app "core" is set to "31" + And file "welcome.txt" of user "user0" is shared with user "user1" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And share 0 is returned with + | path | /welcome.txt | + | permissions | 19 | + | item_permissions | 27 | + When As an "user1" + And user "user1" accepts last share + And sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true" + Then the list of returned shares has 1 shares + And share 0 is returned with + | path | /welcome (2).txt | + | permissions | 19 | + | item_permissions | 27 | + +Scenario: Receiving a share of a folder gives create permission + Given user "user0" exists + And user "user1" exists + And As an "user0" + And parameter "shareapi_default_permissions" of app "core" is set to "31" + And file "PARENT/CHILD" of user "user0" is shared with user "user1" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And share 0 is returned with + | path | /PARENT/CHILD | + | permissions | 31 | + | item_permissions | 31 | + When As an "user1" + And user "user1" accepts last share + And sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true" + Then the list of returned shares has 1 shares + And share 0 is returned with + | path | /CHILD | + | permissions | 31 | + | item_permissions | 31 | + +# User can remove itself from a share +Scenario: Receiving a share of a file without delete permission gives delete permission anyway + Given user "user0" exists + And user "user1" exists + And As an "user0" + And parameter "shareapi_default_permissions" of app "core" is set to "23" + And file "welcome.txt" of user "user0" is shared with user "user1" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And share 0 is returned with + | path | /welcome.txt | + | permissions | 19 | + | item_permissions | 27 | + When As an "user1" + And user "user1" accepts last share + And sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true" + Then the list of returned shares has 1 shares + And share 0 is returned with + | path | /welcome (2).txt | + | permissions | 19 | + | item_permissions | 27 | + +Scenario: Receiving a share of a file without delete permission gives delete permission anyway + Given user "user0" exists + And user "user1" exists + And As an "user0" + And group "group1" exists + And user "user1" belongs to group "group1" + And parameter "shareapi_default_permissions" of app "core" is set to "23" + And file "welcome.txt" of user "user0" is shared with group "group1" + And sending "GET" to "/apps/files_sharing/api/v1/shares" + And share 0 is returned with + | path | /welcome.txt | + | permissions | 19 | + | item_permissions | 27 | + When As an "user1" + And user "user1" accepts last share + And sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true" + Then the list of returned shares has 1 shares + And share 0 is returned with + | path | /welcome (2).txt | + | permissions | 19 | + | item_permissions | 27 |