From 5812ee22351fff89868d942d76e6e15da461473d Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 28 Jun 2024 10:43:21 +0200 Subject: [PATCH] feat(tests): Add integration test for file system tags cache bug Signed-off-by: Joas Schilling --- tests/Integration/app/appinfo/routes.php | 2 + .../app/lib/Controller/EndpointController.php | 41 +++++++++++ .../features/bootstrap/FeatureContext.php | 72 ++++++++++++++----- .../Integration/features/sharing-user.feature | 60 +++++++++++++++- 4 files changed, 156 insertions(+), 19 deletions(-) diff --git a/tests/Integration/app/appinfo/routes.php b/tests/Integration/app/appinfo/routes.php index da3a1a0..9f5cb17 100644 --- a/tests/Integration/app/appinfo/routes.php +++ b/tests/Integration/app/appinfo/routes.php @@ -9,5 +9,7 @@ declare(strict_types=1); return [ 'ocs' => [ ['name' => 'Endpoint#reset', 'url' => '', 'verb' => 'DELETE'], + ['name' => 'Endpoint#prepare', 'url' => '', 'verb' => 'POST'], + ['name' => 'Endpoint#tagFile', 'url' => '/tag-file', 'verb' => 'POST'], ], ]; diff --git a/tests/Integration/app/lib/Controller/EndpointController.php b/tests/Integration/app/lib/Controller/EndpointController.php index 9350858..6439a84 100644 --- a/tests/Integration/app/lib/Controller/EndpointController.php +++ b/tests/Integration/app/lib/Controller/EndpointController.php @@ -10,14 +10,22 @@ namespace OCA\FilesAccessControlTesting\Controller; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; +use OCP\Files\IRootFolder; use OCP\IDBConnection; use OCP\IRequest; +use OCP\SystemTag\ISystemTagManager; +use OCP\SystemTag\ISystemTagObjectMapper; +use OCP\SystemTag\TagNotFoundException; class EndpointController extends OCSController { public function __construct( string $appName, IRequest $request, protected IDBConnection $db, + protected ISystemTagManager $systemTagManager, + protected ISystemTagObjectMapper $systemTagObjectMapper, + protected IRootFolder $rootFolder, + protected string $userId, ) { parent::__construct($appName, $request); } @@ -32,6 +40,39 @@ class EndpointController extends OCSController { $query->delete('flow_operations')->executeStatement(); $query->delete('flow_operations_scope')->executeStatement(); + + try { + $tag = $this->systemTagManager->getTag('files_accesscontrol_intergrationtest', true, true); + $this->systemTagManager->deleteTags([$tag->getId()]); + } catch (TagNotFoundException) { + } + + return new DataResponse(); + } + + /** + * @return DataResponse + */ + public function prepare(): DataResponse { + try { + $tag = $this->systemTagManager->getTag('files_accesscontrol_intergrationtest', true, true); + } catch (TagNotFoundException) { + $tag = $this->systemTagManager->createTag('files_accesscontrol_intergrationtest', true, true); + } + return new DataResponse(['tagId' => $tag->getId()]); + } + + /** + * @NoAdminRequired + * @return DataResponse + */ + public function tagFile(string $path): DataResponse { + $tag = $this->systemTagManager->getTag('files_accesscontrol_intergrationtest', true, true); + + $userFolder = $this->rootFolder->getUserFolder($this->userId); + $node = $userFolder->get($path); + $this->systemTagObjectMapper->assignTags((string)$node->getId(), 'files', [$tag->getId()]); + return new DataResponse(); } } diff --git a/tests/Integration/features/bootstrap/FeatureContext.php b/tests/Integration/features/bootstrap/FeatureContext.php index e513c63..16bbd5a 100644 --- a/tests/Integration/features/bootstrap/FeatureContext.php +++ b/tests/Integration/features/bootstrap/FeatureContext.php @@ -42,6 +42,9 @@ class FeatureContext implements Context { /** @var string */ protected $baseUrl; + protected string $tagId = ''; + protected array $createdUsers = []; + /** * FeatureContext constructor. */ @@ -55,29 +58,45 @@ class FeatureContext implements Context { * @AfterScenario */ public function cleanUpBetweenTests() { - // TODO: Remove all created tags? $this->setCurrentUser('admin'); $this->sendingTo('DELETE', '/apps/files_accesscontrol_testing'); $this->assertStatusCode($this->response, 200); + } - try { - $this->userDeletesFile('test1', 'folder', '/subdir'); - } catch (\Exception $e) { - } - try { - $this->userDeletesFile('test1', 'file', '/foobar.txt'); - } catch (\Exception $e) { - } - try { - $this->userDeletesFile('test1', 'file', '/definitely.notexe'); - } catch (\Exception $e) { - } - try { - $this->emptyTrashbin('test1'); - } catch (\Exception $e) { + /** + * @AfterScenario + */ + public function tearDown() { + foreach ($this->createdUsers as $user) { + $this->deleteUser($user); } } + /** + * @Given /^Ensure tag exists$/ + */ + public function createTag() { + $this->setCurrentUser('admin'); + $this->sendingTo('POST', '/apps/files_accesscontrol_testing'); + $this->assertStatusCode($this->response, 200); + + $ocsData = json_decode($this->response->getBody()->getContents(), true, flags: JSON_THROW_ON_ERROR); + $data = $ocsData['ocs']['data']; + $this->tagId = $data['tagId']; + } + + /** + * @Given /^user "([^"]*)" tags file "([^"]*)"$/ + */ + public function tagFile(string $user, string $path) { + // TODO: Remove all created tags? + $this->setCurrentUser($user); + $this->sendingToWith('POST', '/apps/files_accesscontrol_testing/tag-file', [ + 'path' => $path, + ]); + $this->assertStatusCode($this->response, 200); + } + /** * @Given /^user "([^"]*)" creates (global|user) flow with (\d+)$/ */ @@ -89,6 +108,7 @@ class FeatureContext implements Context { $checks = []; foreach ($formData as $key => $value) { if (strpos($key, 'checks-') === 0) { + $value = str_replace('{{{FILES_ACCESSCONTROL_INTEGRATIONTEST_TAGID}}}', $this->tagId, $value); $checks[] = json_decode($value, true); unset($formData[$key]); } @@ -195,6 +215,26 @@ class FeatureContext implements Context { ]; $client->get($userProvisioningUrl . '/' . $user, $options2); + $this->createdUsers[] = $user; + $this->currentUser = $previous_user; + } + + private function deleteUser(string $user): void { + $previous_user = $this->currentUser; + $this->currentUser = 'admin'; + + $userProvisioningUrl = $this->baseUrl . 'ocs/v2.php/cloud/users/' . $user; + $client = new Client(); + $options = [ + 'auth' => ['admin', 'admin'], + 'headers' => [ + 'OCS-APIREQUEST' => 'true', + ], + ]; + $client->delete($userProvisioningUrl, $options); + + unset($this->createdUsers[$user]); + $this->currentUser = $previous_user; } diff --git a/tests/Integration/features/sharing-user.feature b/tests/Integration/features/sharing-user.feature index 986a83d..577988b 100644 --- a/tests/Integration/features/sharing-user.feature +++ b/tests/Integration/features/sharing-user.feature @@ -84,7 +84,7 @@ Feature: Sharing user When User "test2" deletes file "/subdir/foobar.txt" Then The webdav response should have a status code "403" - Scenario: Upload and share a file that is allowed by mimetype exludes + Scenario: Upload and share a file that is allowed by mimetype excludes And user "admin" creates global flow with 200 | name | Admin flow | | class | OCA\FilesAccessControl\Operation | @@ -102,7 +102,7 @@ Feature: Sharing user And Downloading file "/nextcloud.pdf" as "test2" And The webdav response should have a status code "200" - Scenario: Share a file that is allowed by mimetype exludes + Scenario: Share a file that is allowed by mimetype excludes Given User "test1" uploads file "data/nextcloud.pdf" to "/nextcloud2.pdf" And The webdav response should have a status code "201" And user "test1" shares file "/nextcloud2.pdf" with user "test2" @@ -117,4 +117,58 @@ Feature: Sharing user | checks-0 | {"class":"OCA\\WorkflowEngine\\Check\\FileMimeType", "operator": "!is", "value": "httpd/directory"} | | checks-1 | {"class":"OCA\\WorkflowEngine\\Check\\FileMimeType", "operator": "!is", "value": "application/pdf"} | And Downloading file "/nextcloud2.pdf" as "test2" - And The webdav response should have a status code "200" \ No newline at end of file + And The webdav response should have a status code "200" + + Scenario: Jailed storage cache bug blocking first + Given Ensure tag exists + Given User "test1" uploads file "data/textfile.txt" to "/nextcloud2.txt" + And The webdav response should have a status code "201" + Given User "test1" uploads file "data/textfile.txt" to "/nextcloud3.txt" + And The webdav response should have a status code "201" + And user "test1" shares file "/nextcloud2.txt" with user "test2" + And Downloading file "/nextcloud2.txt" as "test2" + And The webdav response should have a status code "200" + And user "test1" shares file "/nextcloud3.txt" with user "test2" + And Downloading file "/nextcloud3.txt" as "test2" + And The webdav response should have a status code "200" + And user "test1" tags file "/nextcloud2.txt" + When user "admin" creates global flow with 200 + | name | Admin flow | + | class | OCA\FilesAccessControl\Operation | + | entity | OCA\WorkflowEngine\Entity\File | + | events | [] | + | operation | deny | + | checks-0 | {"class":"OCA\\WorkflowEngine\\Check\\FileSystemTags", "operator": "is", "value": "{{{FILES_ACCESSCONTROL_INTEGRATIONTEST_TAGID}}}"} | + Then Downloading file "/nextcloud2.txt" as "test2" + And The webdav response should have a status code "404" + And Downloading file "/nextcloud3.txt" as "test2" + And The webdav response should have a status code "200" + And user "test2" should see following elements + | /nextcloud3.txt | + + Scenario: Jailed storage cache bug blocking last + Given Ensure tag exists + Given User "test1" uploads file "data/textfile.txt" to "/nextcloud2.txt" + And The webdav response should have a status code "201" + Given User "test1" uploads file "data/textfile.txt" to "/nextcloud3.txt" + And The webdav response should have a status code "201" + And user "test1" shares file "/nextcloud2.txt" with user "test2" + And Downloading file "/nextcloud2.txt" as "test2" + And The webdav response should have a status code "200" + And user "test1" shares file "/nextcloud3.txt" with user "test2" + And Downloading file "/nextcloud3.txt" as "test2" + And The webdav response should have a status code "200" + And user "test1" tags file "/nextcloud3.txt" + When user "admin" creates global flow with 200 + | name | Admin flow | + | class | OCA\FilesAccessControl\Operation | + | entity | OCA\WorkflowEngine\Entity\File | + | events | [] | + | operation | deny | + | checks-0 | {"class":"OCA\\WorkflowEngine\\Check\\FileSystemTags", "operator": "is", "value": "{{{FILES_ACCESSCONTROL_INTEGRATIONTEST_TAGID}}}"} | + Then Downloading file "/nextcloud2.txt" as "test2" + And The webdav response should have a status code "200" + And Downloading file "/nextcloud3.txt" as "test2" + And The webdav response should have a status code "404" + And user "test2" should see following elements + | /nextcloud2.txt |