diff --git a/tests/TestHelpers/GraphHelper.php b/tests/TestHelpers/GraphHelper.php index 6e005ccbf..4c14b059c 100644 --- a/tests/TestHelpers/GraphHelper.php +++ b/tests/TestHelpers/GraphHelper.php @@ -1360,4 +1360,37 @@ class GraphHelper { self::getRequestHeaders() ); } + + /** + * @param string $baseUrl + * @param string $xRequestId + * @param string $user + * @param string $password + * @param string $userId + * @param string $path + * + * @return ResponseInterface + * @throws GuzzleException + */ + public static function generateGDPRReport( + string $baseUrl, + string $xRequestId, + string $user, + string $password, + string $userId, + string $path + ): ResponseInterface { + $url = self::getFullUrl($baseUrl, 'users/' . $userId . '/exportPersonalData'); + // this payload is the storage location of the report generated + $payload['storageLocation'] = $path; + return HttpRequestHelper::sendRequest( + $url, + $xRequestId, + "POST", + $user, + $password, + self::getRequestHeaders(), + \json_encode($payload) + ); + } } diff --git a/tests/acceptance/features/apiGraph/userGDPRExport.feature b/tests/acceptance/features/apiGraph/userGDPRExport.feature new file mode 100644 index 000000000..c804e8830 --- /dev/null +++ b/tests/acceptance/features/apiGraph/userGDPRExport.feature @@ -0,0 +1,180 @@ +@api +Feature: user GDPR (General Data Protection Regulation) report + As a user + I want to generate my GDPR report + So that I can review what events are stored by the server + + Background: + Given user "Alice" has been created with default attributes and without skeleton files + And using spaces DAV path + + + Scenario: generate a GDPR report and check user data in the downloaded report + When user "Alice" exports her GDPR report to "/.personal_data_export.json" using the Graph API + And user "Alice" downloads the content of GDPR report ".personal_data_export.json" + Then the HTTP status code of responses on each endpoint should be "201, 200" respectively + And the downloaded JSON content should contain key 'user' and match + """ + { + "type": "object", + "required": [ + "id", + "username", + "mail", + "display_name", + "uid_number", + "gid_number" + ], + "properties": { + "id": { + "type": "object", + "required": [ + "idp", + "opaque_id", + "type" + ], + "properties": { + "idp": { + "type": "string", + "pattern": "^%base_url%$" + }, + "opaque_id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + }, + "type": { + "type": "number", + "enum": [1] + } + } + }, + "username": { + "type": "string", + "enum": ["Alice"] + }, + "mail": { + "type": "string", + "enum": ["alice@example.org"] + }, + "display_name": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "uid_number": { + "type": "number", + "enum": [99] + }, + "gid_number": { + "type": "number", + "enum": [99] + } + } + } + """ + + Scenario: generate a GDPR report and check events when a user is created + When user "Alice" exports her GDPR report to "/.personal_data_export.json" using the Graph API + And user "Alice" downloads the content of GDPR report ".personal_data_export.json" + Then the HTTP status code of responses on each endpoint should be "201, 200" respectively + And the downloaded JSON content should contain event type "events.UserCreated" in item 'events' and should match + """ + { + "type": "object", + "required": [ + "event" + ], + "properties": { + "event" : { + "type": "object", + "required": [ + "Executant", + "UserID" + ], + "properties": { + "Executant": { + "type": "object", + "required": [ + "idp", + "opaque_id", + "type" + ], + "properties": { + "idp": { + "type": "string", + "pattern": "^%base_url%$" + }, + "opaque_id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + }, + "type": { + "type": "number", + "enum": [1] + } + } + }, + "UserID": { + "type": "string", + "pattern": "^%user_id_pattern%$" + } + } + } + } + } + """ + And the downloaded JSON content should contain event type "events.SpaceCreated" in item 'events' and should match + """ + { + "type": "object", + "required": [ + "event" + ], + "properties": { + "event" : { + "type": "object", + "required": [ + "Executant", + "Name", + "Type", + "Quota" + ], + "properties": { + "Executant": { + "type": "object", + "required": [ + "idp", + "opaque_id", + "type" + ], + "properties": { + "idp": { + "type": "string", + "pattern": "^%base_url%$" + }, + "opaque_id": { + "type": "string", + "pattern": "^%user_id_pattern%$" + }, + "type": { + "type": "number", + "enum": [1] + } + } + }, + "Name": { + "type": "string", + "enum": ["Alice Hansen"] + }, + "Type": { + "type": "string", + "enum": ["personal"] + }, + "Quota": { + "type": ["number", "null"], + "enum": [null] + } + } + } + } + } + """ diff --git a/tests/acceptance/features/bootstrap/GraphContext.php b/tests/acceptance/features/bootstrap/GraphContext.php index c88a0c053..9dc54e1d7 100644 --- a/tests/acceptance/features/bootstrap/GraphContext.php +++ b/tests/acceptance/features/bootstrap/GraphContext.php @@ -2317,4 +2317,81 @@ class GraphContext implements Context { ) ); } + + /** + * @When /^user "([^"]*)" exports (?:her|his) GDPR report to "([^"]*)" using the Graph API$/ + * + * @param string $user + * @param string $path + * + * @return void + * @throws GuzzleException + */ + public function userGeneratesGDPRReportOfOwnDataToPath(string $user, string $path): void { + $credentials = $this->getAdminOrUserCredentials($user); + $userId = $this->featureContext->getAttributeOfCreatedUser($user, 'id'); + $this->featureContext->setResponse( + GraphHelper::generateGDPRReport( + $this->featureContext->getBaseUrl(), + $this->featureContext->getStepLineRef(), + $credentials['username'], + $credentials['password'], + $userId, + $path + ) + ); + $this->featureContext->pushToLastStatusCodesArrays(); + } + + /** + * @Then the downloaded JSON content should contain event type :eventType in item 'events' and should match + * + * @param string $eventType + * @param PyStringNode $schemaString + * + * @return void + * @throws GuzzleException + * + */ + public function downloadedJsonContentShouldContainEventTypeInItemAndShouldMatch(string $eventType, PyStringNode $schemaString): void { + $actualResponseToAssert = null; + $events = $this->featureContext->getJsonDecodedResponseBodyContent()->events; + foreach ($events as $event) { + if ($event->type === $eventType) { + $actualResponseToAssert = $event; + break; + } + } + if ($actualResponseToAssert === null) { + throw new Error( + "Response does not contain event type '" . $eventType . "'." + ); + } + JsonAssertions::assertJsonDocumentMatchesSchema( + $actualResponseToAssert, + $this->featureContext->getJSONSchema($schemaString) + ); + } + + /** + * @Then the downloaded JSON content should contain key 'user' and match + * + * @param PyStringNode $schemaString + * + * @return void + * @throws GuzzleException + * + */ + public function downloadedJsonContentShouldContainKeyUserAndMatch(PyStringNode $schemaString): void { + $actualResponseToAssert = $this->featureContext->getJsonDecodedResponseBodyContent(); + if (!isset($actualResponseToAssert->user)) { + throw new Error( + "Response does not contain key 'user'" + ); + } + JsonAssertions::assertJsonDocumentMatchesSchema( + $actualResponseToAssert->user, + $this->featureContext->getJSONSchema($schemaString) + ); + } } diff --git a/tests/acceptance/features/bootstrap/WebDav.php b/tests/acceptance/features/bootstrap/WebDav.php index 49838962f..8250b81c4 100644 --- a/tests/acceptance/features/bootstrap/WebDav.php +++ b/tests/acceptance/features/bootstrap/WebDav.php @@ -24,6 +24,7 @@ use Behat\Gherkin\Node\TableNode; use GuzzleHttp\Exception\BadResponseException; use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Ring\Exception\ConnectException; +use Helmich\JsonAssert\JsonAssertions; use PHPUnit\Framework\Assert; use Psr\Http\Message\ResponseInterface; use GuzzleHttp\Stream\StreamInterface; @@ -5384,4 +5385,19 @@ trait WebDav { "Expected display name of version with index $index in response to user '$this->responseUser' was '$expectedUserDisplayName', but got '$actualUserDisplayName'" ); } + + /** + * @When user :user downloads the content of GDPR report :pathToFile + * + * @param string $user + * @param string $pathToFile + * + * @return void + * @throws Exception + */ + public function userGetsTheContentOfGeneratedJsonReport(string $user, string $pathToFile): void { + $password = $this->getPasswordForUser($user); + $this->downloadFileAsUserUsingPassword($user, $pathToFile, $password); + $this->pushToLastStatusCodesArrays(); + } }