Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions tests/acceptance/bootstrap/ArchiverContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,140 @@ public function userDownloadsTheArchiveOfTheseItems(
);
}

/**
* Sends a PROPFIND request to the public WebDAV endpoint for the last created public link
* and extracts archive-related metadata from the XML response.
*
* The returned data includes:
* - fileIds: IDs of all files included in the public link
* - signature: signature required for archive download
* - expiration: expiration timestamp required for archive download
*
* @param string $password
*
* @return array {
* fileIds: string[],
* signature: string|null,
* expiration: string|null
* }
*
* @throws GuzzleException
*/
private function fetchPublicLinkArchiveData(string $password = ''): array {
$body = '<?xml version="1.0"?>
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
<d:prop>
<d:resourcetype/>
<oc:public-link-item-type/>
<oc:public-link-permission/>
<oc:public-link-expiration/>
<oc:fileid/>
<oc:downloadURL/>
<oc:signature-auth/>
<oc:public-link-share-datetime/>
<oc:public-link-share-owner/>
</d:prop>
</d:propfind>';

$token = $this->featureContext->isUsingSharingNG()
? $this->featureContext->shareNgGetLastCreatedLinkShareToken()
: $this->featureContext->getLastCreatedPublicShareToken();

$password = $this->featureContext->getActualPassword($password);

$url = $this->featureContext->getBaseUrl() . "/dav/public-files/$token";

$headers = [
"Depth" => "1",
"OCS-APIRequest" => "true",
"public-token" => $token,
"Content-Type" => "application/xml; charset=utf-8",
"Authorization" => "Basic " . base64_encode("public:$password"),
];

$response = HttpRequestHelper::sendRequest(
$url,
"PROPFIND",
null,
null,
$headers,
$body,
);
Comment on lines +279 to +317
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check if this works

Suggested change
$body = '<?xml version="1.0"?>
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
<d:prop>
<d:resourcetype/>
<oc:public-link-item-type/>
<oc:public-link-permission/>
<oc:public-link-expiration/>
<oc:fileid/>
<oc:downloadURL/>
<oc:signature-auth/>
<oc:public-link-share-datetime/>
<oc:public-link-share-owner/>
</d:prop>
</d:propfind>';
$token = $this->featureContext->isUsingSharingNG()
? $this->featureContext->shareNgGetLastCreatedLinkShareToken()
: $this->featureContext->getLastCreatedPublicShareToken();
$password = $this->featureContext->getActualPassword($password);
$url = $this->featureContext->getBaseUrl() . "/dav/public-files/$token";
$headers = [
"Depth" => "1",
"OCS-APIRequest" => "true",
"public-token" => $token,
"Content-Type" => "application/xml; charset=utf-8",
"Authorization" => "Basic " . base64_encode("public:$password"),
];
$response = HttpRequestHelper::sendRequest(
$url,
"PROPFIND",
null,
null,
$headers,
$body,
);
$token = $this->featureContext->isUsingSharingNG()
? $this->featureContext->shareNgGetLastCreatedLinkShareToken()
: $this->featureContext->getLastCreatedPublicShareToken();
$password = $this->featureContext->getActualPassword($password);
$response = WebDavHelper::propfind(
$this->featureContext->getBaseUrl(),
"public",
"$password",
"$token",
['oc:fileid', 'oc:downloadURL', 'oc:signature-auth'],
null,
null,
"public-files",
);


$responseBody = $response->getBody()->getContents();

$this->featureContext->setResponse($response);

$xmlDocument = new \SimpleXMLElement($responseBody);
$xmlDocument->registerXPathNamespace('d', 'DAV:');
$xmlDocument->registerXPathNamespace('oc', 'http://owncloud.org/ns');

$fileIds = [];
$signature = null;
$expiration = null;

foreach ($xmlDocument->xpath('//d:response') as $responseNode) {
$type = $responseNode->xpath('.//oc:public-link-item-type');
$fileId = $responseNode->xpath('.//oc:fileid');

$sig = $responseNode->xpath('.//oc:signature-auth');
$exp = $responseNode->xpath('.//oc:public-link-expiration');

if (!empty($type) && (string)$type[0] === 'file' && !empty($fileId)) {
$fileIds[] = (string)$fileId[0];
}

if ($signature === null && !empty($sig)) {
$signature = (string)$sig[0];
}

if ($expiration === null && !empty($exp)) {
$expiration = (string)$exp[0];
}
}

return [
'fileIds' => $fileIds,
'signature' => $signature,
'expiration' => $expiration,
];
}

/**
* @When /^the public downloads the archive of the last created public link with password "([^"]*)"$/
*
* @param string $password
* @return void
*
* @throws GuzzleException|Exception
*/
public function publicDownloadsTheArchiveOfTheLastCreatedPublicLink(string $password = ""): void {
$data = $this->fetchPublicLinkArchiveData($password);
$fileIds = $data['fileIds'];
$signature = $data['signature'];
$expiration = $data['expiration'];

$token = $this->featureContext->isUsingSharingNG()
? $this->featureContext->shareNgGetLastCreatedLinkShareToken()
: $this->featureContext->getLastCreatedPublicShareToken();
$password = $this->featureContext->getActualPassword($password);
$queryParts = [];
$queryParts[] = "public-token=" . urlencode($token);

foreach ($fileIds as $fileId) {
$queryParts[] = "id=" . urlencode($fileId);
}

$queryParts[] = "signature=" . urlencode($signature);
$queryParts[] = "expiration=" . urlencode($expiration);
$queryString = implode('&', $queryParts);
$url = $this->featureContext->getBaseUrl() . "/archiver?" . $queryString;

$this->featureContext->setResponse(
HttpRequestHelper::get($url, 'public', $password),
);
}

/**
* @Then the downloaded :type archive should contain these files:
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,29 @@ Feature: Public can download folders from project space public link
And the downloaded zip archive should contain these files:
| name | content |
| folder/test.txt | some content |

@env-config
Scenario Outline: download an archive of a password protected public link share (project space)
And using SharingNG
And user "Alice" has created a folder "project-folder" in space "new-space"
And user "Alice" has uploaded a file inside space "new-space" with content "some content1" to "project-folder/test1.txt"
And user "Alice" has uploaded a file inside space "new-space" with content "some content2" to "project-folder/test2.txt"
And user "Alice" has uploaded a file inside space "new-space" with content "some content3" to "project-folder/test3.txt"
And user "Alice" has created the following resource link share:
| resource | project-folder |
| space | new-space |
| permissionsRole | <permissionsRole> |
| password | %public% |
Comment on lines +57 to +60
Copy link
Copy Markdown
Member

@saw-jan saw-jan May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
| resource | project-folder |
| space | new-space |
| permissionsRole | <permissionsRole> |
| password | %public% |
| resource | project-folder |
| space | new-space |
| permissionsRole | <permissionsRole> |
| password | %public% |

When the public downloads the archive of the last created public link with password "%public%"
Then the HTTP status code should be "200"
And the downloaded "zip" archive should contain these files:
| name | content |
| test1.txt | some content1 |
| test2.txt | some content2 |
| test3.txt | some content3 |

Examples:
| permissionsRole |
| Edit |
| View |
| Upload |
Loading