diff --git a/tests/TestHelpers/GraphHelper.php b/tests/TestHelpers/GraphHelper.php index 524e9862b02..ec0ff647894 100644 --- a/tests/TestHelpers/GraphHelper.php +++ b/tests/TestHelpers/GraphHelper.php @@ -60,6 +60,13 @@ public static function getSpaceIdRegex(): string { return self::getUUIDv4Regex() . '\\\$' . self::getUUIDv4Regex(); } + /** + * @return string + */ + public static function getShareIdRegex(): string { + return self::getUUIDv4Regex() . ':' . self::getUUIDv4Regex() . ':' . self::getUUIDv4Regex(); + } + /** * Key name can consist of @@@ * This function separate such key and return its actual value from actual drive response which can be used for assertion @@ -1532,4 +1539,100 @@ public static function getPermissionsList( self::getRequestHeaders() ); } + + /** + * Get the role id by name + * + * @param string $baseUrl + * @param string $xRequestId + * @param string $user + * @param string $password + * @param string $role + * + * @return string + * + */ + public static function getRoleIdByName( + string $baseUrl, + string $xRequestId, + string $user, + string $password, + string $role + ) : string { + $url = self::getBetaFullUrl($baseUrl, "roleManagement/permissions/roleDefinitions"); + + $response = HttpRequestHelper::get( + $url, + $xRequestId, + $user, + $password, + self::getRequestHeaders(), + ); + + $roles = \json_decode($response->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR); + + if (isset($roles)) { + foreach ($roles as $item) { + if ($item["displayName"] === $role) { + return $item["id"]; + } + } + } + } + + /** + * @param string $baseUrl + * @param string $xRequestId + * @param string $user + * @param string $password + * @param string $spaceId + * @param string $itemId + * @param array $shareeId + * @param array|null $shareType + * @param string|null $role + * + * @return ResponseInterface + * @throws \JsonException + */ + public static function sendSharingInvitation( + string $baseUrl, + string $xRequestId, + string $user, + string $password, + string $spaceId, + string $itemId, + array $shareeId, + ?array $shareType, + ?string $role + ): ResponseInterface { + $url = self::getBetaFullUrl($baseUrl, "drives/$spaceId/items/$itemId/invite"); + $body = []; + + for ($i = 0; $i < count($shareeId); $i++) { + if ($shareType[$i] === 'group') { + $body['recipients'][] = [ + "@libre.graph.recipient.type"=> "group", + "objectId" => $shareeId[$i] + ]; + } else { + $body['recipients'][] = [ + 'objectId' => $shareeId[$i] + ]; + } + } + + if ($role !== null) { + $roleId = self::getRoleIdByName($baseUrl, $xRequestId, $user, $password, $role); + $body['roles'] = [$roleId]; + } + + return HttpRequestHelper::post( + $url, + $xRequestId, + $user, + $password, + self::getRequestHeaders(), + \json_encode($body) + ); + } } diff --git a/tests/acceptance/features/apiSharingNg/shareInvitations.feature b/tests/acceptance/features/apiSharingNg/shareInvitations.feature new file mode 100644 index 00000000000..eba8ec809e5 --- /dev/null +++ b/tests/acceptance/features/apiSharingNg/shareInvitations.feature @@ -0,0 +1,215 @@ +Feature: Send a sharing invitations + As the owner of a resource + I want to be able to send invitations to other users + So that they can have access to it + + https://owncloud.dev/libre-graph-api/#/drives.permissions/Invite + + Background: + Given these users have been created with default attributes and without skeleton files: + | username | + | Alice | + | Brian | + + + Scenario Outline: send sharing invitation to user with different roles via the Graph API + Given user "Alice" has uploaded file with content "to share" to "/textfile1.txt" + And user "Alice" has created folder "FolderToShare" + When user "Alice" sends the following share invitation using the Graph API: + | resourceType | | + | resource | | + | space | Personal | + | sharee | Brian | + | shareType | user | + | role | | + 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", + "items": { + "type": "object", + "required": [ + "id", + "roles", + "grantedToV2" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^%share_id_pattern%$" + }, + "roles": { + "type": "array", + "items": { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + }, + "grantedToV2": { + "type": "object", + "required": [ + "user" + ], + "properties": { + "user": { + "type": "object", + "required": [ + "id", + "displayName" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + }, + "displayName": { + "type": "string", + "enum": [ + "Brian Murphy" + ] + } + } + } + } + } + } + } + } + } + } + """ + Examples: + | role | resource-type | path | + | Viewer | file | /textfile1.txt | + | Editor | file | /textfile1.txt | + | Co Owner | file | /textfile1.txt | + | Uploader | file | /textfile1.txt | + | Manager | file | /textfile1.txt | + | Viewer | folder | FolderToShare | + | Editor | folder | FolderToShare | + | Co Owner | folder | FolderToShare | + | Uploader | folder | FolderToShare | + | Manager | folder | FolderToShare | + + + Scenario Outline: send sharing invitation to group with different roles via the Graph API + Given user "Carol" has been created with default attributes and without skeleton files + And group "grp1" has been created + And the following users have been added to the following groups + | username | groupname | + | Brian | grp1 | + | Carol | grp1 | + And user "Alice" has uploaded file with content "to share" to "/textfile1.txt" + And user "Alice" has created folder "FolderToShare" + When user "Alice" sends the following share invitation using the Graph API: + | resourceType | | + | resource | | + | space | Personal | + | sharee | grp1 | + | shareType | group | + | role | | + 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", + "items": { + "type": "object", + "required": [ + "id", + "roles", + "grantedToV2" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^%share_id_pattern%$" + }, + "roles": { + "type": "array", + "items": { + "type": "string", + "pattern": "^%role_id_pattern%$" + } + }, + "grantedToV2": { + "type": "object", + "required": [ + "group" + ], + "properties": { + "group": { + "type": "object", + "required": [ + "id", + "displayName" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^%group_id_pattern%$" + }, + "displayName": { + "type": "string", + "enum": [ + "grp1" + ] + } + } + } + } + } + } + } + } + } + } + """ + Examples: + | role | resource-type | path | + | Viewer | file | /textfile1.txt | + | Editor | file | /textfile1.txt | + | Co Owner | file | /textfile1.txt | + | Uploader | file | /textfile1.txt | + | Manager | file | /textfile1.txt | + | Viewer | folder | FolderToShare | + | Editor | folder | FolderToShare | + | Co Owner | folder | FolderToShare | + | Uploader | folder | FolderToShare | + | Manager | folder | FolderToShare | + + + Scenario: send sharing invitation to user and group at once via the Graph API + Given these users have been created with default attributes and without skeleton files: + | username | + | Carol | + | Bob | + And the administrator has assigned the role "Admin" to user "Alice" using the Graph API + And user "Alice" has created a group "grp1" using the Graph API + And the administrator "Alice" has added the following users to a group "grp1" at once using the Graph API + | username | + | Brian | + | Carol | + And user "Alice" has uploaded file with content "to share" to "/textfile1.txt" + When user "Alice" sends the following share invitation using the Graph API: + | resourceType | file | + | resource | /textfile1.txt | + | space | Personal | + | sharee | grp1, Bob | + | shareType | group, user | + | role | Viewer | + Then the HTTP status code should be "200" + And the JSON data of the response should match diff --git a/tests/acceptance/features/bootstrap/FeatureContext.php b/tests/acceptance/features/bootstrap/FeatureContext.php index 39ca02c7f66..a927e90c900 100644 --- a/tests/acceptance/features/bootstrap/FeatureContext.php +++ b/tests/acceptance/features/bootstrap/FeatureContext.php @@ -2783,6 +2783,22 @@ public function substituteInLineCodes( $this, "getGroupIdByGroupName" ], "parameter" => [$group] + ], + [ + "code" => "%role_id_pattern%", + "function" => [ + __NAMESPACE__ . '\TestHelpers\GraphHelper', + "getUUIDv4Regex" + ], + "parameter" => [] + ], + [ + "code" => "%share_id_pattern%", + "function" => [ + __NAMESPACE__ . '\TestHelpers\GraphHelper', + "getShareIdRegex" + ], + "parameter" => [] ] ]; if ($user !== null) { diff --git a/tests/acceptance/features/bootstrap/SharingNgContext.php b/tests/acceptance/features/bootstrap/SharingNgContext.php index c3de67cd570..5c3478631bb 100644 --- a/tests/acceptance/features/bootstrap/SharingNgContext.php +++ b/tests/acceptance/features/bootstrap/SharingNgContext.php @@ -22,6 +22,7 @@ use Behat\Behat\Context\Context; use Behat\Behat\Hook\Scope\BeforeScenarioScope; use TestHelpers\GraphHelper; +use Behat\Gherkin\Node\TableNode; require_once 'bootstrap.php'; @@ -63,7 +64,7 @@ public function before(BeforeScenarioScope $scope): void { */ public function theUserPermissionsListOfResource(string $user, string $fileOrFolder, string $resource, string $space):void { $spaceId = ($this->spacesContext->getSpaceByName($user, $space))["id"]; - + if ($fileOrFolder === 'folder') { $itemId = $this->spacesContext->getResourceId($user, $space, $resource); } else { @@ -80,4 +81,46 @@ public function theUserPermissionsListOfResource(string $user, string $fileOrFol ) ); } + + /** + * @When /^user "([^"]*)" sends the following share invitation using the Graph API:$/ + * + * @param string $user + * @param TableNode $table + * + * @return void + * @throws Exception + */ + public function userSendsTheFollowingShareInvitationUsingTheGraphApi(string $user, TableNode $table): void { + $rows = $table->getRowsHash(); + $spaceId = ($this->spacesContext->getSpaceByName($user, $rows['space']))["id"]; + + $itemId = ($rows['resourceType'] === 'folder') + ? $this->spacesContext->getResourceId($user, $rows['space'], $rows['resource']) + : $this->spacesContext->getFileId($user, $rows['space'], $rows['resource']); + + $sharee = array_map('trim', explode(',', $rows['sharee'])); + $shareType = array_map('trim', explode(',', $rows['shareType'])); + + $shareeId = []; + for ($i = 0; $i < count($sharee); $i++) { + $shareeId[] = ($shareType[$i] === 'user') + ? $this->featureContext->getAttributeOfCreatedUser($sharee[$i], 'id') + : $this->featureContext->getAttributeOfCreatedGroup($sharee[$i], 'id'); + } + + $this->featureContext->setResponse( + GraphHelper::sendSharingInvitation( + $this->featureContext->getBaseUrl(), + $this->featureContext->getStepLineRef(), + $user, + $this->featureContext->getPasswordForUser($user), + $spaceId, + $itemId, + $shareeId, + $shareType, + $rows['role'] + ) + ); + } }