diff --git a/tests/TestHelpers/GraphHelper.php b/tests/TestHelpers/GraphHelper.php index a81470095f..2056fb6d8c 100644 --- a/tests/TestHelpers/GraphHelper.php +++ b/tests/TestHelpers/GraphHelper.php @@ -26,14 +26,54 @@ class GraphHelper { ]; } + /** + * + * @return string + */ + public static function getUUIDv4Regex(): string { + return '[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}'; + } + /** * @param string $id * - * @return int (1 = true | 0 = false) + * @return bool */ - public static function isUUIDv4(string $id): int { - $regex = '/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i'; - return preg_match($regex, $id); + public static function isUUIDv4(string $id): bool { + $regex = "/^" . self::getUUIDv4Regex() . "$/i"; + return (bool)preg_match($regex, $id); + } + + /** + * @param string $spaceId + * + * @return bool + */ + public static function isSpaceId(string $spaceId): bool { + $regex = "/^" . self::getUUIDv4Regex() . '\\$' . self::getUUIDv4Regex() . "$/i"; + return (bool)preg_match($regex, $spaceId); + } + + /** + * 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 + * + * @param string $keyName + * @param array $actualDriveInformation + * + * @return string + */ + public static function separateAndGetValueForKey(string $keyName, array $actualDriveInformation): string { + // break the segment with @@@ to find the actual value from the actual drive information + $separatedKey = explode("@@@", $keyName); + // this stores the actual value of each key from drive information response used for assertion + $actualKeyValue = $actualDriveInformation; + + foreach ($separatedKey as $key) { + $actualKeyValue = $actualKeyValue[$key]; + } + + return $actualKeyValue; } /** @@ -242,6 +282,32 @@ class GraphHelper { ); } + /** + * @param string $baseUrl + * @param string $xRequestId + * @param string $byUser + * @param string $userPassword + * @param string|null $user + * + * @return ResponseInterface + * @throws GuzzleException + */ + public static function getUserWithDriveInformation( + string $baseUrl, + string $xRequestId, + string $byUser, + string $userPassword, + ?string $user = null + ): ResponseInterface { + $url = self::getFullUrl($baseUrl, 'users/' . $user . '?%24select=&%24expand=drive'); + return HttpRequestHelper::get( + $url, + $xRequestId, + $byUser, + $userPassword, + ); + } + /** * @param string $baseUrl * @param string $xRequestId diff --git a/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md b/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md index b611ea8c9a..4073214d05 100644 --- a/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md +++ b/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md @@ -78,3 +78,6 @@ The expected failures in this file are from features in the owncloud/ocis repo. #### [Requests with invalid credentials do not return CORS headers](https://github.com/owncloud/ocis/issues/5194) - [apiCors/cors.feature:67](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiCors/cors.feature#L67) - [apiCors/cors.feature:68](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiCors/cors.feature#L68) + +#### [A User can get information of another user with Graph API](https://github.com/owncloud/ocis/issues/5125) +- [apiGraph/getUser.feature:23](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiGraph/getUser.feature#L23) diff --git a/tests/acceptance/features/apiGraph/getUser.feature b/tests/acceptance/features/apiGraph/getUser.feature new file mode 100644 index 0000000000..b125df5558 --- /dev/null +++ b/tests/acceptance/features/apiGraph/getUser.feature @@ -0,0 +1,77 @@ +@api @skipOnOcV10 +Feature: get users + As an admin + I want to be able to retrieve user information + So that I can see the information + + Background: + Given these users have been created with default attributes and without skeleton files: + | username | + | Alice | + | Brian | + And the administrator has given "Alice" the role "Admin" using the settings api + + + Scenario: admin user gets the information of a user + When user "Alice" gets information of user "Brian" using Graph API + Then the HTTP status code should be "200" + And the user retrieve API response should contain the following information: + | displayName | id | mail | onPremisesSamAccountName | + | Brian Murphy | %uuid_v4% | brian@example.org | Brian | + + + Scenario: non-admin user tries to get the information of a user + When user "Brian" tries to get information of user "Alice" using Graph API + Then the HTTP status code should be "401" + And the last response should be an unauthorized response + + + Scenario: admin user gets all users + When user "Alice" gets all users using the Graph API + Then the HTTP status code should be "200" + And the API response should contain all users with the following information: + | displayName | id | mail | onPremisesSamAccountName | + | Alice Hansen | %uuid_v4% | alice@example.org | Alice | + | Brian Murphy | %uuid_v4% | brian@example.org | Brian | + + + Scenario: non-admin user tries to get all users + When user "Brian" tries to get all users using the Graph API + Then the HTTP status code should be "401" + And the last response should be an unauthorized response + + + Scenario: admin user gets the drive information of a user + When the user "Alice" gets user "Brian" along with his drive information using Graph API + Then the HTTP status code should be "200" + And the user retrieve API response should contain the following information: + | displayName | id | mail | onPremisesSamAccountName | + | Brian Murphy | %uuid_v4% | brian@example.org | Brian | + And the user retrieve API response should contain the following drive information: + | driveType | personal | + | driveAlias | personal/brian | + | id | %space_id% | + | name | Brian Murphy | + | owner@@@user@@@id | %user_id% | + | quota@@@state | normal | + | root@@@id | %space_id% | + | root@@@webDavUrl | %base_url%/dav/spaces/%space_id% | + | webUrl | %base_url%/f/%space_id% | + + + Scenario: normal user gets his/her own drive information + When the user "Brian" gets his drive information using Graph API + Then the HTTP status code should be "200" + And the user retrieve API response should contain the following information: + | displayName | id | mail | onPremisesSamAccountName | + | Brian Murphy | %uuid_v4% | brian@example.org | Brian | + And the user retrieve API response should contain the following drive information: + | driveType | personal | + | driveAlias | personal/brian | + | id | %space_id% | + | name | Brian Murphy | + | owner@@@user@@@id | %user_id% | + | quota@@@state | normal | + | root@@@id | %space_id% | + | root@@@webDavUrl | %base_url%/dav/spaces/%space_id% | + | webUrl | %base_url%/f/%space_id% | diff --git a/tests/acceptance/features/bootstrap/GraphContext.php b/tests/acceptance/features/bootstrap/GraphContext.php index 0eb723b37f..92b857452f 100644 --- a/tests/acceptance/features/bootstrap/GraphContext.php +++ b/tests/acceptance/features/bootstrap/GraphContext.php @@ -28,6 +28,11 @@ class GraphContext implements Context { */ private FeatureContext $featureContext; + /** + * @var SpacesContext + */ + private SpacesContext $spacesContext; + /** * This will run before EVERY scenario. * It will set the properties for this object. @@ -1221,12 +1226,7 @@ class GraphContext implements Context { . trim($expectedValue[$keyName], '%') ); } - Assert::assertEquals( - 1, - GraphHelper::isUUIDv4($actualValue['id']), - __METHOD__ . - ' Expected user_id to have UUIDv4 pattern but found: ' . $actualValue['id'] - ); + Assert::assertTrue(GraphHelper::isUUIDv4($actualValue['id']), __METHOD__ . ' Expected user_id to have UUIDv4 pattern but found: ' . $actualValue['id']); break; default: Assert::assertEquals( @@ -1240,4 +1240,187 @@ class GraphContext implements Context { } } } + + /** + * @When user :byUser tries to get information of user :user using Graph API + * @When user :byUser gets information of user :user using Graph API + * + * @param string $byUser + * @param string $user + * + * @return void + * @throws GuzzleException + */ + public function userTriesToGetInformationOfUser(string $byUser, string $user): void { + $credentials = $this->getAdminOrUserCredentials($byUser); + $response = GraphHelper::getUser( + $this->featureContext->getBaseUrl(), + $this->featureContext->getStepLineRef(), + $credentials['username'], + $credentials['password'], + $user + ); + $this->featureContext->setResponse($response); + } + + /** + * @When user :user tries to get all users using the Graph API + * @When user :user gets all users using the Graph API + * + * @param string $user + * + * @return void + * @throws GuzzleException + */ + public function userGetsAllUserUsingTheGraphApi(string $user) { + $credentials = $this->getAdminOrUserCredentials($user); + $response = GraphHelper::getUsers( + $this->featureContext->getBaseUrl(), + $this->featureContext->getStepLineRef(), + $credentials['username'], + $credentials['password'], + ); + $this->featureContext->setResponse($response); + } + + /** + * @Then the API response should contain all users with the following information: + * + * @param TableNode $table + * + * @throws Exception + * @return void + */ + public function theApiResponseShouldContainAllUserWithFollowingInformation(TableNode $table): void { + $values = $table->getHash(); + $apiResponse = $this->featureContext->getJsonDecodedResponse($this->featureContext->getResponse())['value']; + foreach ($values as $expectedValue) { + $found = false; + foreach ($apiResponse as $key => $actualResponseValue) { + if ($expectedValue["displayName"] === $actualResponseValue["displayName"]) { + $found = true; + $this->checkUserInformation($expectedValue, $actualResponseValue); + unset($apiResponse[$key]); + break; + } + } + if (!$found) { + throw new Exception('User ' . $expectedValue["displayName"] . ' could not be found in the response.'); + } + } + } + + /** + * @param string $byUser + * @param string|null $user + * + * @return ResponseInterface + * @throws JsonException + * @throws GuzzleException + */ + public function retrieveUserInformationAlongWithDriveUsingGraphApi( + string $byUser, + ?string $user = null + ):ResponseInterface { + $user = $user ?? $byUser; + $credentials = $this->getAdminOrUserCredentials($user); + return GraphHelper::getUserWithDriveInformation( + $this->featureContext->getBaseUrl(), + $this->featureContext->getStepLineRef(), + $credentials["username"], + $credentials["password"], + $user + ); + } + + /** + * @When /^the user "([^"]*)" gets user "([^"]*)" along with (his|her) drive information using Graph API$/ + * + * @param string $byUser + * @param string $user + * + * @return void + */ + public function userTriesToGetInformationOfUserAlongWithHisDriveData(string $byUser, string $user) { + $response = $this->retrieveUserInformationAlongWithDriveUsingGraphApi($byUser, $user); + $this->featureContext->setResponse($response); + } + + /** + * + * @When /^the user "([^"]*)" gets (his|her) drive information using Graph API$/ + * + * @param string $user + * + * @return void + */ + public function userTriesToGetOwnDriveInformation(string $user) { + $response = $this->retrieveUserInformationAlongWithDriveUsingGraphApi($user); + $this->featureContext->setResponse($response); + } + + /** + * @param array $driveInformation + * + * @return string + */ + public static function getSpaceIdFromActualDriveinformation(array $driveInformation): string { + return $driveInformation['id']; + } + + /** + * check if single drive information is correct + * + * @param array $expectedDriveInformation + * @param array $actualDriveInformation + * + * @return void + */ + public function checkUserDriveInformation(array $expectedDriveInformation, array $actualDriveInformation):void { + foreach (array_keys($expectedDriveInformation) as $keyName) { + $actualKeyValue = GraphHelper::separateAndGetValueForKey($keyName, $actualDriveInformation); + switch ($expectedDriveInformation[$keyName]) { + case '%user_id%': + Assert::assertTrue(GraphHelper::isUUIDv4($actualKeyValue), __METHOD__ . ' Expected user_id to have UUIDv4 pattern but found: ' . $actualKeyValue); + break; + case '%space_id%': + Assert::assertTrue(GraphHelper::isSpaceId($actualKeyValue), __METHOD__ . ' Expected space_id to have a UUIDv4:UUIDv4 pattern but found: ' . $actualKeyValue); + break; + default: + $expectedDriveInformation[$keyName] = $this->featureContext->substituteInLineCodes( + $expectedDriveInformation[$keyName], + $this->featureContext->getCurrentUser(), + [], + [ + [ + // the actual space_id is substituted from the actual drive information rather than making an API request and substituting + "code" => "%space_id%", + "function" => + [$this, "getSpaceIdFromActualDriveinformation"], + "parameter" => [$actualDriveInformation] + ], + ] + ); + Assert::assertEquals($expectedDriveInformation[$keyName], $actualKeyValue); + } + } + } + + /** + * @param TableNode $table + * + * @Then the user retrieve API response should contain the following drive information: + * + * @return void + */ + public function theResponseShouldContainTheFollowingDriveInformation(TableNode $table): void { + $expectedDriveInformation = $table->getRowsHash(); + // array of user drive information (Personal Drive Information Only) + $actualDriveInformation = $this->featureContext->getJsonDecodedResponse($this->featureContext->getResponse()); + if (\is_array($actualDriveInformation) && \array_key_exists('drive', $actualDriveInformation)) { + $this->checkUserDriveInformation($expectedDriveInformation, $actualDriveInformation['drive']); + } else { + throw new Error('Response is not an array or the array does not consist key "drive"'); + } + } }