diff --git a/lib/Capabilities.php b/lib/Capabilities.php index 7818b5c2a..f80adc6b5 100644 --- a/lib/Capabilities.php +++ b/lib/Capabilities.php @@ -46,7 +46,7 @@ public function __construct(IAppManager $appManager, LoggerInterface $logger, IC /** * - * @return array{tables: array{enabled: bool, version: string, apiVersions: string[], column_types: string[]}} + * @return array{tables: array{enabled: bool, version: string, apiVersions: string[], features: string[], column_types: string[]}} * * @inheritDoc */ diff --git a/lib/Command/RenameTable.php b/lib/Command/RenameTable.php index f2a3f57bf..3a41153b5 100644 --- a/lib/Command/RenameTable.php +++ b/lib/Command/RenameTable.php @@ -45,7 +45,7 @@ public function __construct(TableService $tableService, LoggerInterface $logger) protected function configure(): void { $this ->setName('tables:update') - ->setAliases('tables:rename') + ->setAliases(['tables:rename']) ->setDescription('Rename a table.') ->addArgument( 'ID', diff --git a/lib/Controller/ApiFavoriteController.php b/lib/Controller/ApiFavoriteController.php index df36b54b3..6c08cec6a 100644 --- a/lib/Controller/ApiFavoriteController.php +++ b/lib/Controller/ApiFavoriteController.php @@ -32,7 +32,7 @@ public function __construct( } /** - * [api v2] Create a new table and return it + * [api v2] Add a node (table or view) to user favorites * * @NoAdminRequired * @@ -57,7 +57,7 @@ public function create(int $nodeType, int $nodeId): DataResponse { /** - * [api v2] Delete a table + * [api v2] Remove a node (table or view) to from favorites * * @NoAdminRequired * diff --git a/lib/Service/FavoritesService.php b/lib/Service/FavoritesService.php index 40a24e44b..8a9a7b6da 100644 --- a/lib/Service/FavoritesService.php +++ b/lib/Service/FavoritesService.php @@ -34,9 +34,19 @@ class FavoritesService { + private IDBConnection $connection; + private PermissionsService $permissionsService; + private ?string $userId; private CappedMemoryCache $cache; - public function __construct(private IDBConnection $connection, private ?string $userId) { + public function __construct( + IDBConnection $connection, + PermissionsService $permissionsService, + ?string $userId + ) { + $this->connection = $connection; + $this->permissionsService = $permissionsService; + $this->userId = $userId; $this->cache = new CappedMemoryCache(); } @@ -116,21 +126,13 @@ private function checkValidNodeType(int $nodeType): void { /** * @throws PermissionError - * @throws NotFoundError - * @throws InternalError */ private function checkAccessToNode(int $nodeType, int $nodeId): void { - if ($nodeType === Application::NODE_TYPE_TABLE) { - //$this->tableService->find($nodeId, false, $this->userId); - return; - } - - if ($nodeType === Application::NODE_TYPE_VIEW) { - //$this->viewService->find($nodeId, false, $this->userId); + if ($this->permissionsService->canAccessNodeById($nodeType, $nodeId)) { return; } - throw new InternalError('Invalid node type'); + throw new PermissionError('Invalid node type and id'); } } diff --git a/lib/Service/PermissionsService.php b/lib/Service/PermissionsService.php index 8b3c851de..989c463cf 100644 --- a/lib/Service/PermissionsService.php +++ b/lib/Service/PermissionsService.php @@ -2,6 +2,7 @@ namespace OCA\Tables\Service; +use OCA\Tables\AppInfo\Application; use OCA\Tables\Db\Share; use OCA\Tables\Db\ShareMapper; use OCA\Tables\Db\Table; @@ -95,6 +96,17 @@ public function canUpdateTable(Table $table, ?string $userId = null): bool { return $this->canManageTable($table, $userId); } + public function canAccessNodeById(int $nodeType, int $nodeId, ?string $userId = null): bool { + if ($nodeType === Application::NODE_TYPE_TABLE) { + return $this->canReadColumnsByTableId($nodeId, $this->userId); + } + if ($nodeType === Application::NODE_TYPE_VIEW) { + return $this->canReadColumnsByViewId($nodeId, $this->userId); + } + + return false; + } + public function canAccessView(View $view, ?string $userId = null): bool { if($this->basisCheck($view, 'view', $userId)) { return true; diff --git a/tests/integration/features/APIv2.feature b/tests/integration/features/APIv2.feature index 7afbabba7..1848ce77e 100644 --- a/tests/integration/features/APIv2.feature +++ b/tests/integration/features/APIv2.feature @@ -2,6 +2,7 @@ Feature: APIv2 Background: Given user "participant1-v2" exists Given user "participant2-v2" exists + Given user "participant3-v2" exists @api2 Scenario: Test initial setup @@ -15,11 +16,37 @@ Feature: APIv2 Given table "Table 1 via api v2" with emoji "👋" exists for user "participant1-v2" as "t1" via v2 Then user "participant1-v2" has the following tables via v2 | Table 1 via api v2 | + And user "participant1-v2" sees the following table attributes on table "t1" + | archived | 0 | Then user "participant1-v2" updates table "t1" set title "updated title" and emoji "⛵︎" via v2 Then user "participant1-v2" has the following tables via v2 | updated title | + Then user "participant1-v2" updates table "t1" set archived 1 via v2 + And user "participant1-v2" sees the following table attributes on table "t1" + | archived | 1 | + Then user "participant1-v2" updates table "t1" set archived 0 via v2 + And user "participant1-v2" sees the following table attributes on table "t1" + | archived | 0 | Then user "participant1-v2" deletes table "t1" via v2 + @api2 + Scenario: Favorite tables + Given table "Own table" with emoji "👋" exists for user "participant1-v2" as "t1" via v2 + And user "participant1-v2" shares table with user "participant2-v2" + And user "participant1-v2" adds the table "t1" to favorites + And user "participant1-v2" sees the following table attributes on table "t1" + | favorite | 1 | + And user "participant2-v2" fetches table info for table "t1" + And user "participant2-v2" sees the following table attributes on table "t1" + | favorite | 0 | + When user "participant1-v2" removes the table "t1" from favorites + And user "participant1-v2" sees the following table attributes on table "t1" + | favorite | 0 | + When user "participant3-v2" adds the table "t1" to favorites + Then the last response should have a "403" status code + + + @api2 Scenario: Basic column actions Given table "Table 2" with emoji "👋" exists for user "participant1-v2" as "t2" via v2 diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index 959cbc870..7a4757e24 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -70,6 +70,10 @@ class FeatureContext implements Context { private array $viewIds = []; private array $columnIds = []; + // Store data from last request to perform assertions, id is used as a key + private array $tableData = []; + private array $viewData = []; + // use CommandLineTrait; /** @@ -127,16 +131,30 @@ public function createTableV2(string $user, string $title, string $tableName, st Assert::assertEquals($newTable['emoji'], $emoji); Assert::assertEquals($newTable['ownership'], $user); + $tableToVerify = $this->userFetchesTableInfo($user, $tableName); + Assert::assertEquals(200, $this->response->getStatusCode()); + Assert::assertEquals($tableToVerify['title'], $title); + Assert::assertEquals($tableToVerify['emoji'], $emoji); + Assert::assertEquals($tableToVerify['ownership'], $user); + } + + /** + * @Given user :user fetches table info for table :tableName + */ + public function userFetchesTableInfo($user, $tableName) { + $this->setCurrentUser($user); + $tableId = $this->tableIds[$tableName]; + $this->sendOcsRequest( 'GET', - '/apps/tables/api/2/tables/'.$newTable['id'], + '/apps/tables/api/2/tables/'.$tableId, ); $tableToVerify = $this->getDataFromResponse($this->response)['ocs']['data']; - Assert::assertEquals(200, $this->response->getStatusCode()); - Assert::assertEquals($tableToVerify['title'], $title); - Assert::assertEquals($tableToVerify['emoji'], $emoji); - Assert::assertEquals($tableToVerify['ownership'], $user); + $this->tableData[$tableName] = $tableToVerify; + $this->tableId = $tableToVerify['id']; + + return $tableToVerify; } /** @@ -223,6 +241,7 @@ public function initialResourcesV2(string $user, TableNode $body = null): void { /** * @Then user :user updates table :tableName set title :title and emoji :emoji via v2 + * @Then user :user updates table :tableName set archived :archived via v2 * * @param string $user * @param string $title @@ -230,13 +249,26 @@ public function initialResourcesV2(string $user, TableNode $body = null): void { * @param string $tableName * @throws Exception */ - public function updateTableV2(string $user, string $title, ?string $emoji, string $tableName): void { + public function updateTableV2(string $user, string $tableName, string $title = null, ?string $emoji = null, ?bool $archived = null): void { $this->setCurrentUser($user); - $data = ['title' => $title]; + $this->sendOcsRequest( + 'GET', + '/apps/tables/api/2/tables/'.$this->tableIds[$tableName], + ); + + $previousData = $this->getDataFromResponse($this->response)['ocs']['data']; + + $data = []; + if ($title !== null) { + $data['title'] = $title; + } if ($emoji !== null) { $data['emoji'] = $emoji; } + if ($archived !== null) { + $data['archived'] = $archived; + } $this->sendOcsRequest( 'PUT', @@ -247,9 +279,10 @@ public function updateTableV2(string $user, string $title, ?string $emoji, strin $updatedTable = $this->getDataFromResponse($this->response)['ocs']['data']; Assert::assertEquals(200, $this->response->getStatusCode()); - Assert::assertEquals($updatedTable['title'], $title); - Assert::assertEquals($updatedTable['emoji'], $emoji); - Assert::assertEquals($updatedTable['ownership'], $user); + Assert::assertEquals($updatedTable['title'], $title ?? $previousData['title']); + Assert::assertEquals($updatedTable['emoji'], $emoji ?? $previousData['emoji']); + Assert::assertEquals($updatedTable['ownership'], $user ?? $previousData['ownership']); + Assert::assertEquals($updatedTable['archived'], $archived ?? $previousData['archived']); $this->sendOcsRequest( 'GET', @@ -258,9 +291,12 @@ public function updateTableV2(string $user, string $title, ?string $emoji, strin $tableToVerify = $this->getDataFromResponse($this->response)['ocs']['data']; Assert::assertEquals(200, $this->response->getStatusCode()); - Assert::assertEquals($tableToVerify['title'], $title); - Assert::assertEquals($tableToVerify['emoji'], $emoji); - Assert::assertEquals($tableToVerify['ownership'], $user); + Assert::assertEquals($tableToVerify['title'], $title ?? $previousData['title']); + Assert::assertEquals($tableToVerify['emoji'], $emoji ?? $previousData['emoji']); + Assert::assertEquals($tableToVerify['ownership'], $user ?? $previousData['ownership']); + Assert::assertEquals($tableToVerify['archived'], $archived ?? $previousData['archived']); + + $this->tableData[$tableName] = $tableToVerify; } /** @@ -1634,4 +1670,59 @@ protected function assertStatusCode(ResponseInterface $response, int $statusCode Assert::assertEquals($statusCode, $response->getStatusCode(), $message); } } + + /** + * @Given user :user sees the following table attributes on table :tableName + */ + public function userSeesTheFollowingTableAttributesOnTable($user, $tableName, TableNode $table) { + foreach ($table->getRows() as $row) { + $attribute = $row[0]; + $value = $row[1]; + if (in_array($attribute, ['archived', 'favorite'])) { + $value = (bool)$value; + } + Assert::assertEquals($value, $this->tableData[$tableName][$attribute]); + } + } + + /** + * @Given user :user adds the table :tableName to favorites + */ + public function userAddsTheTableToFavorites($user, $tableName) { + $this->setCurrentUser($user); + $nodeType = 0; + $tableId = $this->tableIds[$tableName]; + + $this->sendOcsRequest( + 'POST', + '/apps/tables/api/2/favorites/' . $nodeType. '/' . $tableId, + ); + if ($this->response->getStatusCode() === 200) { + $this->userFetchesTableInfo($user, $tableName); + } + } + + /** + * @Given user :user removes the table :tableName from favorites + */ + public function userRemovesTheTableFromFavorites($user, $tableName) { + $this->setCurrentUser($user); + $nodeType = 0; + $tableId = $this->tableIds[$tableName]; + + $this->sendOcsRequest( + 'DELETE', + '/apps/tables/api/2/favorites/' . $nodeType. '/' . $tableId, + ); + if ($this->response->getStatusCode() === 200) { + $this->userFetchesTableInfo($user, $tableName); + } + } + + /** + * @Then /^the last response should have a "([^"]*)" status code$/ + */ + public function theLastResponseShouldHaveAStatusCode(int $statusCode) { + Assert::assertEquals($statusCode, $this->response->getStatusCode()); + } }