api-test: cover additional unified roles in acceptance tests (#3169)

This commit is contained in:
Viktor Scharf
2026-07-27 09:12:38 +02:00
committed by GitHub
parent a87163fb14
commit f3693ef112
12 changed files with 619 additions and 1 deletions

View File

@@ -2377,7 +2377,6 @@ def serverTestingDocs(ctx):
{
"event": ["push"],
"branch": "${CI_REPO_DEFAULT_BRANCH}",
"path": "tests/README.md",
},
],
},
@@ -2385,6 +2384,7 @@ def serverTestingDocs(ctx):
"when": [
{
"event": ["push", "pull_request"],
"path": "tests/README.md",
},
],
}]

View File

@@ -40,7 +40,14 @@ class GraphHelper {
public const ADDITIONAL_PERMISSIONS_ROLES = [
'Secure Viewer' => 'aa97fe03-7980-45ac-9e50-b325749fd7e6',
'Viewer With Versions' => 'd1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5',
'Viewer List Grants' => 'd5041006-ebb3-4b4a-b6a4-7c180ecfb17d',
'Space Viewer With Versions' => '3de465fc-6e17-4839-8b8a-a77cc497878b',
'Editor With Versions' => 'b8c6e1c9-5d2a-4f0e-9c3b-1a2b3c4d5e6f',
'Editor List Grants' => 'e8ea8b21-abd4-45d2-b893-8d1546378e9e',
'Space Editor Without Versions' => '3284f2d5-0070-4ad8-ac40-c247f7c1fb27',
'File Editor With Versions' => '3d00ce52-1fc2-4dbc-8b95-a73b73395f5a',
'File Editor List Grants' => 'c1235aea-d106-42db-8458-7d5610fb0a67',
'Denied' => '63e64e19-8d43-42ec-a738-2b6af2610efa',
];

View File

@@ -272,6 +272,103 @@ class FilesVersionsContext implements Context {
$this->featureContext->setResponse($response, $user);
}
/**
* @param string $user
* @param int $versionIndex
* @param string $fileId
*
* @return ResponseInterface
* @throws Exception
*/
public function restoreVersionIndexOfFileUsingFileId(
string $user,
int $versionIndex,
string $fileId
): ResponseInterface {
$user = $this->featureContext->getActualUsername($user);
$response = $this->listVersionFolder($user, $fileId, 1);
$responseXmlObject = HttpRequestHelper::getResponseXml(
$response,
__METHOD__
);
$xmlPart = $responseXmlObject->xpath("//d:response/d:href");
$destinationUrl = $this->featureContext->getBaseUrl() . "/" .
WebDavHelper::getDavPath(WebDavHelper::DAV_VERSION_SPACES, $fileId);
$fullUrl = $this->featureContext->getBaseUrlWithoutPath() .
$xmlPart[$versionIndex];
return HttpRequestHelper::sendRequest(
$fullUrl,
$this->featureContext->getStepLineRef(),
'COPY',
$user,
$this->featureContext->getPasswordForUser($user),
['Destination' => $destinationUrl]
);
}
/**
* @param string $user
* @param int $versionIndex
* @param string $fileId
*
* @return void
* @throws Exception
*/
#[When('user :user restores version index :versionIndex of file :resource using file-id :fileId')]
public function userRestoresVersionIndexOfFileUsingFileId(
string $user,
int $versionIndex,
string $fileId
): void {
$response = $this->restoreVersionIndexOfFileUsingFileId($user, $versionIndex, $fileId);
$this->featureContext->setResponse($response, $user);
}
/**
* @param string $user
* @param string $share
*
* @return void
* @throws Exception
*/
#[When('user :user gets the number of versions of shared resource :share')]
#[When('user :user tries to get the number of versions of shared resource :share')]
public function userGetsTheNumberOfVersionsOfSharedResource(string $user, string $share): void {
$fileId = $this->spacesContext->getSharesMountId($user, $share);
$this->featureContext->setResponse(
$this->featureContext->makeDavRequest(
$user,
"PROPFIND",
$fileId,
null,
null,
null,
"versions"
)
);
}
/**
* @param string $user
* @param int $versionIndex
* @param string $share
*
* @return void
* @throws Exception
*/
#[When('user :user restores version index :versionIndex of shared resource :share')]
public function userRestoresVersionIndexOfSharedResource(
string $user,
int $versionIndex,
string $share
): void {
$fileId = $this->spacesContext->getSharesMountId($user, $share);
$this->featureContext->setResponse(
$this->restoreVersionIndexOfFileUsingFileId($user, $versionIndex, $fileId),
$user
);
}
/**
* assert file versions count
*

View File

@@ -2115,6 +2115,121 @@ class SharingNgContext implements Context {
}
}
/**
* @param string $roleId
*
* @return void
* @throws GuzzleException
*/
#[Then('the permissions roles allowed values should contain a role with id :roleId')]
public function thePermissionsRolesAllowedValuesShouldContainARoleWithId(string $roleId): void {
$responseBody = $this->featureContext->getJsonDecodedResponseBodyContent();
$allowedValues = $responseBody->{'@libre.graph.permissions.roles.allowedValues'} ?? [];
$actualIds = [];
foreach ($allowedValues as $role) {
if (isset($role->id)) {
$actualIds[] = $role->id;
}
}
Assert::assertContains(
$roleId,
$actualIds,
"Permission role id '$roleId' was not found in the allowed values. Found: " . \implode(', ', $actualIds)
);
}
/**
* @return string[] list of user ids the resource is granted to (from the last permissions response)
*/
private function getGranteeUserIdsFromResponse(): array {
$responseBody = $this->featureContext->getJsonDecodedResponseBodyContent();
$grantees = [];
foreach (($responseBody->value ?? []) as $permission) {
if (isset($permission->grantedToV2->user->id)) {
$grantees[] = $permission->grantedToV2->user->id;
}
}
return $grantees;
}
/**
* @param string $grantee
*
* @return void
* @throws GuzzleException
*/
#[Then('the permissions list of the response should contain a grant for user :grantee')]
public function thePermissionsListOfTheResponseShouldContainAGrantForUser(string $grantee): void {
$granteeId = $this->featureContext->getAttributeOfCreatedUser($grantee, 'id');
$grantees = $this->getGranteeUserIdsFromResponse();
Assert::assertContains(
$granteeId,
$grantees,
"Expected a grant for user '$grantee' ($granteeId) but found grantees: " . \implode(', ', $grantees)
);
}
/**
* @param string $grantee
*
* @return void
* @throws GuzzleException
*/
#[Then('the permissions list of the response should not contain a grant for user :grantee')]
public function thePermissionsListOfTheResponseShouldNotContainAGrantForUser(string $grantee): void {
$granteeId = $this->featureContext->getAttributeOfCreatedUser($grantee, 'id');
$grantees = $this->getGranteeUserIdsFromResponse();
Assert::assertNotContains(
$granteeId,
$grantees,
"Did not expect a grant for user '$grantee' ($granteeId) but it was present"
);
}
/**
* @param string $user
* @param string $share
*
* @return void
* @throws GuzzleException
*/
#[When('user :user gets permissions list of shared resource :share using the Graph API')]
public function userGetsPermissionsListOfSharedResourceUsingTheGraphAPI(string $user, string $share): void {
$credentials = $this->featureContext->graphContext->getAdminOrUserCredentials($user);
$sharedWithMe = GraphHelper::getSharesSharedWithMe(
$this->featureContext->getBaseUrl(),
$this->featureContext->getStepLineRef(),
$credentials['username'],
$credentials['password']
);
$jsonBody = $this->featureContext->getJsonDecodedResponseBodyContent($sharedWithMe);
$driveId = null;
$itemId = null;
foreach ($jsonBody->value as $item) {
if (isset($item->name) && $item->name === $share && isset($item->remoteItem->id)) {
$itemId = $item->remoteItem->id;
$driveId = $item->remoteItem->parentReference->driveId;
break;
}
}
Assert::assertNotNull(
$itemId,
"Cannot find shared resource '$share' in the shared-with-me list of user '$user'"
);
$this->featureContext->setResponse(
GraphHelper::getPermissionsList(
$this->featureContext->getBaseUrl(),
$this->featureContext->getStepLineRef(),
$credentials['username'],
$credentials['password'],
$driveId,
$itemId
)
);
}
/**
*
* @param string $user

View File

@@ -302,6 +302,37 @@ class SpacesContext implements Context {
throw new Exception("Cannot find share: $share");
}
/**
* @param string $user
* @param string $share
*
* @return string
*
* @throws Exception|GuzzleException
*/
public function getSharesMountId(string $user, string $share): string {
$credentials = $this->featureContext->graphContext->getAdminOrUserCredentials($user);
$response = GraphHelper::getSharesSharedWithMe(
$this->featureContext->getBaseUrl(),
$this->featureContext->getStepLineRef(),
$credentials['username'],
$credentials['password']
);
$jsonBody = $this->featureContext->getJsonDecodedResponseBodyContent($response);
foreach ($jsonBody->value as $item) {
if (isset($item->name) && $item->name === $share) {
if (isset($item->id)) {
return $item->id;
}
throw new Exception("Failed to find mount ID for share: $share");
}
}
throw new Exception("Cannot find share: $share");
}
/**
* The method finds file by fileName and spaceName and returns data of file which contains in responseHeader
* fileName contains the path, if the file is in the folder

View File

@@ -326,6 +326,7 @@ default:
- SharingNgContext:
- OcConfigContext:
- SettingsContext:
- FilesVersionsContext:
apiSharingNg2:
paths:
@@ -355,6 +356,7 @@ default:
- FeatureContext: *common_feature_context_params
- SpacesContext:
- SharingNgContext:
- OcConfigContext:
apiSharingNgLinkSharePermission:
paths:

View File

@@ -349,6 +349,11 @@ _ocdav: api compatibility, return correct status code_
- [cliCommands/restoreTrashBinItems.feature:23](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/cliCommands/restoreTrashBinItems.feature#L23)
- [cliCommands/sharesCleanup.feature:12](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/cliCommands/sharesCleanup.feature#L12)
#### [user with roleFileEditorWithVersions permission cannot restore version of file](https://github.com/opencloud-eu/opencloud/issues/3168)
- [apiSharingNg1/shareFileVersions.feature:55](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSharingNg1/shareFileVersions.feature#L55)
- [apiSharingNg1/shareFileVersions.feature:56](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSharingNg1/shareFileVersions.feature#L56)
### Won't fix
Not everything needs to be implemented for opencloud.

View File

@@ -354,6 +354,10 @@ tests/acceptance/features/cliCommands/restoreTrashBinItems.feature
- [cliCommands/restoreTrashBinItems.feature:23](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/cliCommands/restoreTrashBinItems.feature#L23)
- [cliCommands/sharesCleanup.feature:12](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/cliCommands/sharesCleanup.feature#L12)
#### [user with roleFileEditorWithVersions permission cannot restore version of file](https://github.com/opencloud-eu/opencloud/issues/3168)
- [apiSharingNg1/shareFileVersions.feature:55](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSharingNg1/shareFileVersions.feature#L55)
- [apiSharingNg1/shareFileVersions.feature:56](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSharingNg1/shareFileVersions.feature#L56)
### Won't fix

View File

@@ -6,6 +6,64 @@ Feature: List a sharing permissions
| username |
| Alice |
@env-config
Scenario Outline: enabled additional role is listed in the permissions of a folder
Given the administrator has enabled the permissions role "<permissions-role>"
And user "Alice" has created folder "folder"
When user "Alice" gets permissions list for folder "folder" of the space "Personal" using the Graph API
Then the HTTP status code should be "200"
And the permissions roles allowed values should contain a role with id "<role-id>"
Examples:
| permissions-role | role-id |
| Viewer With Versions | d1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5 |
| Viewer List Grants | d5041006-ebb3-4b4a-b6a4-7c180ecfb17d |
| Editor With Versions | b8c6e1c9-5d2a-4f0e-9c3b-1a2b3c4d5e6f |
| Editor List Grants | e8ea8b21-abd4-45d2-b893-8d1546378e9e |
@env-config
Scenario Outline: enabled additional role is listed in the permissions of a file
Given the administrator has enabled the permissions role "<permissions-role>"
And user "Alice" has uploaded file with content "hello" to "textfile.txt"
When user "Alice" gets permissions list for file "textfile.txt" of the space "Personal" using the Graph API
Then the HTTP status code should be "200"
And the permissions roles allowed values should contain a role with id "<role-id>"
Examples:
| permissions-role | role-id |
| Viewer With Versions | d1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5 |
| Viewer List Grants | d5041006-ebb3-4b4a-b6a4-7c180ecfb17d |
| File Editor With Versions | 3d00ce52-1fc2-4dbc-8b95-a73b73395f5a |
| File Editor List Grants | c1235aea-d106-42db-8458-7d5610fb0a67 |
@env-config
Scenario Outline: sharee with a list-grants role can see the other grants of a shared resource
Given user "Brian" has been created with default attributes
And user "Carol" has been created with default attributes
And the administrator has enabled the permissions role "<permissions-role>"
And user "Alice" has uploaded file with content "hello" to "textfile.txt"
And user "Alice" has sent the following resource share invitation:
| resource | textfile.txt |
| space | Personal |
| sharee | Brian |
| shareType | user |
| permissionsRole | <permissions-role> |
And user "Alice" has sent the following resource share invitation:
| resource | textfile.txt |
| space | Personal |
| sharee | Carol |
| shareType | user |
| permissionsRole | Viewer |
And user "Brian" has a share "textfile.txt" synced
When user "Brian" gets permissions list of shared resource "textfile.txt" using the Graph API
Then the HTTP status code should be "200"
And the permissions list of the response should contain a grant for user "Carol"
When user "Carol" gets permissions list of shared resource "textfile.txt" using the Graph API
Then the HTTP status code should be "200"
And the permissions list of the response should not contain a grant for user "Brian"
Examples:
| permissions-role |
| Viewer List Grants |
| File Editor List Grants |
Scenario: user lists permissions of a folder in personal space
Given user "Alice" has created folder "folder"
@@ -2345,6 +2403,104 @@ Feature: List a sharing permissions
}
"""
@env-config
Scenario: user lists permissions of a space after enabling 'Space Viewer With Versions' role
Given the administrator has enabled the permissions role "Space Viewer With Versions"
And the administrator has assigned the role "Space Admin" to user "Alice" using the Graph API
And user "Alice" has created a space "new-space" with the default quota using the Graph API
When user "Alice" lists the permissions of space "new-space" using root endpoint of the Graph API
Then the HTTP status code should be "200"
And the JSON data of the response should match
"""
{
"type": "object",
"required": [
"@libre.graph.permissions.actions.allowedValues",
"@libre.graph.permissions.roles.allowedValues"
],
"properties": {
"@libre.graph.permissions.roles.allowedValues": {
"type": "array",
"minItems": 4,
"maxItems": 4,
"uniqueItems": true,
"items": {
"oneOf": [
{
"type": "object",
"required": [
"@libre.graph.weight",
"description",
"displayName",
"id"
],
"properties": {
"displayName": {
"const": "Can view"
},
"id": {
"const": "a8d5fe5e-96e3-418d-825b-534dbdf22b99"
}
}
},
{
"type": "object",
"required": [
"@libre.graph.weight",
"description",
"displayName",
"id"
],
"properties": {
"@libre.graph.weight": {
"const": 40
},
"description": {
"const": "View and download including the history."
},
"displayName": {
"const": "Can view"
},
"id": {
"const": "3de465fc-6e17-4839-8b8a-a77cc497878b"
}
}
},
{
"type": "object",
"required": [
"@libre.graph.weight",
"description",
"displayName",
"id"
],
"properties": {
"displayName": {
"const": "Can edit"
}
}
},
{
"type": "object",
"required": [
"@libre.graph.weight",
"description",
"displayName",
"id"
],
"properties": {
"displayName": {
"const": "Can manage"
}
}
}
]
}
}
}
}
"""
@env-config
Scenario: user lists permissions of a folder after enabling 'Denied' role
Given the administrator has enabled the permissions role "Denied"

View File

@@ -0,0 +1,56 @@
Feature: file versions of a shared resource
As a user
I want to access versions of a resource shared with me
So that I can view and manage its history according to my role
# These scenarios address the shared file the same way a web client does: by the
# Shares-mount id resolved from the sharee's shared-with-me list (not by the owner's
# storage resource id), so that regressions of the sharee code path are caught.
Background:
Given these users have been created with default attributes:
| username |
| Alice |
| Brian |
And user "Alice" has uploaded file with content "hello world version 1" to "text.txt"
And user "Alice" has uploaded file with content "hello world version 1.1" to "text.txt"
@env-config
Scenario Outline: sharee can view file versions of a shared file while upload and restore depend on the role
Given the administrator has enabled the permissions role "<role>"
And user "Alice" has sent the following resource share invitation:
| resource | text.txt |
| space | Personal |
| sharee | Brian |
| shareType | user |
| permissionsRole | <role> |
And user "Brian" has a share "text.txt" synced
When user "Brian" gets the number of versions of shared resource "text.txt"
Then the HTTP status code should be "207"
And the number of versions should be "1"
When user "Brian" uploads file with content "shared file new version" to "/Shares/text.txt" using the WebDAV API
Then the HTTP status code should be "<upload-code>"
Examples:
| role | upload-code |
| Viewer With Versions | 403 |
| File Editor With Versions | 204 |
@env-config @issue-3168
Scenario Outline: sharee tries to restore file version
Given the administrator has enabled the permissions role "<role>"
And user "Alice" has sent the following resource share invitation:
| resource | text.txt |
| space | Personal |
| sharee | Brian |
| shareType | user |
| permissionsRole | <role> |
And user "Brian" has a share "text.txt" synced
When user "Brian" gets the number of versions of shared resource "text.txt"
Then the HTTP status code should be "207"
And the number of versions should be "1"
When user "Brian" restores version index "1" of shared resource "text.txt"
Then the HTTP status code should be "<restore-code>"
Examples:
| role | restore-code |
| Viewer With Versions | 403 |
| File Editor With Versions | 204 |

View File

@@ -97,6 +97,95 @@ Feature: Send a sharing invitations
| Editor | FolderToShare |
| Uploader | FolderToShare |
@env-config
Scenario Outline: send share invitation to user with the disabled-by-default roles
Given the administrator has enabled the permissions role "<permissions-role>"
And user "Alice" has uploaded file with content "to share" to "/textfile1.txt"
And user "Alice" has created folder "FolderToShare"
When user "Alice" sends the following resource share invitation using the Graph API:
| resource | <resource> |
| space | Personal |
| sharee | Brian |
| shareType | user |
| permissionsRole | <permissions-role> |
Then the HTTP status code should be "200"
And user "Brian" has a share "<resource>" synced
And user "Brian" should have a share "<resource>" shared by user "Alice" from space "Personal"
And the JSON data of the response should match
"""
{
"type": "object",
"required": [
"value"
],
"properties": {
"value": {
"type": "array",
"maxItems": 1,
"minItems": 1,
"items": {
"type": "object",
"required": [
"createdDateTime",
"id",
"roles",
"grantedToV2"
],
"properties": {
"id": {
"type": "string",
"pattern": "^%permissions_id_pattern%$"
},
"roles": {
"type": "array",
"maxItems": 1,
"minItems": 1,
"items": {
"type": "string",
"pattern": "^%role_id_pattern%$"
}
},
"grantedToV2": {
"type": "object",
"required": [
"user"
],
"properties": {
"user": {
"type": "object",
"required": [
"id",
"displayName"
],
"properties": {
"id": {
"type": "string",
"pattern": "^%user_id_pattern%$"
},
"displayName": {
"const": "Brian Murphy"
}
}
}
}
}
}
}
}
}
}
"""
Examples:
| permissions-role | resource |
| Viewer With Versions | /textfile1.txt |
| Viewer With Versions | FolderToShare |
| File Editor With Versions | /textfile1.txt |
| Editor With Versions | FolderToShare |
| Viewer List Grants | /textfile1.txt |
| Viewer List Grants | FolderToShare |
| File Editor List Grants | /textfile1.txt |
| Editor List Grants | FolderToShare |
Scenario Outline: send share invitation to group with different roles
Given user "Carol" has been created with default attributes

View File

@@ -3,6 +3,22 @@ Feature: checking file versions using file id
I want the versions of files to be available
So that I can manage the changes made to the files
role view upload restore
Viewer no no no
Viewer With Versions yes no no
File Editor no yes no
File Editor With Versions yes yes yes
Editor no yes no
Editor With Versions yes yes yes
Space Viewer no no no
Space Viewer With Versions yes no no
Space Editor Without Versions no yes no
Space Editor yes yes yes
Manager yes yes yes
Background:
Given these users have been created with default attributes:
| username |
@@ -62,6 +78,46 @@ Feature: checking file versions using file id
When user "Brian" tries to get the number of versions of file "/text.txt" using file-id "<<FILEID>>"
Then the HTTP status code should be "403"
@env-config
Scenario: sharee can view file versions in a shared space as space viewer with versions role
Given the administrator has enabled the permissions role "Space Viewer With Versions"
And user "Alice" has sent the following space share invitation:
| space | Project1 |
| sharee | Brian |
| shareType | user |
| permissionsRole | Space Viewer With Versions |
When user "Brian" gets the number of versions of file "/text.txt" using file-id "<<FILEID>>"
Then the HTTP status code should be "207"
And the number of versions should be "1"
When user "Brian" restores version index "1" of file "/text.txt" using file-id "<<FILEID>>"
Then the HTTP status code should be "403"
@env-config
Scenario Outline: sharee can view file versions of a file inside a shared folder while upload and restore depend on the role
Given the administrator has enabled the permissions role "<role>"
And user "Alice" has created a folder "folderToShare" in space "Project1"
And user "Alice" has uploaded a file inside space "Project1" with content "folder file version 1" to "folderToShare/insideText.txt"
And user "Alice" has uploaded a file inside space "Project1" with content "folder file version 1.1" to "folderToShare/insideText.txt"
And we save it into "FOLDERFILEID"
And user "Alice" has sent the following resource share invitation:
| resource | folderToShare |
| space | Project1 |
| sharee | Brian |
| shareType | user |
| permissionsRole | <role> |
And user "Brian" has a share "folderToShare" synced
When user "Brian" gets the number of versions of file "/Shares/folderToShare/insideText.txt" using file-id "<<FOLDERFILEID>>"
Then the HTTP status code should be "207"
And the number of versions should be "1"
When user "Brian" uploads file with content "folder file new version" to "/Shares/folderToShare/insideText.txt" using the WebDAV API
Then the HTTP status code should be "<upload-code>"
When user "Brian" restores version index "1" of file "/Shares/folderToShare/insideText.txt" using file-id "<<FOLDERFILEID>>"
Then the HTTP status code should be "<restore-code>"
Examples:
| role | upload-code | restore-code |
| Viewer With Versions | 403 | 403 |
| Editor With Versions | 204 | 204 |
@issue-7738
Scenario Outline: check the versions of a file after moving to a shared folder inside a project space as editor/viewer
Given user "Alice" has created a folder "testFolder" in space "Project1"