diff --git a/.bingo/go-xgettext.mod b/.bingo/go-xgettext.mod index 8e3c6210b5..b14946a0b8 100644 --- a/.bingo/go-xgettext.mod +++ b/.bingo/go-xgettext.mod @@ -3,3 +3,5 @@ module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT go 1.23.4 require github.com/gosexy/gettext v0.0.0-20160830220431-74466a0a0c4a // go-xgettext + +require github.com/jessevdk/go-flags v1.6.1 // indirect diff --git a/.woodpecker.star b/.woodpecker.star index 7b7e8fb420..6d49238f3f 100644 --- a/.woodpecker.star +++ b/.woodpecker.star @@ -232,6 +232,12 @@ config = { ], "skip": False, }, + "sharingNgAdditionalShareRole": { + "suites": [ + "apiSharingNgAdditionalShareRole", + ], + "skip": False, + }, "notification": { "suites": [ "apiNotification", diff --git a/services/graph/pkg/config/defaults/defaultconfig.go b/services/graph/pkg/config/defaults/defaultconfig.go index c2c669469d..598e4a099e 100644 --- a/services/graph/pkg/config/defaults/defaultconfig.go +++ b/services/graph/pkg/config/defaults/defaultconfig.go @@ -17,9 +17,13 @@ var ( unifiedrole.UnifiedRoleSecureViewerID, unifiedrole.UnifiedRoleSpaceViewerWithVersionsID, unifiedrole.UnifiedRoleSpaceEditorWithoutVersionsID, + unifiedrole.UnifiedRoleSpaceEditorWithoutTrashbinID, + unifiedrole.UnifiedRoleSpaceEditorWithoutVersionsWithoutTrashbinID, unifiedrole.UnifiedRoleViewerListGrantsID, unifiedrole.UnifiedRoleEditorListGrantsID, + unifiedrole.UnifiedRoleEditorListGrantsWithVersionsID, unifiedrole.UnifiedRoleFileEditorListGrantsID, + unifiedrole.UnifiedRoleFileEditorListGrantsWithVersionsID, unifiedrole.UnifiedRoleViewerWithVersionsID, unifiedrole.UnifiedRoleEditorWithVersionsID, unifiedrole.UnifiedRoleFileEditorWithVersionsID, diff --git a/services/graph/pkg/l10n/locale/de/LC_MESSAGES/graph.po b/services/graph/pkg/l10n/locale/de/LC_MESSAGES/graph.po index ad7453067e..957b2cfc1b 100644 --- a/services/graph/pkg/l10n/locale/de/LC_MESSAGES/graph.po +++ b/services/graph/pkg/l10n/locale/de/LC_MESSAGES/graph.po @@ -162,3 +162,28 @@ msgid "View, download, upload, edit, add, delete including the history." msgstr "" "Ansehen, herunterladen, hochladen, bearbeiten, hinzufügen, löschen - " "inklusive des Verlaufs." + +#. UnifiedRole SpaceEditorWithoutTrashbin, Role DisplayName (resolves +#. directly) +#: pkg/unifiedrole/roles.go:183 +msgid "Can edit with versions" +msgstr "Kann bearbeiten mit Historie" + +#. UnifiedRole EditorListGrantsWithVersions, Role Description (resolves +#. directly) +#: pkg/unifiedrole/roles.go:162 +msgid "" +"View, download, upload, edit, delete, show all versions and all invited " +"people." +msgstr "" +"Ansehen, herunterladen, hochladen, bearbeiten, löschen und anzeigen aller " +"Versionen und eingeladenen Personen." + +#. UnifiedRole FileEditorListGrantsWithVersions, Role Description (resolves +#. directly) +#: pkg/unifiedrole/roles.go:201 +msgid "" +"View, download, upload, edit, show all versions and all invited people." +msgstr "" +"Ansehen, herunterladen, hochladen, bearbeiten und anzeigen aller Versionen " +"und eingeladenen Personen." diff --git a/services/graph/pkg/unifiedrole/conversion.go b/services/graph/pkg/unifiedrole/conversion.go index a554ff2b9a..3e85e8a18b 100644 --- a/services/graph/pkg/unifiedrole/conversion.go +++ b/services/graph/pkg/unifiedrole/conversion.go @@ -219,16 +219,24 @@ func cs3RoleToDisplayName(role *conversions.Role) string { return _editorWithVersionsUnifiedRoleDisplayName case conversions.RoleEditorListGrants: return _editorListGrantsUnifiedRoleDisplayName + case conversions.RoleEditorListGrantsWithVersions: + return _editorListGrantsWithVersionsUnifiedRoleDisplayName case conversions.RoleSpaceEditor: return _spaceEditorUnifiedRoleDisplayName case conversions.RoleSpaceEditorWithoutVersions: return _spaceEditorWithoutVersionsUnifiedRoleDisplayName + case conversions.RoleSpaceEditorWithoutTrashbin: + return _spaceEditorWithoutTrashbinUnifiedRoleDisplayName + case conversions.RoleSpaceEditorWithoutVersionsWithoutTrashbin: + return _spaceEditorWithoutVersionsWithoutTrashbinUnifiedRoleDisplayName case conversions.RoleFileEditor: return _fileEditorUnifiedRoleDisplayName case conversions.RoleFileEditorWithVersions: return _fileEditorWithVersionsUnifiedRoleDisplayName case conversions.RoleFileEditorListGrants: return _fileEditorListGrantsUnifiedRoleDisplayName + case conversions.RoleFileEditorListGrantsWithVersions: + return _fileEditorListGrantsWithVersionsUnifiedRoleDisplayName case conversions.RoleEditorLite: return _editorLiteUnifiedRoleDisplayName case conversions.RoleManager: diff --git a/services/graph/pkg/unifiedrole/export_test.go b/services/graph/pkg/unifiedrole/export_test.go index c47ab0ea31..ffafccc3ec 100644 --- a/services/graph/pkg/unifiedrole/export_test.go +++ b/services/graph/pkg/unifiedrole/export_test.go @@ -1,23 +1,27 @@ package unifiedrole var ( - RoleViewer = roleViewer - RoleViewerWithVersions = roleViewerWithVersions - RoleViewerListGrants = roleViewerListGrants - RoleSpaceViewer = roleSpaceViewer - RoleSpaceViewerWithVersions = roleSpaceViewerWithVersions - RoleEditor = roleEditor - RoleEditorWithVersions = roleEditorWithVersions - RoleEditorListGrants = roleEditorListGrants - RoleSpaceEditor = roleSpaceEditor - RoleSpaceEditorWithVersions = roleSpaceEditorWithVersions - RoleFileEditor = roleFileEditor - RoleFileEditorWithVersions = roleFileEditorWithVersions - RoleFileEditorListGrants = roleFileEditorListGrants - RoleEditorLite = roleEditorLite - RoleManager = roleManager - RoleSecureViewer = roleSecureViewer - RoleDenied = roleDenied + RoleViewer = roleViewer + RoleViewerWithVersions = roleViewerWithVersions + RoleViewerListGrants = roleViewerListGrants + RoleSpaceViewer = roleSpaceViewer + RoleSpaceViewerWithVersions = roleSpaceViewerWithVersions + RoleEditor = roleEditor + RoleEditorWithVersions = roleEditorWithVersions + RoleEditorListGrants = roleEditorListGrants + RoleEditorListGrantsWithVersions = roleEditorListGrantsWithVersions + RoleSpaceEditor = roleSpaceEditor + RoleSpaceEditorWithVersions = roleSpaceEditorWithVersions + RoleSpaceEditorWithoutTrashbin = roleSpaceEditorWithoutTrashbin + RoleSpaceEditorWithoutVersionsWithoutTrashbin = roleSpaceEditorWithoutVersionsWithoutTrashbin + RoleFileEditor = roleFileEditor + RoleFileEditorWithVersions = roleFileEditorWithVersions + RoleFileEditorListGrants = roleFileEditorListGrants + RoleFileEditorListGrantsWithVersions = roleFileEditorListGrantsWithVersions + RoleEditorLite = roleEditorLite + RoleManager = roleManager + RoleSecureViewer = roleSecureViewer + RoleDenied = roleDenied BuildInRoles = buildInRoles diff --git a/services/graph/pkg/unifiedrole/roles.go b/services/graph/pkg/unifiedrole/roles.go index 52aeb0d2ab..e67bbdbcee 100644 --- a/services/graph/pkg/unifiedrole/roles.go +++ b/services/graph/pkg/unifiedrole/roles.go @@ -32,16 +32,25 @@ const ( UnifiedRoleEditorWithVersionsID = "b8c6e1c9-5d2a-4f0e-9c3b-1a2b3c4d5e6f" // UnifiedRoleEditorListGrantsID Unified role editor id. UnifiedRoleEditorListGrantsID = "e8ea8b21-abd4-45d2-b893-8d1546378e9e" + // UnifiedRoleEditorListGrantsWithVersionsID Unified role editor with list grants and versions id. + UnifiedRoleEditorListGrantsWithVersionsID = "0911d62b-1e3f-4778-8b1b-903b7e4e8476" // UnifiedRoleSpaceEditorID Unified role space editor id. UnifiedRoleSpaceEditorID = "58c63c02-1d89-4572-916a-870abc5a1b7d" // UnifiedRoleSpaceEditorWithoutVersionsID Unified role space editor without list/restore versions id. UnifiedRoleSpaceEditorWithoutVersionsID = "3284f2d5-0070-4ad8-ac40-c247f7c1fb27" + // UnifiedRoleSpaceEditorWithoutTrashbinID Unified role space editor without list/restore resources in trashbin id. + UnifiedRoleSpaceEditorWithoutTrashbinID = "8f4701d9-c68f-4109-a482-88e22ee32805" + // UnifiedRoleSpaceEditorWithoutVersionsWithoutTrashbinID Unified role space editor without list/restore + // versions and without list/restore resources in trashbin id. + UnifiedRoleSpaceEditorWithoutVersionsWithoutTrashbinID = "a5f73816-4d4b-452d-8973-3b61c3d0bed4" // UnifiedRoleFileEditorID Unified role file editor id. UnifiedRoleFileEditorID = "2d00ce52-1fc2-4dbc-8b95-a73b73395f5a" // UnifiedRoleFileEditorWithVersionsID Unified role file editor id. UnifiedRoleFileEditorWithVersionsID = "3d00ce52-1fc2-4dbc-8b95-a73b73395f5a" // UnifiedRoleFileEditorListGrantsID Unified role file editor id. UnifiedRoleFileEditorListGrantsID = "c1235aea-d106-42db-8458-7d5610fb0a67" + // UnifiedRoleFileEditorListGrantsWithVersionsID Unified role file editor with list grants and versions id. + UnifiedRoleFileEditorListGrantsWithVersionsID = "b173329d-cf2e-42f0-a595-ee410645d840" // UnifiedRoleEditorLiteID Unified role editor-lite id. UnifiedRoleEditorLiteID = "1c996275-f1c9-4e71-abdf-a42f6495e960" // UnifiedRoleManagerID Unified role manager id. @@ -149,6 +158,12 @@ var ( // UnifiedRole EditorListGrants, Role DisplayName (resolves directly) _editorListGrantsUnifiedRoleDisplayName = l10n.Template("Can edit") + // UnifiedRole EditorListGrantsWithVersions, Role Description (resolves directly) + _editorListGrantsWithVersionsUnifiedRoleDescription = l10n.Template("View, download, upload, edit, delete, show all versions and all invited people.") + + // UnifiedRole EditorListGrantsWithVersions, Role DisplayName (resolves directly) + _editorListGrantsWithVersionsUnifiedRoleDisplayName = l10n.Template("Can edit") + // UnifiedRole SpaseEditor, Role Description (resolves directly) _spaceEditorUnifiedRoleDescription = l10n.Template("View, download, upload, edit, add, delete including the history.") @@ -161,6 +176,18 @@ var ( // UnifiedRole SpaseEditorWithoutVersions, Role DisplayName (resolves directly) _spaceEditorWithoutVersionsUnifiedRoleDisplayName = l10n.Template("Can edit without versions") + // UnifiedRole SpaceEditorWithoutTrashbin, Role Description (resolves directly) + _spaceEditorWithoutTrashbinUnifiedRoleDescription = l10n.Template("View, download, upload, edit, add, delete including the history.") + + // UnifiedRole SpaceEditorWithoutTrashbin, Role DisplayName (resolves directly) + _spaceEditorWithoutTrashbinUnifiedRoleDisplayName = l10n.Template("Can edit with versions") + + // UnifiedRole SpaceEditorWithoutVersionsWithoutTrashbin, Role Description (resolves directly) + _spaceEditorWithoutVersionsWithoutTrashbinUnifiedRoleDescription = l10n.Template("View, download, upload, edit, add and delete.") + + // UnifiedRole SpaceEditorWithoutVersionsWithoutTrashbin, Role DisplayName (resolves directly) + _spaceEditorWithoutVersionsWithoutTrashbinUnifiedRoleDisplayName = l10n.Template("Can edit") + // UnifiedRole FileEditor, Role Description (resolves directly) _fileEditorUnifiedRoleDescription = l10n.Template("View, download and edit.") @@ -170,6 +197,12 @@ var ( // UnifiedRole FileEditorListGrants, Role Description (resolves directly) _fileEditorListGrantsUnifiedRoleDescription = l10n.Template("View, download, edit and show all invited people.") + // UnifiedRole FileEditorListGrantsWithVersions, Role Description (resolves directly) + _fileEditorListGrantsWithVersionsUnifiedRoleDescription = l10n.Template("View, download, upload, edit, show all versions and all invited people.") + + // UnifiedRole FileEditorListGrantsWithVersions, Role DisplayName (resolves directly) + _fileEditorListGrantsWithVersionsUnifiedRoleDisplayName = l10n.Template("Can edit") + // UnifiedRole FileEditorWithVersions, Role DisplayName (resolves directly) _fileEditorWithVersionsUnifiedRoleDisplayName = l10n.Template("Can edit") @@ -227,12 +260,16 @@ var ( roleSpaceViewerWithVersions, roleEditor, roleEditorListGrants, + roleEditorListGrantsWithVersions, roleEditorWithVersions, roleSpaceEditorWithVersions, roleSpaceEditor, + roleSpaceEditorWithoutTrashbin, + roleSpaceEditorWithoutVersionsWithoutTrashbin, roleFileEditor, roleFileEditorWithVersions, roleFileEditorListGrants, + roleFileEditorListGrantsWithVersions, roleEditorLite, roleManager, roleSecureViewer, @@ -432,6 +469,27 @@ var ( } }() + // roleEditorListGrantsWithVersions creates an editor role that can also list versions. + roleEditorListGrantsWithVersions = func() *libregraph.UnifiedRoleDefinition { + r := conversions.NewEditorListGrantsWithVersionsRole() + return &libregraph.UnifiedRoleDefinition{ + Id: proto.String(UnifiedRoleEditorListGrantsWithVersionsID), + Description: proto.String(_editorListGrantsWithVersionsUnifiedRoleDescription), + DisplayName: proto.String(cs3RoleToDisplayName(r)), + RolePermissions: []libregraph.UnifiedRolePermission{ + { + AllowedResourceActions: CS3ResourcePermissionsToLibregraphActions(r.CS3ResourcePermissions()), + Condition: proto.String(UnifiedRoleConditionFolder), + }, + { + AllowedResourceActions: CS3ResourcePermissionsToLibregraphActions(r.CS3ResourcePermissions()), + Condition: proto.String(UnifiedRoleConditionFolderFederatedUser), + }, + }, + LibreGraphWeight: proto.Int32(72), + } + }() + roleEditorWithVersions = func() *libregraph.UnifiedRoleDefinition { r := conversions.NewEditorWithVersionsRole() return &libregraph.UnifiedRoleDefinition{ @@ -448,6 +506,42 @@ var ( } }() + // roleSpaceEditorWithoutVersionsWithoutTrashbin creates a space editor role without + // list/restore versions and without list/restore resources in the trashbin. + roleSpaceEditorWithoutVersionsWithoutTrashbin = func() *libregraph.UnifiedRoleDefinition { + r := conversions.NewSpaceEditorWithoutVersionsWithoutTrashbinRole() + return &libregraph.UnifiedRoleDefinition{ + Id: proto.String(UnifiedRoleSpaceEditorWithoutVersionsWithoutTrashbinID), + Description: proto.String(_spaceEditorWithoutVersionsWithoutTrashbinUnifiedRoleDescription), + DisplayName: proto.String(cs3RoleToDisplayName(r)), + RolePermissions: []libregraph.UnifiedRolePermission{ + { + AllowedResourceActions: CS3ResourcePermissionsToLibregraphActions(r.CS3ResourcePermissions()), + Condition: proto.String(UnifiedRoleConditionDrive), + }, + }, + LibreGraphWeight: proto.Int32(78), + } + }() + + // roleSpaceEditorWithoutTrashbin creates a space editor role without list/restore + // resources in the trashbin. + roleSpaceEditorWithoutTrashbin = func() *libregraph.UnifiedRoleDefinition { + r := conversions.NewSpaceEditorWithoutTrashbinRole() + return &libregraph.UnifiedRoleDefinition{ + Id: proto.String(UnifiedRoleSpaceEditorWithoutTrashbinID), + Description: proto.String(_spaceEditorWithoutTrashbinUnifiedRoleDescription), + DisplayName: proto.String(cs3RoleToDisplayName(r)), + RolePermissions: []libregraph.UnifiedRolePermission{ + { + AllowedResourceActions: CS3ResourcePermissionsToLibregraphActions(r.CS3ResourcePermissions()), + Condition: proto.String(UnifiedRoleConditionDrive), + }, + }, + LibreGraphWeight: proto.Int32(88), + } + }() + // roleSpaceEditor creates an editor without versions role roleSpaceEditor = func() *libregraph.UnifiedRoleDefinition { r := conversions.NewSpaceEditorWithoutVersionsRole() @@ -541,6 +635,27 @@ var ( } }() + // roleFileEditorListGrantsWithVersions creates a file-editor role that can also list versions. + roleFileEditorListGrantsWithVersions = func() *libregraph.UnifiedRoleDefinition { + r := conversions.NewFileEditorListGrantsWithVersionsRole() + return &libregraph.UnifiedRoleDefinition{ + Id: proto.String(UnifiedRoleFileEditorListGrantsWithVersionsID), + Description: proto.String(_fileEditorListGrantsWithVersionsUnifiedRoleDescription), + DisplayName: proto.String(cs3RoleToDisplayName(r)), + RolePermissions: []libregraph.UnifiedRolePermission{ + { + AllowedResourceActions: CS3ResourcePermissionsToLibregraphActions(r.CS3ResourcePermissions()), + Condition: proto.String(UnifiedRoleConditionFile), + }, + { + AllowedResourceActions: CS3ResourcePermissionsToLibregraphActions(r.CS3ResourcePermissions()), + Condition: proto.String(UnifiedRoleConditionFileFederatedUser), + }, + }, + LibreGraphWeight: proto.Int32(111), + } + }() + // roleManager creates a manager role roleManager = func() *libregraph.UnifiedRoleDefinition { r := conversions.NewManagerRole() diff --git a/services/graph/pkg/unifiedrole/roles_test.go b/services/graph/pkg/unifiedrole/roles_test.go index 986163e2cd..d6d910f008 100644 --- a/services/graph/pkg/unifiedrole/roles_test.go +++ b/services/graph/pkg/unifiedrole/roles_test.go @@ -185,6 +185,7 @@ func TestGetRolesByPermissions(t *testing.T) { unifiedrole.RoleFileEditor, unifiedrole.RoleFileEditorWithVersions, unifiedrole.RoleFileEditorListGrants, + unifiedrole.RoleFileEditorListGrantsWithVersions, }, }, "BuildInRoles | folder": { @@ -199,6 +200,7 @@ func TestGetRolesByPermissions(t *testing.T) { unifiedrole.RoleEditor, unifiedrole.RoleEditorListGrants, unifiedrole.RoleEditorWithVersions, + unifiedrole.RoleEditorListGrantsWithVersions, unifiedrole.RoleDenied, }, }, @@ -208,7 +210,9 @@ func TestGetRolesByPermissions(t *testing.T) { unifiedRoleDefinition: []*libregraph.UnifiedRoleDefinition{ unifiedrole.RoleSpaceViewer, unifiedrole.RoleSpaceViewerWithVersions, + unifiedrole.RoleSpaceEditorWithoutVersionsWithoutTrashbin, unifiedrole.RoleSpaceEditor, + unifiedrole.RoleSpaceEditorWithoutTrashbin, unifiedrole.RoleSpaceEditorWithVersions, unifiedrole.RoleManager, }, diff --git a/tests/acceptance/TestHelpers/GraphHelper.php b/tests/acceptance/TestHelpers/GraphHelper.php index 8fda5ccc58..4a1a6e7fa5 100644 --- a/tests/acceptance/TestHelpers/GraphHelper.php +++ b/tests/acceptance/TestHelpers/GraphHelper.php @@ -49,6 +49,10 @@ class GraphHelper { 'File Editor With Versions' => '3d00ce52-1fc2-4dbc-8b95-a73b73395f5a', 'File Editor List Grants' => 'c1235aea-d106-42db-8458-7d5610fb0a67', 'Denied' => '63e64e19-8d43-42ec-a738-2b6af2610efa', + 'Editor List Grants With Versions' => '0911d62b-1e3f-4778-8b1b-903b7e4e8476', + 'Space Editor Without Trashbin' => '8f4701d9-c68f-4109-a482-88e22ee32805', + 'Space Editor Without Versions Without Trashbin' => 'a5f73816-4d4b-452d-8973-3b61c3d0bed4', + 'File Editor List Grants With Versions' => 'b173329d-cf2e-42f0-a595-ee410645d840', ]; public const SHARES_SPACE_ID = 'a0ca6a90-a365-4782-871e-d44447bbc668$a0ca6a90-a365-4782-871e-d44447bbc668'; diff --git a/tests/acceptance/bootstrap/GraphContext.php b/tests/acceptance/bootstrap/GraphContext.php index 8129bb35f0..3cce71e6ee 100644 --- a/tests/acceptance/bootstrap/GraphContext.php +++ b/tests/acceptance/bootstrap/GraphContext.php @@ -2921,15 +2921,9 @@ class GraphContext implements Context { string $resource, string $spaceName ): void { - $resourceId = $this->featureContext->spacesContext->getResourceId($user, $spaceName, $resource); - $response = GraphHelper::getActivities( - $this->featureContext->getBaseUrl(), - $this->featureContext->getStepLineRef(), - $user, - $this->featureContext->getPasswordForUser($user), - $resourceId + $this->featureContext->setResponse( + $this->getActivities($user, $resource, $spaceName) ); - $this->featureContext->setResponse($response); } /** @@ -3490,4 +3484,107 @@ class GraphContext implements Context { $url = "/graph/$apiVersion/drives/$driveId/root:/$encoded"; $this->sendGraphRequestAndCaptureResponse($user, "GET", $url); } + + /** + * @param string $user + * @param string $resource + * @param string $spaceName + * + * @return ResponseInterface + * @throws GuzzleException + */ + public function getActivities( + string $user, + string $resource, + string $spaceName + ): ResponseInterface { + if ($spaceName === "Shares") { + $resourceId = $this->spacesContext->getSharesRemoteItemId($user, $resource); + } else { + $resourceId = $this->spacesContext->getResourceId($user, $spaceName, $resource); + } + return GraphHelper::getActivities( + $this->featureContext->getBaseUrl(), + $this->featureContext->getStepLineRef(), + $user, + $this->featureContext->getPasswordForUser($user), + $resourceId + ); + } + + /** + * @param string $user + * @param string $resource + * @param string $spaceName + * @param TableNode $table + * + * @return void + * @throws GuzzleException + */ + #[Then('/^for user "([^"]*)" (?:folder|file) "([^"]*)" of the space "([^"]*)" should have the following activities:$/')] + public function forUserFolderOrFileOfTheSpaceShouldHaveTheseActivities( + string $user, + string $resource, + string $spaceName, + TableNode $table + ): void { + $expectedMessages = \array_map(fn ($row) => $row[0], $table->getRows()); + + // Activities are recorded asynchronously from events, so poll until every + // expected activity shows up (or the wait times out). + $actualMessages = []; + WaitHelper::waitUntil( + function () use ($user, $resource, $spaceName, &$actualMessages) { + $activities = $this->featureContext->getJsonDecodedResponse( + $this->getActivities($user, $resource, $spaceName) + ); + $actualMessages = \array_map( + fn ($activity) => $activity['template']['message'], + $activities['value'] ?? [] + ); + }, + function () use ($expectedMessages, &$actualMessages) { + foreach ($expectedMessages as $message) { + if (!\in_array($message, $actualMessages, true)) { + return false; + } + } + return true; + } + ); + + $errors = []; + foreach ($expectedMessages as $message) { + if (!\in_array($message, $actualMessages, true)) { + $errors[] = "Expected activity '$message' was not found in the response. "; + } + } + if (!empty($errors)) { + Assert::fail(implode("\n", $errors)); + } + } + + /** + * @param string $user + * @param string $resource + * @param string $spaceName + * + * @return void + * @throws GuzzleException + */ + #[Then('/^for user "([^"]*)" (?:folder|file) "([^"]*)" of the space "([^"]*)" should not have any activity$/')] + public function forUserFileOfTheSpaceShouldNotHaveAnyActivity( + string $user, + string $resource, + string $spaceName + ): void { + $response = $this->getActivities($user, $resource, $spaceName); + $responseBody = $response->getBody()->getContents(); + Assert::assertEmpty( + $responseBody, + __METHOD__ + . "\nExpected no activity of resource '$resource' for user '$user', but some activities were found\n" + . print_r(json_decode($responseBody, true), true) + ); + } } diff --git a/tests/acceptance/bootstrap/OcConfigContext.php b/tests/acceptance/bootstrap/OcConfigContext.php index b4efb0cf5c..1e1bb4fd8e 100644 --- a/tests/acceptance/bootstrap/OcConfigContext.php +++ b/tests/acceptance/bootstrap/OcConfigContext.php @@ -123,6 +123,36 @@ class OcConfigContext implements Context { $this->setEnabledPermissionsRoles($defaultRoles); } + /** + * + * @param TableNode $table + * + * @return void + */ + #[Given('the administrator has enabled the following share permissions roles:')] + public function theAdministratorHasEnabledTheFollowingSharePermissionsRoles(TableNode $table): void { + $defaultRoles = array_values(GraphHelper::DEFAULT_PERMISSIONS_ROLES); + $roles = []; + foreach ($table->getHash() as $row) { + $roles[] = $row['permissions-role']; + $roleId = GraphHelper::getPermissionsRoleIdByName($row['permissions-role']); + if (!\in_array($roleId, $defaultRoles)) { + $defaultRoles[] = $roleId; + } + } + $envs = [ + "GRAPH_AVAILABLE_ROLES" => implode(',', $defaultRoles), + ]; + $response = OcConfigHelper::reConfigureOc($envs); + Assert::assertEquals( + 200, + $response->getStatusCode(), + "Failed to enable roles: " . implode(', ', $roles) + . ". Response: " . $response->getBody()->getContents() + ); + $this->setEnabledPermissionsRoles($defaultRoles); + } + /** * * @param string $role diff --git a/tests/acceptance/bootstrap/SharingNgContext.php b/tests/acceptance/bootstrap/SharingNgContext.php index ffc7b31450..4aa1d82d4c 100644 --- a/tests/acceptance/bootstrap/SharingNgContext.php +++ b/tests/acceptance/bootstrap/SharingNgContext.php @@ -26,6 +26,7 @@ use GuzzleHttp\Exception\GuzzleException; use PHPUnit\Framework\Assert; use Psr\Http\Message\ResponseInterface; use TestHelpers\GraphHelper; +use TestHelpers\WaitHelper; use TestHelpers\WebDavHelper; use TestHelpers\HttpRequestHelper; use TestHelpers\BehatHelper; @@ -161,12 +162,18 @@ class SharingNgContext implements Context { ?string $resource = '', ?string $query = null ): ResponseInterface { - $spaceId = ($this->spacesContext->getSpaceByName($user, $space))["id"]; - - if ($fileOrFolder === 'folder') { - $itemId = $this->spacesContext->getResourceId($user, $space, $resource); + if ($space === "Shares" && $resource !== '') { + // a shared resource lives in the owner's space; its permissions are + // listed via the share's remote item id and its parent drive id + $spaceId = $this->spacesContext->getSharesRemoteItemParentDriveId($user, $resource); + $itemId = $this->spacesContext->getSharesRemoteItemId($user, $resource); } else { - $itemId = $this->spacesContext->getFileId($user, $space, $resource); + $spaceId = ($this->spacesContext->getSpaceByName($user, $space))["id"]; + if ($fileOrFolder === 'folder') { + $itemId = $this->spacesContext->getResourceId($user, $space, $resource); + } else { + $itemId = $this->spacesContext->getFileId($user, $space, $resource); + } } return GraphHelper::getPermissionsList( @@ -233,6 +240,63 @@ class SharingNgContext implements Context { ); } + /** + * @param string $user + * @param string $fileOrFolder (file|folder) + * @param string $resource + * @param TableNode $table + * + * @return void + * @throws GuzzleException + */ + #[Then('/^for user "([^"]*)" (file|folder) "([^"]*)" should have the following shares:$/')] + public function userGetsAllTheSharesOfTheResource( + string $user, + string $fileOrFolder, + string $resource, + TableNode $table + ): void { + $permission = $this->getPermissionsList($user, $fileOrFolder, "Shares", $resource); + $jsonBody = $this->featureContext->getJsonDecodedResponseBodyContent($permission); + + $errors = []; + foreach ($table->getHash() as $row) { + $expectedRoleId = GraphHelper::getPermissionsRoleIdByName($row['permissionsRole']); + if ($row['shareType'] === 'user') { + $expectedSharee = $this->featureContext->getDisplayNameForUser($row['sharee']); + } else { + $expectedSharee = $row['sharee']; + } + $found = false; + $actualSharee = ''; + foreach ($jsonBody->value as $share) { + if ($row['shareType'] === 'user') { + if (isset($share->grantedToV2->user->displayName)) { + $actualSharee = $share->grantedToV2->user->displayName; + } + } else { + if (isset($share->grantedToV2->group->displayName)) { + $actualSharee = $share->grantedToV2->group->displayName; + } + } + if ($actualSharee === $expectedSharee) { + $found = true; + if ($share->roles[0] !== $expectedRoleId) { + $errors[] = "Expected user $actualSharee share role id to be '$expectedRoleId'" + . " but found '{$share->roles[0]}'"; + } + break; + } + } + if (!$found) { + $errors[] = "Expected sharee '$expectedSharee' to be present but found '$actualSharee'"; + } + } + if (!empty($errors)) { + Assert::fail(implode("\n", $errors)); + } + } + /** * * @param string $user @@ -458,10 +522,30 @@ class SharingNgContext implements Context { $rows, "'resource' should be provided in the data-table while sharing a resource" ); - $response = $this->sendShareInvitation($user, $rows); + $response = WaitHelper::waitUntil( + fn () => $this->sendShareInvitation($user, $rows), + fn ($response) => !self::isShareManagerMigrating($response), + null, + 30 + ); $this->featureContext->theHTTPStatusCodeShouldBe(200, "", $response); } + /** + * @param ResponseInterface $response + * + * @return bool + */ + private static function isShareManagerMigrating(ResponseInterface $response): bool { + if ($response->getStatusCode() !== 500) { + return false; + } + return \str_contains( + (string)$response->getBody(), + "share manager is currently migrating" + ); + } + /** * * @param string $user diff --git a/tests/acceptance/bootstrap/SpacesContext.php b/tests/acceptance/bootstrap/SpacesContext.php index 2d20b3ce66..89fc9870c6 100644 --- a/tests/acceptance/bootstrap/SpacesContext.php +++ b/tests/acceptance/bootstrap/SpacesContext.php @@ -307,6 +307,38 @@ class SpacesContext implements Context { throw new Exception("Cannot find share: $share"); } + /** + * @param string $user + * @param string $share + * + * @return string + * + * @throws Exception|GuzzleException + */ + public function getSharesRemoteItemParentDriveId(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); + + // Search parent driveId of a given share's remoteItem + foreach ($jsonBody->value as $item) { + if (isset($item->name) && $item->name === $share) { + if (isset($item->remoteItem->parentReference->driveId)) { + return $item->remoteItem->parentReference->driveId; + } + throw new Exception("Failed to find remoteItem parent driveId for share: $share"); + } + } + + throw new Exception("Cannot find share: $share"); + } + /** * @param string $user * @param string $share @@ -3145,6 +3177,7 @@ class SpacesContext implements Context { * @throws GuzzleException */ #[When('user :user lists all deleted files in the trash bin of the space :spaceName')] + #[When('user :user tries to list all deleted files in the trash bin of the space :spaceName')] public function userListAllDeletedFilesInTrash( string $user, string $spaceName @@ -3264,6 +3297,7 @@ class SpacesContext implements Context { * @throws Exception */ #[When('/^user "([^"]*)" restores the (?:file|folder) "([^"]*)" from the trash of the space "([^"]*)" to "([^"]*)"$/')] + #[When('/^user "([^"]*)" tries to restore the (?:file|folder) "([^"]*)" from the trash of the space "([^"]*)" to "([^"]*)"$/')] public function userRestoresSpaceObjectsFromTrashRequest( string $user, string $object, @@ -3271,9 +3305,10 @@ class SpacesContext implements Context { string $destination ): void { $space = $this->getSpaceByName($user, $spaceName); + $spaceOwner = $this->getSpaceCreator($spaceName); // find object in trash - $objectsInTrash = $this->getObjectsInTrashbin($user, $spaceName); + $objectsInTrash = $this->getObjectsInTrashbin($spaceOwner, $spaceName); $pathToDeletedObject = ""; foreach ($objectsInTrash as $objectInTrash) { if ($objectInTrash["name"] === $object) { diff --git a/tests/acceptance/config/behat.yml b/tests/acceptance/config/behat.yml index 39d04b02cf..7b45b0cf21 100644 --- a/tests/acceptance/config/behat.yml +++ b/tests/acceptance/config/behat.yml @@ -383,6 +383,18 @@ default: - SharingNgContext: - PublicWebDavContext: - OcConfigContext: + + apiSharingNgAdditionalShareRole: + paths: + - "%paths.base%/../features/apiSharingNgAdditionalShareRole" + context: *common_ldap_suite_context + contexts: + - FeatureContext: *common_feature_context_params + - SpacesContext: + - GraphContext: + - SharingNgContext: + - FilesVersionsContext: + - OcConfigContext: apiOcm: paths: diff --git a/tests/acceptance/features/apiSharingNgAdditionalShareRole/editorListGrantsWithVersions.feature b/tests/acceptance/features/apiSharingNgAdditionalShareRole/editorListGrantsWithVersions.feature new file mode 100644 index 0000000000..47ba000c71 --- /dev/null +++ b/tests/acceptance/features/apiSharingNgAdditionalShareRole/editorListGrantsWithVersions.feature @@ -0,0 +1,1047 @@ +@env-config +Feature: an user shares resources + As a user + I want to share resources with Editor List Grants With Versions role + So that users can edit the resource and see the versions + + Background: + Given these users have been created with default attributes: + | username | + | Alice | + | Brian | + And the administrator has enabled the following share permissions roles: + | permissions-role | + | File Editor List Grants With Versions | + | Editor List Grants With Versions | + + + Scenario: sharee checks version of a file shared with FileEditorListGrantsWithVersions role + Given user "Alice" has uploaded file with content "to share" to "textfile.txt" + And we save it into "FILEID" + When user "Alice" sends the following resource share invitation using the Graph API: + | resource | textfile.txt | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | File Editor List Grants With Versions | + 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", + "maxItems": 1, + "minItems": 1, + "items": { + "type": "object", + "required": ["createdDateTime", "id", "roles", "grantedToV2", "invitation"], + "properties": { + "createdDateTime": { "format": "date-time" }, + "id": { "pattern": "^%permissions_id_pattern%$" }, + "roles": { + "type": "array", + "maxItems": 1, + "minItems": 1, + "items": { "const": "b173329d-cf2e-42f0-a595-ee410645d840" } + }, + "invitation": { + "type": "object", + "required": ["invitedBy"], + "properties": { + "invitedBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id", "@libre.graph.userType"], + "properties": { + "displayName": { "const": "Alice Hansen" }, + "id": { "pattern": "^%user_id_pattern%$" }, + "@libre.graph.userType": { "const": "Member" } + } + } + } + } + } + }, + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["id", "displayName", "@libre.graph.userType"], + "properties": { + "id": { "pattern": "^%user_id_pattern%$" }, + "displayName": { "const": "Brian Murphy" }, + "@libre.graph.userType": { "const": "Member" } + } + } + } + } + } + } + } + } + } + """ + And user "Brian" has uploaded file with content "updated content" to "Shares/textfile.txt" + When user "Brian" gets the number of versions of file "textfile.txt" using file-id "<>" + Then the HTTP status code should be "207" + And the number of versions should be "1" + + + Scenario: sharee checks version of a file inside a folder shared with EditorListGrantsWithVersions role + Given user "Alice" has created folder "folderToShare" + And user "Alice" has uploaded file with content "to share" to "folderToShare/textfile.txt" + And we save it into "FILEID" + When user "Alice" sends the following resource share invitation using the Graph API: + | resource | folderToShare | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | Editor List Grants With Versions | + 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", + "maxItems": 1, + "minItems": 1, + "items": { + "type": "object", + "required": ["createdDateTime", "id", "roles", "grantedToV2", "invitation"], + "properties": { + "createdDateTime": { "format": "date-time" }, + "id": { "pattern": "^%permissions_id_pattern%$" }, + "roles": { + "type": "array", + "maxItems": 1, + "minItems": 1, + "items": { "const": "0911d62b-1e3f-4778-8b1b-903b7e4e8476" } + }, + "invitation": { + "type": "object", + "required": ["invitedBy"], + "properties": { + "invitedBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id", "@libre.graph.userType"], + "properties": { + "displayName": { "const": "Alice Hansen" }, + "id": { "pattern": "^%user_id_pattern%$" }, + "@libre.graph.userType": { "const": "Member" } + } + } + } + } + } + }, + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["id", "displayName", "@libre.graph.userType"], + "properties": { + "id": { "pattern": "^%user_id_pattern%$" }, + "displayName": { "const": "Brian Murphy" }, + "@libre.graph.userType": { "const": "Member" } + } + } + } + } + } + } + } + } + } + """ + And user "Brian" has uploaded file with content "updated content" to "Shares/folderToShare/textfile.txt" + When user "Brian" gets the number of versions of file "textfile.txt" using file-id "<>" + Then the HTTP status code should be "207" + And the number of versions should be "1" + + + Scenario: user lists permissions of a file in personal space after enabling FileEditorListGrantsWithVersions role + Given user "Alice" has uploaded file with content "hello world" to "textfile0.txt" + When user "Alice" gets permissions list for file "textfile0.txt" of the space "Personal" using 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.actions.allowedValues": { + "type": "array", + "minItems": 19, + "maxItems": 19, + "uniqueItems": true, + "items": { "type": "string" } + }, + "@libre.graph.permissions.roles.allowedValues": { + "type": "array", + "minItems": 3, + "maxItems": 3, + "uniqueItems": true, + "items": { + "oneOf": [ + { + "type": "object", + "required": ["@libre.graph.weight", "description", "displayName", "id"], + "properties": { + "@libre.graph.weight": { "const": 10 }, + "description": { "const": "View and download." }, + "displayName": { "const": "Can view" }, + "id": { "const": "b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5" } + } + }, + { + "type": "object", + "required": ["@libre.graph.weight", "description", "displayName", "id"], + "properties": { + "@libre.graph.weight": { "const": 100 }, + "description": { "const": "View, download and edit." }, + "displayName": { "const": "Can edit" }, + "id": { "const": "2d00ce52-1fc2-4dbc-8b95-a73b73395f5a" } + } + }, + { + "type": "object", + "required": ["@libre.graph.weight", "description", "displayName", "id"], + "properties": { + "@libre.graph.weight": { "const": 111 }, + "description": { "const": "View, download, upload, edit, show all versions and all invited people." }, + "displayName": { "const": "Can edit" }, + "id": { "const": "b173329d-cf2e-42f0-a595-ee410645d840" } + } + } + ] + } + } + } + } + """ + + + Scenario: user lists permissions of a file in project space after enabling FileEditorListGrantsWithVersions role + Given using spaces DAV path + And the administrator has assigned the role "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 + And user "Alice" has uploaded a file inside space "new-space" with content "hello world" to "textfile0.txt" + When user "Alice" gets permissions list for file "textfile0.txt" of the space "new-space" using 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.actions.allowedValues": { + "type": "array", + "minItems": 19, + "maxItems": 19, + "uniqueItems": true, + "items": { "type": "string" } + }, + "@libre.graph.permissions.roles.allowedValues": { + "type": "array", + "minItems": 3, + "maxItems": 3, + "uniqueItems": true, + "items": { + "oneOf": [ + { + "type": "object", + "required": ["@libre.graph.weight", "description", "displayName", "id"], + "properties": { + "@libre.graph.weight": { "const": 10 }, + "description": { "const": "View and download." }, + "displayName": { "const": "Can view" }, + "id": { "const": "b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5" } + } + }, + { + "type": "object", + "required": ["@libre.graph.weight", "description", "displayName", "id"], + "properties": { + "@libre.graph.weight": { "const": 100 }, + "description": { "const": "View, download and edit." }, + "displayName": { "const": "Can edit" }, + "id": { "const": "2d00ce52-1fc2-4dbc-8b95-a73b73395f5a" } + } + }, + { + "type": "object", + "required": ["@libre.graph.weight", "description", "displayName", "id"], + "properties": { + "@libre.graph.weight": { "const": 111 }, + "description": { "const": "View, download, upload, edit, show all versions and all invited people." }, + "displayName": { "const": "Can edit" }, + "id": { "const": "b173329d-cf2e-42f0-a595-ee410645d840" } + } + } + ] + } + } + } + } + """ + + + Scenario: user lists permissions of a folder in personal space after enabling EditorListGrantsWithVersions role + Given user "Alice" has created folder "folderToShare" + When user "Alice" gets permissions list for folder "folderToShare" of the space "Personal" using 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.actions.allowedValues": { + "type": "array", + "minItems": 19, + "maxItems": 19, + "uniqueItems": true, + "items": { "type": "string" } + }, + "@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": { + "@libre.graph.weight": { "const": 10 }, + "description": { "const": "View and download." }, + "displayName": { "const": "Can view" }, + "id": { "const": "b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5" } + } + }, + { + "type": "object", + "required": ["@libre.graph.weight", "description", "displayName", "id"], + "properties": { + "@libre.graph.weight": { "const": 50 }, + "description": { "const": "View, download and upload." }, + "displayName": { "const": "Can upload" }, + "id": { "const": "1c996275-f1c9-4e71-abdf-a42f6495e960" } + } + }, + { + "type": "object", + "required": ["@libre.graph.weight", "description", "displayName", "id"], + "properties": { + "@libre.graph.weight": { "const": 60 }, + "description": { "const": "View, download, upload, edit, add and delete." }, + "displayName": { "const": "Can edit" }, + "id": { "const": "fb6c3e19-e378-47e5-b277-9732f9de6e21" } + } + }, + { + "type": "object", + "required": ["@libre.graph.weight", "description", "displayName", "id"], + "properties": { + "@libre.graph.weight": { "const": 72 }, + "description": { "const": "View, download, upload, edit, delete, show all versions and all invited people." }, + "displayName": { "const": "Can edit" }, + "id": { "const": "0911d62b-1e3f-4778-8b1b-903b7e4e8476" } + } + } + ] + } + } + } + } + """ + + + Scenario: user lists permissions of a folder in project space after enabling EditorListGrantsWithVersions role + Given using spaces DAV path + And the administrator has assigned the role "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 + And user "Alice" has created a folder "folder" in space "new-space" + When user "Alice" gets permissions list for folder "folder" of the space "new-space" using 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.actions.allowedValues": { + "type": "array", + "minItems": 19, + "maxItems": 19, + "uniqueItems": true, + "items": { "type": "string" } + }, + "@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": { + "@libre.graph.weight": { "const": 10 }, + "description": { "const": "View and download." }, + "displayName": { "const": "Can view" }, + "id": { "const": "b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5" } + } + }, + { + "type": "object", + "required": ["@libre.graph.weight", "description", "displayName", "id"], + "properties": { + "@libre.graph.weight": { "const": 50 }, + "description": { "const": "View, download and upload." }, + "displayName": { "const": "Can upload" }, + "id": { "const": "1c996275-f1c9-4e71-abdf-a42f6495e960" } + } + }, + { + "type": "object", + "required": ["@libre.graph.weight", "description", "displayName", "id"], + "properties": { + "@libre.graph.weight": { "const": 60 }, + "description": { "const": "View, download, upload, edit, add and delete." }, + "displayName": { "const": "Can edit" }, + "id": { "const": "fb6c3e19-e378-47e5-b277-9732f9de6e21" } + } + }, + { + "type": "object", + "required": ["@libre.graph.weight", "description", "displayName", "id"], + "properties": { + "@libre.graph.weight": { "const": 72 }, + "description": { "const": "View, download, upload, edit, delete, show all versions and all invited people." }, + "displayName": { "const": "Can edit" }, + "id": { "const": "0911d62b-1e3f-4778-8b1b-903b7e4e8476" } + } + } + ] + } + } + } + } + """ + + + Scenario: sharee lists the received shares (Personal Space) + Given user "Alice" has uploaded file with content "hello world" to "textfile.txt" + And user "Alice" has created folder "folder" + And user "Alice" has sent the following resource share invitation: + | resource | textfile.txt | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | File Editor List Grants With Versions | + And user "Alice" has sent the following resource share invitation: + | resource | folder | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | Editor List Grants With Versions | + When user "Brian" lists the shares shared with him using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should contain resource "textfile.txt" with the following data: + """ + { + "type": "object", + "required": ["@UI.Hidden","@client.synchronize","createdBy","eTag","file","id", + "lastModifiedDateTime","name","parentReference","remoteItem","size"], + "properties": { + "@UI.Hidden": { "const": false }, + "@client.synchronize": { "const": true }, + "eTag": { "pattern": "%etag_pattern%" }, + "file": { + "type": "object", + "required": ["mimeType"], + "properties": { "mimeType": { "const": "text/plain" } } + }, + "id": { "pattern": "^%share_id_pattern%$" }, + "name": { "const": "textfile.txt" }, + "remoteItem": { + "type": "object", + "required": ["createdBy","eTag","file","id","lastModifiedDateTime","name", + "parentReference","permissions","size"], + "properties": { + "createdBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["id", "displayName"], + "properties": { + "id": { "pattern": "^%user_id_pattern%$" }, + "displayName": { "const": "Alice Hansen" } + } + } + } + }, + "eTag": { "pattern": "%etag_pattern%" }, + "file": { + "type": "object", + "required": ["mimeType"], + "properties": { + "mimeType": { "const": "text/plain" } + } + }, + "id": { "pattern": "^%file_id_pattern%$" }, + "name": { "const": "textfile.txt" }, + "parentReference": { + "type": "object", + "required": ["driveId", "driveType"], + "properties": { + "driveId": { "pattern": "^%file_id_pattern%$" }, + "driveType": { "const": "personal" } + } + }, + "permissions": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": { + "type": "object", + "required": ["grantedToV2", "id", "invitation", "roles"], + "properties": { + "id": { "pattern": "^%permissions_id_pattern%$" }, + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id"], + "properties": { + "displayName": { "const": "Brian Murphy" }, + "id": { "pattern": "^%user_id_pattern%$" } + } + } + } + }, + "invitation": { + "type": "object", + "properties": { + "invitedBy": { + "type": "object", + "properties": { + "user": { + "type": "object", + "properties": { + "displayName": { "const": "Alice Hansen" }, + "id": { "pattern": "^%user_id_pattern%$" } + }, + "required": ["displayName", "id"] + } + }, + "required": ["user"] + } + }, + "required": ["invitedBy"] + }, + "roles": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": { "const": "b173329d-cf2e-42f0-a595-ee410645d840" } + } + } + } + } + } + }, + "size": { "const": 11 } + } + } + """ + And the JSON data of the response should contain resource "folder" with the following data: + """ + { + "type": "object", + "required": ["@UI.Hidden","@client.synchronize","createdBy","eTag","folder","id", + "lastModifiedDateTime","name","parentReference","remoteItem"], + "properties": { + "@UI.Hidden": { "const": false }, + "@client.synchronize": { "const": true }, + "eTag": { "pattern": "%etag_pattern%" }, + "id": { "pattern": "^%share_id_pattern%$" }, + "name": { "const": "folder" }, + "remoteItem": { + "type": "object", + "required": ["createdBy","eTag","folder","id","lastModifiedDateTime", + "name","parentReference","permissions"], + "properties": { + "createdBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["id", "displayName"], + "properties": { + "id": { "pattern": "^%user_id_pattern%$" }, + "displayName": { "const": "Alice Hansen" } + } + } + } + }, + "eTag": { "pattern": "%etag_pattern%" }, + "file": { + "type": "object", + "required": ["mimeType"], + "properties": { + "mimeType": { "const": "text/plain" } + } + }, + "id": { "pattern": "^%file_id_pattern%$" }, + "name": { "const": "folder" }, + "parentReference": { + "type": "object", + "required": ["driveId", "driveType"], + "properties": { + "driveId": { "pattern": "^%file_id_pattern%$" }, + "driveType": { "const": "personal" } + } + }, + "permissions": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": { + "type": "object", + "required": ["grantedToV2", "id", "invitation", "roles"], + "properties": { + "id": { "pattern": "^%permissions_id_pattern%$" }, + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "properties": { + "displayName": { "const": "Brian Murphy" }, + "id": { "pattern": "^%user_id_pattern%$" } + }, + "required": ["displayName", "id"] + } + } + }, + "invitation": { + "type": "object", + "properties": { + "invitedBy": { + "type": "object", + "properties": { + "user": { + "type": "object", + "properties": { + "displayName": { "const": "Alice Hansen" }, + "id": { "pattern": "^%user_id_pattern%$" } + }, + "required": ["displayName", "id"] + } + }, + "required": ["user"] + } + }, + "required": ["invitedBy"] + }, + "roles": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": { "const": "0911d62b-1e3f-4778-8b1b-903b7e4e8476" } + } + } + } + } + } + } + } + } + """ + + + Scenario: sharee lists the received shares (Project Space) + Given using spaces DAV path + 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 + And user "Alice" has uploaded a file inside space "new-space" with content "some content" to "testfile.txt" + And user "Alice" has created a folder "folder" in space "new-space" + And user "Alice" has sent the following resource share invitation: + | resource | testfile.txt | + | space | new-space | + | sharee | Brian | + | shareType | user | + | permissionsRole | File Editor List Grants With Versions | + And user "Alice" has sent the following resource share invitation: + | resource | folder | + | space | new-space | + | sharee | Brian | + | shareType | user | + | permissionsRole | Editor List Grants With Versions | + When user "Brian" lists the shares shared with him using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should contain resource "testfile.txt" with the following data: + """ + { + "type": "object", + "required": ["@UI.Hidden","@client.synchronize","eTag","file","id", + "lastModifiedDateTime","name","parentReference","remoteItem","size"], + "properties": { + "@UI.Hidden": { "const": false }, + "@client.synchronize": { "const": true }, + "eTag": { "pattern": "%etag_pattern%" }, + "id": { "pattern": "^%share_id_pattern%$" }, + "name": { "const": "testfile.txt" }, + "remoteItem": { + "type": "object", + "required": ["eTag","file","id","lastModifiedDateTime", + "name","parentReference","permissions","size"], + "properties": { + "eTag": { "pattern": "%etag_pattern%" }, + "file": { + "type": "object", + "required": ["mimeType"], + "properties": { + "mimeType": { "const": "text/plain" } + } + }, + "id": { "pattern": "^%file_id_pattern%$" }, + "name": { "const": "testfile.txt" }, + "parentReference": { + "type": "object", + "required": ["driveId", "driveType"], + "properties": { + "driveId": { "pattern": "^%file_id_pattern%$" }, + "driveType": { "const": "project" } + } + }, + "permissions": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": { + "type": "object", + "required": ["grantedToV2", "id", "invitation", "roles"], + "properties": { + "id": { "pattern": "^%permissions_id_pattern%$" }, + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id"], + "properties": { + "displayName": { "const": "Brian Murphy" }, + "id": { "pattern": "^%user_id_pattern%$" } + } + } + } + }, + "invitation": { + "type": "object", + "properties": { + "invitedBy": { + "type": "object", + "properties": { + "user": { + "type": "object", + "properties": { + "displayName": { "const": "Alice Hansen" }, + "id": { "pattern": "^%user_id_pattern%$" } + }, + "required": ["displayName", "id"] + } + }, + "required": ["user"] + } + }, + "required": ["invitedBy"] + }, + "roles": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": { "const": "b173329d-cf2e-42f0-a595-ee410645d840" } + } + } + } + } + } + }, + "size": { "const": 12 } + } + } + """ + And the JSON data of the response should contain resource "folder" with the following data: + """ + { + "type": "object", + "required": ["@UI.Hidden","@client.synchronize","eTag","folder","id", + "lastModifiedDateTime","name","parentReference","remoteItem"], + "properties": { + "@UI.Hidden": { "const": false }, + "@client.synchronize": { "const": true }, + "eTag": { "pattern": "%etag_pattern%" }, + "id": { "pattern": "^%share_id_pattern%$" }, + "name": { "const": "folder" }, + "remoteItem": { + "type": "object", + "required": ["eTag","folder","id","lastModifiedDateTime", + "name","parentReference","permissions"], + "properties": { + "eTag": { "pattern": "%etag_pattern%" }, + "file": { + "type": "object", + "required": ["mimeType"], + "properties": { + "mimeType": { "const": "text/plain" } + } + }, + "id": { "pattern": "^%file_id_pattern%$" }, + "name": { "const": "folder" }, + "parentReference": { + "type": "object", + "required": ["driveId", "driveType"], + "properties": { + "driveId": { "pattern": "^%file_id_pattern%$" }, + "driveType": { "const": "project" } + } + }, + "permissions": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": { + "type": "object", + "required": ["grantedToV2", "id", "invitation", "roles"], + "properties": { + "id": { "pattern": "^%permissions_id_pattern%$" }, + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "properties": { + "displayName": { "const": "Brian Murphy" }, + "id": { "pattern": "^%user_id_pattern%$" } + }, + "required": ["displayName", "id"] + } + } + }, + "invitation": { + "type": "object", + "properties": { + "invitedBy": { + "type": "object", + "properties": { + "user": { + "type": "object", + "properties": { + "displayName": { "const": "Alice Hansen" }, + "id": { "pattern": "^%user_id_pattern%$" } + }, + "required": ["displayName", "id"] + } + }, + "required": ["user"] + } + }, + "required": ["invitedBy"] + }, + "roles": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": { "const": "0911d62b-1e3f-4778-8b1b-903b7e4e8476" } + } + } + } + } + } + } + } + } + """ + + + Scenario: sharee checks file versions after updating the permission roles to with-versions roles (Personal Space) + Given using spaces DAV path + And user "Alice" has created folder "folderToShare" + And user "Alice" has uploaded file with content "to share" to "folderToShare/lorem.txt" + And user "Alice" has uploaded file with content "to share" to "textfile.txt" + And user "Alice" has sent the following resource share invitation: + | resource | textfile.txt | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | File Editor | + And user "Brian" has uploaded file with content "updated content" to "Shares/textfile.txt" + And user "Alice" has updated the last resource share with the following properties: + | permissionsRole | File Editor List Grants With Versions | + | space | Personal | + | resource | textfile.txt | + And user "Alice" has sent the following resource share invitation: + | resource | folderToShare | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | Editor | + And user "Brian" has uploaded file with content "updated content" to "Shares/folderToShare/lorem.txt" + And user "Alice" has updated the last resource share with the following properties: + | permissionsRole | Editor List Grants With Versions | + | space | Personal | + | resource | folderToShare | + When user "Brian" gets the number of versions of file "Shares/textfile.txt" + Then the HTTP status code should be "207" + And the number of versions should be "1" + When user "Brian" gets the number of versions of file "Shares/folderToShare/lorem.txt" + Then the HTTP status code should be "207" + And the number of versions should be "1" + + + Scenario: sharee tries to check file versions after updating the with-versions roles to other roles (Personal Space) + Given using spaces DAV path + And user "Alice" has created folder "folderToShare" + And user "Alice" has uploaded file with content "to share" to "folderToShare/lorem.txt" + And user "Alice" has uploaded file with content "to share" to "textfile.txt" + And user "Alice" has sent the following resource share invitation: + | resource | textfile.txt | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | File Editor List Grants With Versions | + And user "Brian" has uploaded file with content "updated content" to "Shares/textfile.txt" + And user "Alice" has updated the last resource share with the following properties: + | permissionsRole | File Editor | + | space | Personal | + | resource | textfile.txt | + And user "Alice" has sent the following resource share invitation: + | resource | folderToShare | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | Editor List Grants With Versions | + And user "Brian" has uploaded file with content "updated content" to "Shares/folderToShare/lorem.txt" + And user "Alice" has updated the last resource share with the following properties: + | permissionsRole | Editor | + | space | Personal | + | resource | folderToShare | + When user "Brian" tries to get versions of file "textfile.txt" from "Alice" + Then the HTTP status code should be "403" + When user "Brian" tries to get versions of file "folderToShare/lorem.txt" from "Alice" + Then the HTTP status code should be "403" + + + Scenario: sharee checks file versions after updating the permission role to with-versions roles (Project Space) + Given using spaces DAV path + 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 + And user "Alice" has created a folder "folderToShare" in space "new-space" + And user "Alice" has uploaded a file inside space "new-space" with content "to share" to "folderToShare/lorem.txt" + And user "Alice" has uploaded a file inside space "new-space" with content "to share" to "textfile.txt" + And user "Alice" has sent the following resource share invitation: + | resource | textfile.txt | + | space | new-space | + | sharee | Brian | + | shareType | user | + | permissionsRole | File Editor | + And user "Brian" has uploaded file with content "updated content" to "Shares/textfile.txt" + And user "Alice" has updated the last resource share with the following properties: + | permissionsRole | File Editor List Grants With Versions | + | space | new-space | + | resource | textfile.txt | + And user "Alice" has sent the following resource share invitation: + | resource | folderToShare | + | space | new-space | + | sharee | Brian | + | shareType | user | + | permissionsRole | Editor | + And user "Brian" has uploaded file with content "updated content" to "Shares/folderToShare/lorem.txt" + And user "Alice" has updated the last resource share with the following properties: + | permissionsRole | Editor List Grants With Versions | + | space | new-space | + | resource | folderToShare | + When user "Brian" gets the number of versions of file "Shares/textfile.txt" + Then the HTTP status code should be "207" + And the number of versions should be "1" + When user "Brian" gets the number of versions of file "Shares/folderToShare/lorem.txt" + Then the HTTP status code should be "207" + And the number of versions should be "1" + + + Scenario: sharee tries to check file versions after updating the with-versions roles to other roles (Project Space) + Given using spaces DAV path + 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 + And user "Alice" has created a folder "folderToShare" in space "new-space" + And user "Alice" has uploaded a file inside space "new-space" with content "to share" to "folderToShare/lorem.txt" + And user "Alice" has uploaded a file inside space "new-space" with content "to share" to "textfile.txt" + And we save it into "FILEID" + And user "Alice" has sent the following resource share invitation: + | resource | textfile.txt | + | space | new-space | + | sharee | Brian | + | shareType | user | + | permissionsRole | File Editor List Grants With Versions | + And user "Brian" has uploaded file with content "updated content" to "Shares/textfile.txt" + And user "Alice" has updated the last resource share with the following properties: + | permissionsRole | File Editor | + | space | new-space | + | resource | textfile.txt | + And user "Alice" has sent the following resource share invitation: + | resource | folderToShare | + | space | new-space | + | sharee | Brian | + | shareType | user | + | permissionsRole | Editor List Grants With Versions | + And user "Brian" has uploaded file with content "updated content" to "Shares/folderToShare/lorem.txt" + And user "Alice" has updated the last resource share with the following properties: + | permissionsRole | Editor | + | space | new-space | + | resource | folderToShare | + When user "Brian" tries to get versions of the file "textfile.txt" from the space "Shares" using the WebDAV API + Then the HTTP status code should be "403" + When user "Brian" tries to get versions of the file "folderToShare/lorem.txt" from the space "Shares" using the WebDAV API + Then the HTTP status code should be "403" \ No newline at end of file diff --git a/tests/acceptance/features/apiSharingNgAdditionalShareRole/listGrantsShareRole.feature b/tests/acceptance/features/apiSharingNgAdditionalShareRole/listGrantsShareRole.feature new file mode 100644 index 0000000000..0b9e790ffa --- /dev/null +++ b/tests/acceptance/features/apiSharingNgAdditionalShareRole/listGrantsShareRole.feature @@ -0,0 +1,1175 @@ +@env-config +Feature: ListGrants role + As a user + I want to share resources with listGrants role + So that sharee can view activities and grants list of shared resources + + Background: + Given these users have been created with default attributes: + | username | + | Alice | + | Brian | + And the administrator has enabled the following share permissions roles: + | permissions-role | + | Viewer List Grants | + | File Editor List Grants | + | Editor List Grants | + + + Scenario: user shares personal resources with ListGrants role + Given user "Alice" has created folder "FolderToShare" + And user "Alice" has uploaded file with content "to share" to "textfile1.txt" + When user "Alice" sends the following resource share invitation using the Graph API: + | resource | textfile1.txt | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | File Editor List Grants | + 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", + "maxItems": 1, + "minItems": 1, + "items": { + "type": "object", + "required": ["createdDateTime","id","roles","grantedToV2"], + "properties": { + "createdDateTime": { "format": "date-time" }, + "id": {"pattern": "^%permissions_id_pattern%$"}, + "roles": { + "type": "array", + "maxItems": 1, + "minItems": 1, + "items": {"pattern": "^%role_id_pattern%$"} + }, + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["id","displayName"], + "properties": { + "id": {"pattern": "^%user_id_pattern%$"}, + "displayName": {"const": "Brian Murphy"} + } + } + } + } + } + } + } + } + } + """ + And for user "Brian" file "textfile1.txt" should have the following shares: + | sharee | shareType | permissionsRole | + | Brian | user | File Editor List Grants | + And for user "Brian" file "textfile1.txt" of the space "Shares" should have the following activities: + | {user} added {resource} to {folder} | + | {user} shared {resource} with {sharee} | + When user "Alice" sends the following resource share invitation using the Graph API: + | resource | FolderToShare | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | Editor List Grants | + 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", + "maxItems": 1, + "minItems": 1, + "items": { + "type": "object", + "required": ["createdDateTime","id","roles","grantedToV2"], + "properties": { + "createdDateTime": { "format": "date-time" }, + "id": {"pattern": "^%permissions_id_pattern%$"}, + "roles": { + "type": "array", + "maxItems": 1, + "minItems": 1, + "items": {"pattern": "^%role_id_pattern%$"} + }, + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["id","displayName"], + "properties": { + "id": {"pattern": "^%user_id_pattern%$"}, + "displayName": {"const": "Brian Murphy"} + } + } + } + } + } + } + } + } + } + """ + And for user "Brian" folder "FolderToShare" should have the following shares: + | sharee | shareType | permissionsRole | + | Brian | user | Editor List Grants | + And for user "Brian" folder "FolderToShare" of the space "Shares" should have the following activities: + | {user} added {resource} to {folder} | + | {user} shared {resource} with {sharee} | + + + Scenario: user shares project resources with ListGrants role + Given the administrator has assigned the role "Space Admin" to user "Alice" using the Graph API + And using spaces DAV path + And user "Alice" has created a space "NewSpace" with the default quota using the Graph API + And user "Alice" has created a folder "FolderToShare" in space "NewSpace" + And user "Alice" has uploaded a file inside space "NewSpace" with content "share space items" to "textfile1.txt" + When user "Alice" sends the following resource share invitation using the Graph API: + | resource | textfile1.txt | + | space | NewSpace | + | sharee | Brian | + | shareType | user | + | permissionsRole | File Editor List Grants | + 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", + "maxItems": 1, + "minItems": 1, + "items": { + "type": "object", + "required": ["createdDateTime","id","roles","grantedToV2"], + "properties": { + "createdDateTime": { "format": "date-time" }, + "id": {"pattern": "^%permissions_id_pattern%$"}, + "roles": { + "type": "array", + "maxItems": 1, + "minItems": 1, + "items": {"pattern": "^%role_id_pattern%$"} + }, + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["id","displayName"], + "properties": { + "id": {"pattern": "^%user_id_pattern%$"}, + "displayName": {"const": "Brian Murphy"} + } + } + } + } + } + } + } + } + } + """ + And for user "Brian" file "textfile1.txt" should have the following shares: + | sharee | shareType | permissionsRole | + | Brian | user | File Editor List Grants | + And for user "Brian" file "textfile1.txt" of the space "Shares" should have the following activities: + | {user} added {resource} to {folder} | + | {user} shared {resource} with {sharee} | + When user "Alice" sends the following resource share invitation using the Graph API: + | resource | FolderToShare | + | space | NewSpace | + | sharee | Brian | + | shareType | user | + | permissionsRole | Editor List Grants | + 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", + "maxItems": 1, + "minItems": 1, + "items": { + "type": "object", + "required": ["createdDateTime","id","roles","grantedToV2"], + "properties": { + "createdDateTime": { "format": "date-time" }, + "id": {"pattern": "^%permissions_id_pattern%$"}, + "roles": { + "type": "array", + "maxItems": 1, + "minItems": 1, + "items": {"pattern": "^%role_id_pattern%$"} + }, + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["id","displayName"], + "properties": { + "id": {"pattern": "^%user_id_pattern%$"}, + "displayName": {"const": "Brian Murphy"} + } + } + } + } + } + } + } + } + } + """ + And for user "Brian" folder "FolderToShare" should have the following shares: + | sharee | shareType | permissionsRole | + | Brian | user | Editor List Grants | + And for user "Brian" folder "FolderToShare" of the space "Shares" should have the following activities: + | {user} added {resource} to {folder} | + | {user} shared {resource} with {sharee} | + + + Scenario Outline: sharer updates shared file roles to ListGrants roles (Personal space) + Given the administrator has enabled the permissions role "" + And user "Alice" has uploaded file with content "to share" to "textfile1.txt" + And user "Alice" has sent the following resource share invitation: + | resource | textfile1.txt | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | | + When user "Alice" updates the last resource share with the following properties using the Graph API: + | permissionsRole | | + | space | Personal | + | resource | textfile1.txt | + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": ["grantedToV2","id","roles"], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties":{ + "user": { + "type": "object", + "required": ["displayName","id"], + "properties": { + "displayName": {"const": "Brian Murphy"}, + "id": {"pattern": "^%user_id_pattern%$"} + } + } + } + }, + "id": {"pattern": "^%permissions_id_pattern%$"}, + "roles": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": {"pattern": "^%role_id_pattern%$"} + } + } + } + """ + And for user "Brian" file "textfile1.txt" should have the following shares: + | sharee | shareType | permissionsRole | + | Brian | user | | + And for user "Brian" file "textfile1.txt" of the space "Shares" should have the following activities: + | {user} added {resource} to {folder} | + | {user} shared {resource} with {sharee} | + Examples: + | permissions-role | new-permissions-role | + | Viewer | File Editor List Grants | + | File Editor | Viewer List Grants | + + + Scenario Outline: sharer updates shared folder roles to ListGrants roles (Personal space) + Given the administrator has enabled the permissions role "" + And user "Alice" has created folder "FolderToShare" + And user "Alice" has sent the following resource share invitation: + | resource | FolderToShare | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | | + When user "Alice" updates the last resource share with the following properties using the Graph API: + | permissionsRole | | + | space | Personal | + | resource | FolderToShare | + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": ["grantedToV2","id","roles"], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties":{ + "user": { + "type": "object", + "required": ["displayName","id"], + "properties": { + "displayName": {"const": "Brian Murphy"}, + "id": {"pattern": "^%user_id_pattern%$"} + } + } + } + }, + "id": {"pattern": "^%permissions_id_pattern%$"}, + "roles": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": {"pattern": "^%role_id_pattern%$"} + } + } + } + """ + And for user "Brian" folder "FolderToShare" should have the following shares: + | sharee | shareType | permissionsRole | + | Brian | user | | + And for user "Brian" folder "FolderToShare" of the space "Shares" should have the following activities: + | {user} added {resource} to {folder} | + | {user} shared {resource} with {sharee} | + Examples: + | permissions-role | new-permissions-role | + | Viewer | Viewer List Grants | + | Editor | Editor List Grants | + + + Scenario Outline: sharer updates shared file roles to ListGrants roles (Project space) + Given the administrator has enabled the permissions role "" + And the administrator has assigned the role "Space Admin" to user "Alice" using the Graph API + And using spaces DAV path + And user "Alice" has created a space "NewSpace" with the default quota using the Graph API + And user "Alice" has uploaded a file inside space "NewSpace" with content "share space items" to "textfile1.txt" + And user "Alice" has sent the following resource share invitation: + | resource | textfile1.txt | + | space | NewSpace | + | sharee | Brian | + | shareType | user | + | permissionsRole | | + When user "Alice" updates the last resource share with the following properties using the Graph API: + | permissionsRole | | + | space | NewSpace | + | resource | textfile1.txt | + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": ["grantedToV2","id","roles"], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties":{ + "user": { + "type": "object", + "required": ["displayName","id"], + "properties": { + "displayName": {"const": "Brian Murphy"}, + "id": {"pattern": "^%user_id_pattern%$"} + } + } + } + }, + "id": {"pattern": "^%permissions_id_pattern%$"}, + "roles": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": {"pattern": "^%role_id_pattern%$"} + } + } + } + """ + And for user "Brian" file "textfile1.txt" should have the following shares: + | sharee | shareType | permissionsRole | + | Brian | user | | + And for user "Brian" file "textfile1.txt" of the space "Shares" should have the following activities: + | {user} added {resource} to {folder} | + | {user} shared {resource} with {sharee} | + | {user} updated {field} for the {resource} | + Examples: + | permissions-role | new-permissions-role | + | Viewer | Viewer List Grants | + | File Editor | File Editor List Grants | + + + Scenario Outline: sharer updates shared folder roles to ListGrants roles (Project space) + Given the administrator has enabled the permissions role "" + And the administrator has assigned the role "Space Admin" to user "Alice" using the Graph API + And using spaces DAV path + And user "Alice" has created a space "NewSpace" with the default quota using the Graph API + And user "Alice" has created a folder "FolderToShare" in space "NewSpace" + And user "Alice" has sent the following resource share invitation: + | resource | FolderToShare | + | space | NewSpace | + | sharee | Brian | + | shareType | user | + | permissionsRole | | + When user "Alice" updates the last resource share with the following properties using the Graph API: + | permissionsRole | | + | space | NewSpace | + | resource | FolderToShare | + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": ["grantedToV2","id","roles"], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties":{ + "user": { + "type": "object", + "required": ["displayName","id"], + "properties": { + "displayName": {"const": "Brian Murphy"}, + "id": {"pattern": "^%user_id_pattern%$"} + } + } + } + }, + "id": {"pattern": "^%permissions_id_pattern%$"}, + "roles": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": {"pattern": "^%role_id_pattern%$"} + } + } + } + """ + And for user "Brian" folder "FolderToShare" should have the following shares: + | sharee | shareType | permissionsRole | + | Brian | user | | + And for user "Brian" folder "FolderToShare" of the space "Shares" should have the following activities: + | {user} added {resource} to {folder} | + | {user} shared {resource} with {sharee} | + | {user} updated {field} for the {resource} | + Examples: + | permissions-role | new-permissions-role | + | Editor | Viewer List Grants | + | Viewer | Editor List Grants | + + + Scenario Outline: sharer updates shared file roles from ListGrants roles to other roles (Personal space) + Given the administrator has enabled the permissions role "" + And user "Alice" has uploaded file with content "to share" to "textfile1.txt" + And user "Alice" has sent the following resource share invitation: + | resource | textfile1.txt | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | | + When user "Alice" updates the last resource share with the following properties using the Graph API: + | permissionsRole | | + | space | Personal | + | resource | textfile1.txt | + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": ["grantedToV2","id","roles"], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties":{ + "user": { + "type": "object", + "required": ["displayName","id"], + "properties": { + "displayName": {"const": "Brian Murphy"}, + "id": {"pattern": "^%user_id_pattern%$"} + } + } + } + }, + "id": {"pattern": "^%permissions_id_pattern%$"}, + "roles": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": {"pattern": "^%role_id_pattern%$"} + } + } + } + """ + And for user "Brian" file "textfile1.txt" should have the following shares: + | sharee | shareType | permissionsRole | + | Brian | user | | + And for user "Brian" file "textfile1.txt" of the space "Shares" should not have any activity + Examples: + | permissions-role | new-permissions-role | + | Viewer List Grants | Viewer | + | File Editor List Grants | File Editor | + + + Scenario Outline: sharer updates shared folder roles from ListGrants roles to other roles (Personal space) + Given the administrator has enabled the permissions role "" + And user "Alice" has created folder "FolderToShare" + And user "Alice" has sent the following resource share invitation: + | resource | FolderToShare | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | | + When user "Alice" updates the last resource share with the following properties using the Graph API: + | permissionsRole | | + | space | Personal | + | resource | FolderToShare | + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": ["grantedToV2","id","roles"], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties":{ + "user": { + "type": "object", + "required": ["displayName","id"], + "properties": { + "displayName": {"const": "Brian Murphy"}, + "id": {"pattern": "^%user_id_pattern%$"} + } + } + } + }, + "id": {"pattern": "^%permissions_id_pattern%$"}, + "roles": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": {"pattern": "^%role_id_pattern%$"} + } + } + } + """ + And for user "Brian" folder "FolderToShare" should have the following shares: + | sharee | shareType | permissionsRole | + | Brian | user | | + And for user "Brian" folder "FolderToShare" of the space "Shares" should not have any activity + Examples: + | permissions-role | new-permissions-role | + | Viewer List Grants | Editor | + | Editor List Grants | Viewer | + + + Scenario Outline: sharer updates shared file roles from ListGrants roles to other roles (Project space) + Given the administrator has enabled the permissions role "" + And the administrator has assigned the role "Space Admin" to user "Alice" using the Graph API + And using spaces DAV path + And user "Alice" has created a space "NewSpace" with the default quota using the Graph API + And user "Alice" has uploaded a file inside space "NewSpace" with content "share space items" to "textfile1.txt" + And user "Alice" has sent the following resource share invitation: + | resource | textfile1.txt | + | space | NewSpace | + | sharee | Brian | + | shareType | user | + | permissionsRole | | + When user "Alice" updates the last resource share with the following properties using the Graph API: + | permissionsRole | | + | space | NewSpace | + | resource | textfile1.txt | + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": ["grantedToV2","id","roles"], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties":{ + "user": { + "type": "object", + "required": ["displayName","id"], + "properties": { + "displayName": {"const": "Brian Murphy"}, + "id": {"pattern": "^%user_id_pattern%$"} + } + } + } + }, + "id": {"pattern": "^%permissions_id_pattern%$"}, + "roles": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": {"pattern": "^%role_id_pattern%$"} + } + } + } + """ + And for user "Brian" file "textfile1.txt" should have the following shares: + | sharee | shareType | permissionsRole | + | Brian | user | | + And for user "Brian" file "textfile1.txt" of the space "Shares" should have the following activities: + | {user} added {resource} to {folder} | + | {user} shared {resource} with {sharee} | + | {user} updated {field} for the {resource} | + Examples: + | new-permissions-role | permissions-role | + | Viewer List Grants | File Editor | + | File Editor List Grants | Viewer | + + + Scenario Outline: sharer updates shared folder roles to ListGrants roles to other roles (Project space) + Given the administrator has enabled the permissions role "" + And the administrator has assigned the role "Space Admin" to user "Alice" using the Graph API + And using spaces DAV path + And user "Alice" has created a space "NewSpace" with the default quota using the Graph API + And user "Alice" has created a folder "FolderToShare" in space "NewSpace" + And user "Alice" has sent the following resource share invitation: + | resource | FolderToShare | + | space | NewSpace | + | sharee | Brian | + | shareType | user | + | permissionsRole | | + When user "Alice" updates the last resource share with the following properties using the Graph API: + | permissionsRole | | + | space | NewSpace | + | resource | FolderToShare | + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": ["grantedToV2","id","roles"], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties":{ + "user": { + "type": "object", + "required": ["displayName","id"], + "properties": { + "displayName": {"const": "Brian Murphy"}, + "id": {"pattern": "^%user_id_pattern%$"} + } + } + } + }, + "id": {"pattern": "^%permissions_id_pattern%$"}, + "roles": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": {"pattern": "^%role_id_pattern%$"} + } + } + } + """ + And for user "Brian" folder "FolderToShare" should have the following shares: + | sharee | shareType | permissionsRole | + | Brian | user | | + And for user "Brian" folder "FolderToShare" of the space "Shares" should have the following activities: + | {user} added {resource} to {folder} | + | {user} shared {resource} with {sharee} | + | {user} updated {field} for the {resource} | + Examples: + | new-permissions-role | permissions-role | + | Viewer List Grants | Viewer | + | Editor List Grants | Uploader | + + + Scenario: sharer lists shared-by-me (Personal space) + Given the administrator has assigned the role "Admin" to user "Alice" using the Graph API + And user "Alice" has created folder "folder" + And user "Alice" has uploaded file with content "to share" to "textfile.txt" + And user "Alice" has created a group "grp1" using the Graph API + And user "Brian" has been added to group "grp1" + And user "Alice" has sent the following resource share invitation: + | resource | folder | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | Editor List Grants | + And user "Alice" has sent the following resource share invitation: + | resource | textfile.txt | + | space | Personal | + | sharee | grp1 | + | shareType | group | + | permissionsRole | File Editor List Grants | + When user "Alice" lists the shares shared by her using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should contain resource "folder" with the following data: + """ + { + "type": "object", + "required": ["parentReference","permissions","name"], + "properties": { + "parentReference": { + "type": "object", + "required": ["driveId","driveType","path","name","id"], + "properties": { + "driveId": {"pattern": "^%space_id_pattern%$"}, + "driveType": {"const": "personal"}, + "path": {"const": "/"}, + "name": {"const": "/"}, + "id": {"pattern": "^%file_id_pattern%$"} + } + }, + "permissions": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": { + "type": "object", + "required": ["grantedToV2","id","roles"], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName","id"], + "properties": { + "id": {"pattern": "^%user_id_pattern%$"}, + "displayName": {"const": "Brian Murphy"} + } + } + } + }, + "id": {"pattern": "^%permissions_id_pattern%$"}, + "roles": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": {"pattern": "^%role_id_pattern%$"} + } + } + } + }, + "name": {"const": "folder"} + } + } + """ + And the JSON data of the response should contain resource "textfile.txt" with the following data: + """ + { + "type": "object", + "required": ["parentReference","permissions","name","size"], + "properties": { + "name": {"const": "textfile.txt"}, + "size": {"const": 8}, + "parentReference": { + "type": "object", + "required": ["driveId","driveType","path","name","id"], + "properties": { + "driveId": {"pattern": "^%space_id_pattern%$"}, + "driveType": {"const": "personal"}, + "path": {"const": "/"}, + "name": {"const": "/"}, + "id": {"pattern": "^%file_id_pattern%$"} + } + }, + "permissions": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": { + "type": "object", + "required": ["createdDateTime","grantedToV2","id","roles","invitation"], + "properties": { + "createdDateTime": { "format": "date-time" }, + "id": {"pattern": "^%permissions_id_pattern%$"}, + "roles": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": {"pattern": "^%role_id_pattern%$"} + }, + "invitation": { + "type": "object", + "required": ["invitedBy"], + "properties": { + "invitedBy": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["displayName", "id", "@libre.graph.userType"], + "properties": { + "displayName": { "const": "Alice Hansen" }, + "id": { "pattern": "^%user_id_pattern%$" }, + "@libre.graph.userType": { "const": "Member" } + } + } + } + } + } + }, + "grantedToV2": { + "type": "object", + "required": ["group"], + "properties": { + "group": { + "type": "object", + "required": ["displayName","id"], + "properties": { + "id": {"pattern": "^%group_id_pattern%$"}, + "displayName": {"const": "grp1"} + } + } + } + } + } + } + } + } + } + """ + + + Scenario: sharee list shared-with-me (Personal space) + Given the administrator has assigned the role "Admin" to user "Alice" using the Graph API + And user "Alice" has created folder "folder" + And user "Alice" has created a group "grp1" using the Graph API + And user "Brian" has been added to group "grp1" + And user "Alice" has uploaded file with content "to share" to "textfile.txt" + And user "Alice" has sent the following resource share invitation: + | resource | textfile.txt | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | Viewer List Grants | + And user "Alice" has sent the following resource share invitation: + | resource | folder | + | space | Personal | + | sharee | grp1 | + | shareType | group | + | permissionsRole | Editor List Grants | + When user "Brian" lists the shares shared with him using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should contain resource "textfile.txt" with the following data: + """ + { + "type": "object", + "required": ["@UI.Hidden","@client.synchronize","createdBy","eTag","file", + "id","lastModifiedDateTime","name","parentReference","remoteItem","size"], + "properties": { + "lastModifiedDateTime": { "format": "date-time" }, + "eTag": {"pattern": "%etag_pattern%"}, + "id": {"pattern": "^%share_id_pattern%$"}, + "name": {"const": "textfile.txt"}, + "remoteItem": { + "type": "object", + "required": ["createdBy","eTag","file","id","lastModifiedDateTime","name","parentReference","permissions","size"], + "properties": { + "eTag": {"pattern": "%etag_pattern%"}, + "id": {"pattern": "^%file_id_pattern%$"}, + "name": {"const": "textfile.txt"}, + "permissions": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "uniqueItems": true, + "items": { + "type": "object", + "required": ["grantedToV2","id","invitation","roles"], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["user"], + "properties":{ + "user": { + "type": "object", + "required": ["displayName","id"], + "properties": { + "displayName": {"const": "Brian Murphy"}, + "id": {"pattern": "^%user_id_pattern%$"} + } + } + } + }, + "id": {"pattern": "^%permissions_id_pattern%$"}, + "invitation": { + "type": "object", + "required": ["invitedBy"], + "properties": { + "user":{ + "type": "object", + "required": ["displayName","id"], + "properties": { + "displayName": {"const": "Alice Hansen"}, + "id": {"pattern": "^%user_id_pattern%$"} + } + } + } + }, + "roles": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": {"pattern": "^%role_id_pattern%$"} + } + } + } + } + } + } + } + } + """ + And the JSON data of the response should contain resource "folder" with the following data: + """ + { + "type": "object", + "required": ["@UI.Hidden","@client.synchronize","createdBy","eTag","folder", + "id","lastModifiedDateTime","name","parentReference","remoteItem"], + "properties": { + "lastModifiedDateTime": { "format": "date-time" }, + "eTag": {"pattern": "%etag_pattern%"}, + "id": {"pattern": "^%share_id_pattern%$"}, + "name": {"const": "folder"}, + "remoteItem": { + "type": "object", + "required": ["createdBy","eTag","folder","id","lastModifiedDateTime","name","parentReference","permissions"], + "properties": { + "eTag": {"pattern": "%etag_pattern%"}, + "file": {}, + "id": {"pattern": "^%file_id_pattern%$"}, + "name": {"const": "folder"}, + "permissions": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "uniqueItems": true, + "items": { + "type": "object", + "required": ["grantedToV2","id","invitation","roles"], + "properties": { + "grantedToV2": { + "type": "object", + "required": ["group"], + "properties":{ + "group": { + "type": "object", + "required": ["displayName","id"], + "properties": { + "displayName": {"const": "grp1"}, + "id": {"pattern": "^%user_id_pattern%$"} + } + } + } + }, + "id": {"pattern": "^%permissions_id_pattern%$"}, + "invitation": { + "type": "object", + "required": ["invitedBy"], + "properties": { + "user":{ + "type": "object", + "required": ["displayName","id"], + "properties": { + "displayName": {"const": "Alice Hansen"}, + "id": {"pattern": "^%user_id_pattern%$"} + } + } + } + }, + "roles": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": {"pattern": "^%role_id_pattern%$"} + } + } + } + } + } + } + } + } + """ + + + Scenario Outline: list activities of folder shared with listGrant roles (Personal space) + Given the administrator has enabled the permissions role "" + And using spaces DAV path + And using SharingNG + And user "Alice" has created folder "folder" + And user "Alice" has sent the following resource share invitation: + | resource | folder | + | space | Personal | + | sharee | Brian | + | shareType | user | + | permissionsRole | | + And user "Brian" has a share "folder" synced + Then for user "Brian" folder "folder" of the space "Shares" should have the following activities: + | {user} added {resource} to {folder} | + | {user} shared {resource} with {sharee} | + Examples: + | permissions-role | + | Viewer List Grants | + | Editor List Grants | + + + Scenario: user lists permissions of a folder after enabling 'Viewer List Grants' role (Personal space) + Given the administrator has enabled the permissions role "Viewer List Grants" + 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 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": { + "@libre.graph.weight": { "const": 10 }, + "description": { "const": "View and download." }, + "displayName": { "const": "Can view" }, + "id": { "const": "b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5" } + } + }, + { + "type": "object", + "required": ["@libre.graph.weight", "description", "displayName", "id"], + "properties": { + "@libre.graph.weight": { "const": 30 }, + "description": { "const": "View, download and show all invited people." }, + "displayName": { "const": "Can view" }, + "id": { "const": "d5041006-ebb3-4b4a-b6a4-7c180ecfb17d" } + } + }, + { + "type": "object", + "required": ["@libre.graph.weight", "description", "displayName", "id"], + "properties": { + "@libre.graph.weight": { "const": 50 }, + "description": { "const": "View, download and upload." }, + "displayName": { "const": "Can upload" }, + "id": { "const": "1c996275-f1c9-4e71-abdf-a42f6495e960" } + } + }, + { + "type": "object", + "required": ["@libre.graph.weight", "description", "displayName", "id"], + "properties": { + "@libre.graph.weight": { "const": 60 }, + "description": { "const": "View, download, upload, edit, add and delete." }, + "displayName": { "const": "Can edit" }, + "id": { "const": "fb6c3e19-e378-47e5-b277-9732f9de6e21" } + } + } + ] + } + } + } + } + """ + + + Scenario: user lists permissions of a file after enabling 'File Editor List Grants' role (Project space) + Given the administrator has enabled the permissions role "File Editor List Grants" + And using spaces DAV path + And the administrator has assigned the role "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 + And user "Alice" has uploaded a file inside space "new-space" with content "hello world" to "textfile0.txt" + When user "Alice" gets permissions list for file "textfile0.txt" of the space "new-space" using 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": 3, + "maxItems": 3, + "uniqueItems": true, + "items": { + "oneOf": [ + { + "type": "object", + "required": ["@libre.graph.weight", "description", "displayName", "id"], + "properties": { + "@libre.graph.weight": { "const": 10 }, + "description": { "const": "View and download." }, + "displayName": { "const": "Can view" }, + "id": { "const": "b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5" } + } + }, + { + "type": "object", + "required": ["@libre.graph.weight", "description", "displayName", "id"], + "properties": { + "@libre.graph.weight": { "const": 100 }, + "description": { "const": "View, download and edit." }, + "displayName": { "const": "Can edit" }, + "id": { "const": "2d00ce52-1fc2-4dbc-8b95-a73b73395f5a" } + } + }, + { + "type": "object", + "required": ["@libre.graph.weight", "description", "displayName", "id"], + "properties": { + "@libre.graph.weight": { "const": 110 }, + "description": { "const": "View, download, edit and show all invited people." }, + "displayName": { "const": "Can edit" }, + "id": { "const": "c1235aea-d106-42db-8458-7d5610fb0a67" } + } + } + ] + } + } + } + } + """ \ No newline at end of file diff --git a/tests/acceptance/features/apiSharingNgAdditionalShareRole/spaceEditorWithoutTrashbin.feature b/tests/acceptance/features/apiSharingNgAdditionalShareRole/spaceEditorWithoutTrashbin.feature new file mode 100644 index 0000000000..d33e3568a2 --- /dev/null +++ b/tests/acceptance/features/apiSharingNgAdditionalShareRole/spaceEditorWithoutTrashbin.feature @@ -0,0 +1,28 @@ +@env-config +Feature: an user shares resources + As a user + I don't want space editor to access deleted files + So that they can't restore them + + + Scenario: sharee checks trashbin after file is deleted + Given these users have been created with default attributes: + | username | + | Alice | + | Brian | + And using spaces DAV path + And the administrator has enabled the permissions role "Space Editor Without Trashbin" + 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 + And user "Alice" has uploaded a file inside space "new-space" with content "hello world" to "textfile.txt" + And user "Alice" has sent the following space share invitation: + | space | new-space | + | sharee | Brian | + | shareType | user | + | permissionsRole | Space Editor Without Trashbin | + And user "Brian" has removed the file "textfile.txt" from space "new-space" + When user "Brian" tries to list all deleted files in the trash bin of the space "new-space" + Then the HTTP status code should be "403" + When user "Brian" tries to restore the file "textfile.txt" from the trash of the space "new-space" to "/textfile.txt" + Then the HTTP status code should be "403" + And as "Alice" file "textfile.txt" should exist in the trashbin of the space "new-space" diff --git a/tests/acceptance/features/apiSharingNgAdditionalShareRole/spaceEditorWithoutVersionsWithoutTrashbin.feature b/tests/acceptance/features/apiSharingNgAdditionalShareRole/spaceEditorWithoutVersionsWithoutTrashbin.feature new file mode 100644 index 0000000000..bb7266552b --- /dev/null +++ b/tests/acceptance/features/apiSharingNgAdditionalShareRole/spaceEditorWithoutVersionsWithoutTrashbin.feature @@ -0,0 +1,33 @@ +@env-config +Feature: an user shares resources + As a user + I don't want space editor to access file versions or the trash bin + So that they can't see the versions or restore deleted files + + + Scenario: space editor without versions without trash bin permissions cannot access versions or restore deleted files + Given these users have been created with default attributes: + | username | + | Alice | + | Brian | + And using spaces DAV path + And the administrator has enabled the permissions role "Space Editor Without Versions Without Trashbin" + 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 + And user "Alice" has uploaded a file inside space "new-space" with content "new content" to "textfile.txt" + And user "Alice" has uploaded a file inside space "new-space" with content "newest content" to "textfile.txt" + And user "Alice" has sent the following space share invitation: + | space | new-space | + | sharee | Brian | + | shareType | user | + | permissionsRole | Space Editor Without Versions Without Trashbin | + When user "Brian" tries to get versions of the file "textfile.txt" from the space "new-space" using the WebDAV API + Then the HTTP status code should be "403" + When user "Brian" tries to download version of the file "textfile.txt" with the index "1" of the space "new-space" using the WebDAV API + Then the HTTP status code should be "403" + When user "Brian" removes the file "textfile.txt" from space "new-space" + And user "Brian" tries to list all deleted files in the trash bin of the space "new-space" + Then the HTTP status code should be "403" + When user "Brian" tries to restore the file "textfile.txt" from the trash of the space "new-space" to "/textfile.txt" + Then the HTTP status code should be "403" + And as "Alice" file "textfile.txt" should exist in the trashbin of the space "new-space" \ No newline at end of file