diff --git a/tests/acceptance/bootstrap/GuestLinkContext.php b/tests/acceptance/bootstrap/GuestLinkContext.php index eec89f2258..cd58c6d93d 100644 --- a/tests/acceptance/bootstrap/GuestLinkContext.php +++ b/tests/acceptance/bootstrap/GuestLinkContext.php @@ -728,6 +728,76 @@ public function guestListsTheSharesSharedWithThem(string $guest): void { ); } + /** + * @param string $guest + * @param string $authorization + * + * @return void + * @throws GuzzleException + */ + #[When('guest :guest lists the shares shared with them with authorization header :authorization using the Graph API')] + public function guestListsTheSharesSharedWithThemWithAuthorizationHeader( + string $guest, + string $authorization + ): void { + $this->featureContext->setResponse( + HttpRequestHelper::get( + $this->featureContext->getBaseUrl() . '/graph/v1beta1/me/drive/sharedWithMe', + $this->featureContext->getStepLineRef(), + null, + null, + \array_merge($this->getSessionHeaders($guest), ['Authorization' => $authorization]), + ) + ); + } + + /** + * @param string $user + * @param string $guest + * + * @return void + * @throws GuzzleException + */ + #[When('user :user lists the shares shared with them with the guest session of guest :guest using the Graph API')] + public function userListsTheSharesSharedWithThemWithTheGuestSession(string $user, string $guest): void { + $this->featureContext->setResponse( + HttpRequestHelper::get( + $this->featureContext->getBaseUrl() . '/graph/v1beta1/me/drive/sharedWithMe', + $this->featureContext->getStepLineRef(), + $this->featureContext->getActualUsername($user), + $this->featureContext->getPasswordForUser($user), + $this->getSessionHeaders($guest), + ) + ); + } + + /** + * @param string $guest + * @param string $resource + * @param string $password + * + * @return void + * @throws GuzzleException + */ + #[When('guest :guest downloads file :resource from the last public link share with password :password and the guest session using the public WebDAV API')] + public function guestDownloadsFileFromTheLastPublicLinkShareWithTheGuestSession( + string $guest, + string $resource, + string $password + ): void { + $token = $this->featureContext->shareNgGetLastCreatedLinkShareToken(); + $davPath = WebDavHelper::getDavPath(WebDavHelper::DAV_VERSION_NEW, $token, 'public-files'); + $this->featureContext->setResponse( + HttpRequestHelper::get( + $this->featureContext->getBaseUrl() . "/$davPath/$resource", + $this->featureContext->getStepLineRef(), + 'public', + $this->featureContext->getActualPassword($password), + \array_merge($this->getSessionHeaders($guest), ['public-token' => $token]), + ) + ); + } + /** * @param string $guest * @param string $resource @@ -1014,6 +1084,144 @@ public function guestSendsHttpMethodToGraphEndpointOfResource( ); } + /** + * @param string $fileId + * @param string|null $user + * @param array $headers + * + * @return string + * @throws GuzzleException + */ + private function getDownloadUrl(string $fileId, ?string $user, array $headers): string { + $response = WebDavHelper::makeDavRequest( + $this->featureContext->getBaseUrl(), + $user === null ? null : $this->featureContext->getActualUsername($user), + $user === null ? null : $this->featureContext->getPasswordForUser($user), + 'PROPFIND', + '', + \array_merge($headers, ['Depth' => '0']), + $fileId, + $this->featureContext->getStepLineRef(), + WebDavHelper::getBodyForPropfind(['oc:downloadURL']), + WebDavHelper::DAV_VERSION_SPACES + ); + $this->featureContext->theHTTPStatusCodeShouldBe(207, '', $response); + $match = HttpRequestHelper::getResponseXml($response, __METHOD__)->xpath('//d:prop/oc:downloadURL'); + Assert::assertNotEmpty($match, 'The response does not contain an oc:downloadURL'); + return (string)$match[0]; + } + + /** + * @param string $guest + * @param string $resource + * @param string $space + * @param string $owner + * + * @return void + * @throws GuzzleException + */ + #[When('guest :guest downloads file :resource shared from space :space of user :owner via its signed download URL')] + public function guestDownloadsSharedFileViaItsDownloadUrl( + string $guest, + string $resource, + string $space, + string $owner + ): void { + $fileId = $this->spacesContext->getResourceId($owner, $space, $resource); + $headers = $this->getSessionHeaders($guest); + $this->featureContext->setResponse( + HttpRequestHelper::get( + $this->getDownloadUrl($fileId, null, $headers), + $this->featureContext->getStepLineRef(), + null, + null, + $headers, + ) + ); + } + + /** + * @param string $user + * @param string $resource + * @param string $guest + * + * @return void + * @throws GuzzleException + */ + #[When('user :user downloads file :resource via its signed download URL with the guest session of guest :guest')] + public function userDownloadsFileViaItsDownloadUrlWithTheGuestSession( + string $user, + string $resource, + string $guest + ): void { + $fileId = $this->spacesContext->getResourceId($user, 'Personal', $resource); + $this->featureContext->setResponse( + HttpRequestHelper::get( + $this->getDownloadUrl($fileId, $user, []), + $this->featureContext->getStepLineRef(), + null, + null, + $this->getSessionHeaders($guest), + ) + ); + } + + /** + * @param string $guest + * @param string $content + * @param string $path + * @param string $folder + * @param string $space + * @param string $owner + * @param string $authorization + * + * @return void + * @throws GuzzleException + */ + #[When('guest :guest uploads content :content to :path inside folder :folder of space :space of user :owner using the TUS protocol with authorization header :authorization')] + public function guestUploadsContentInsideFolderUsingTusWithAuthorizationHeader( + string $guest, + string $content, + string $path, + string $folder, + string $space, + string $owner, + string $authorization + ): void { + $folderId = $this->spacesContext->getResourceId($owner, $space, $folder); + $headers = \array_merge( + $this->getSessionHeaders($guest), + ['Authorization' => $authorization, 'Tus-Resumable' => '1.0.0'] + ); + $response = WebDavHelper::makeDavRequest( + $this->featureContext->getBaseUrl(), + null, + null, + 'POST', + '', + \array_merge( + $headers, + ['Upload-Length' => (string)\strlen($content), 'Upload-Metadata' => 'filename ' . \base64_encode($path)] + ), + $folderId, + $this->featureContext->getStepLineRef(), + null, + WebDavHelper::DAV_VERSION_SPACES + ); + $this->featureContext->theHTTPStatusCodeShouldBe(201, '', $response); + $this->featureContext->setResponse( + HttpRequestHelper::sendRequest( + $response->getHeader('Location')[0], + $this->featureContext->getStepLineRef(), + 'PATCH', + null, + null, + \array_merge($headers, ['Content-Type' => 'application/offset+octet-stream', 'Upload-Offset' => '0']), + $content, + ) + ); + } + /** * @param string $guest * diff --git a/tests/acceptance/features/apiGuestLink/guestLinkOtherCredentials.feature b/tests/acceptance/features/apiGuestLink/guestLinkOtherCredentials.feature new file mode 100644 index 0000000000..ffaeb6e6e4 --- /dev/null +++ b/tests/acceptance/features/apiGuestLink/guestLinkOtherCredentials.feature @@ -0,0 +1,84 @@ +@email +Feature: guest session does not override other credentials + As a user who has opened a guest link in the same browser + I want requests with other credentials to never be authenticated as the guest + So that I get my own data, or a 401 to renew invalid credentials + + Background: + Given user "Alice" has been created with default attributes + And user "Brian" has been created with default attributes + And user "Alice" has uploaded file with content "for guest" to "guestFile.txt" + And user "Alice" has sent the following resource share invitation: + | resource | guestFile.txt | + | space | Personal | + | sharee | guest@example.org | + | shareType | guest | + | permissionsRole | Viewer | + And guest "guest@example.org" has verified the guest link from the last email + + + Scenario Outline: request with invalid credentials and a guest session is rejected + When guest "guest@example.org" lists the shares shared with them with authorization header "" using the Graph API + Then the HTTP status code should be "401" + Examples: + | authorization | + | Bearer invalid-token | + | Basic aW52YWxpZDppbnZhbGlk | + + + Scenario: request with valid user credentials and a guest session is authenticated as the user + When user "Brian" lists the shares shared with them with the guest session of guest "guest@example.org" using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": ["value"], + "properties": { + "value": { + "type": "array", + "minItems": 0, + "maxItems": 0 + } + } + } + """ + + + Scenario: public link request with a guest session is authenticated as the public link + Given user "Alice" has created folder "publicFolder" + And user "Alice" has uploaded file with content "public content" to "publicFolder/public.txt" + And user "Alice" has created the following resource link share: + | resource | publicFolder | + | space | Personal | + | permissionsRole | view | + | password | %public% | + When guest "guest@example.org" downloads file "public.txt" from the last public link share with password "%public%" and the guest session using the public WebDAV API + Then the HTTP status code should be "200" + And the downloaded content should be "public content" + + + Scenario: guest downloads a shared file via its signed download URL + When guest "guest@example.org" downloads file "guestFile.txt" shared from space "Personal" of user "Alice" via its signed download URL + Then the HTTP status code should be "200" + And the downloaded content should be "for guest" + + + Scenario: signed download URL of a user with a guest session is authenticated as the user + Given user "Brian" has uploaded file with content "from Brian" to "brianFile.txt" + When user "Brian" downloads file "brianFile.txt" via its signed download URL with the guest session of guest "guest@example.org" + Then the HTTP status code should be "200" + And the downloaded content should be "from Brian" + + + Scenario: guest uploads a file using TUS with an unusable authorization header + Given user "Alice" has created folder "uploadFolder" + And user "Alice" has sent the following resource share invitation: + | resource | uploadFolder | + | space | Personal | + | sharee | guest@example.org | + | shareType | guest | + | permissionsRole | Editor | + When guest "guest@example.org" uploads content "uploaded by guest" to "tus.txt" inside folder "uploadFolder" of space "Personal" of user "Alice" using the TUS protocol with authorization header "undefined" + Then the HTTP status code should be "204" + And the content of file "uploadFolder/tus.txt" for user "Alice" should be "uploaded by guest"