From aa9431025978d933a74eff9573c4aa023517d9ed Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 28 Jul 2023 11:37:14 -0400 Subject: [PATCH 1/8] Fix missing admin mode checks for collections --- app/controllers/api/databases.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 2589cc4c05..670e8be776 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -2670,7 +2670,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); - if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { + if ($collection->isEmpty() || (!$collection->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } @@ -2898,7 +2898,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') } } - if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { + if ($collection->isEmpty() || (!$collection->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } @@ -3023,7 +3023,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); - if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { + if ($collection->isEmpty() || (!$collection->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } @@ -3235,7 +3235,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); - if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { + if ($collection->isEmpty() || (!$collection->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } @@ -3473,7 +3473,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); - if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { + if ($collection->isEmpty() || (!$collection->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } From b615065fd2104c494be911c47459c829eaa86764 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 28 Jul 2023 11:51:11 -0400 Subject: [PATCH 2/8] Add tests for collection disabled --- .../Databases/DatabasesConsoleClientTest.php | 77 ++++++------------- 1 file changed, 25 insertions(+), 52 deletions(-) diff --git a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php b/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php index 13d28df5f3..860bb170e6 100644 --- a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php +++ b/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php @@ -29,6 +29,7 @@ class DatabasesConsoleClientTest extends Scope $this->assertTrue($database['body']['enabled']); $databaseId = $database['body']['$id']; + /** * Test for SUCCESS */ @@ -51,7 +52,7 @@ class DatabasesConsoleClientTest extends Scope $this->assertEquals($movies['body']['name'], 'Movies'); /** - * Test When database is disabled but can still create collections + * Test when database is disabled but can still create collections */ $database = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId, array_merge([ 'content-type' => 'application/json', @@ -78,6 +79,17 @@ class DatabasesConsoleClientTest extends Scope 'documentSecurity' => true, ]); + /** + * Test when collection is disabled but can still modify collections + */ + $database = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $movies['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Movies', + 'enabled' => false, + ]); + $this->assertEquals(201, $tvShows['headers']['status-code']); $this->assertEquals($tvShows['body']['name'], 'TvShows'); @@ -87,11 +99,12 @@ class DatabasesConsoleClientTest extends Scope /** * @depends testCreateCollection * @param array $data + * @throws \Exception */ public function testListCollection(array $data) { /** - * Test When database is disabled but can still call list collections + * Test when database is disabled but can still call list collections */ $databaseId = $data['databaseId']; @@ -108,6 +121,8 @@ class DatabasesConsoleClientTest extends Scope /** * @depends testCreateCollection * @param array $data + * @throws \Exception + * @throws \Exception */ public function testGetCollection(array $data) { @@ -115,7 +130,7 @@ class DatabasesConsoleClientTest extends Scope $moviesCollectionId = $data['moviesId']; /** - * Test When database is disabled but can still call get collection + * Test when database and collection are disabled but can still call get collection */ $collection = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $moviesCollectionId, array_merge([ 'content-type' => 'application/json', @@ -125,12 +140,14 @@ class DatabasesConsoleClientTest extends Scope $this->assertEquals(200, $collection['headers']['status-code']); $this->assertEquals('Movies', $collection['body']['name']); $this->assertEquals($moviesCollectionId, $collection['body']['$id']); - $this->assertTrue($collection['body']['enabled']); + $this->assertFalse($collection['body']['enabled']); } /** * @depends testCreateCollection * @param array $data + * @throws \Exception + * @throws \Exception */ public function testUpdateCollection(array $data) { @@ -138,7 +155,7 @@ class DatabasesConsoleClientTest extends Scope $moviesCollectionId = $data['moviesId']; /** - * Test When database is disabled but can still call update collection + * Test When database and collection are disabled but can still call update collection */ $collection = $this->client->call(Client::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $moviesCollectionId, array_merge([ 'content-type' => 'application/json', @@ -157,6 +174,8 @@ class DatabasesConsoleClientTest extends Scope /** * @depends testCreateCollection * @param array $data + * @throws \Exception + * @throws \Exception */ public function testDeleteCollection(array $data) { @@ -164,7 +183,7 @@ class DatabasesConsoleClientTest extends Scope $tvShowsId = $data['tvShowsId']; /** - * Test When database is disabled but can still call Delete collection + * Test when database and collection are disabled but can still call delete collection */ $response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $tvShowsId, array_merge([ 'content-type' => 'application/json', @@ -175,52 +194,6 @@ class DatabasesConsoleClientTest extends Scope $this->assertEquals($response['body'], ""); } - /** - * @depends testCreateCollection - */ - // public function testGetDatabaseUsage(array $data) - // { - // $databaseId = $data['databaseId']; - // /** - // * Test for FAILURE - // */ - - // $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/usage', array_merge([ - // 'content-type' => 'application/json', - // 'x-appwrite-project' => $this->getProject()['$id'] - // ], $this->getHeaders()), [ - // 'range' => '32h' - // ]); - - // $this->assertEquals(400, $response['headers']['status-code']); - - // /** - // * Test for SUCCESS - // */ - - // $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/usage', array_merge([ - // 'content-type' => 'application/json', - // 'x-appwrite-project' => $this->getProject()['$id'] - // ], $this->getHeaders()), [ - // 'range' => '24h' - // ]); - - // $this->assertEquals(200, $response['headers']['status-code']); - // $this->assertEquals(count($response['body']), 11); - // $this->assertEquals($response['body']['range'], '24h'); - // $this->assertIsArray($response['body']['documentsCount']); - // $this->assertIsArray($response['body']['collectionsCount']); - // $this->assertIsArray($response['body']['documentsCreate']); - // $this->assertIsArray($response['body']['documentsRead']); - // $this->assertIsArray($response['body']['documentsUpdate']); - // $this->assertIsArray($response['body']['documentsDelete']); - // $this->assertIsArray($response['body']['collectionsCreate']); - // $this->assertIsArray($response['body']['collectionsRead']); - // $this->assertIsArray($response['body']['collectionsUpdate']); - // $this->assertIsArray($response['body']['collectionsDelete']); - // } - - /** * @depends testCreateCollection */ From ff83751450c16c5d7efb6e9c3c50267028908e1d Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 31 Jul 2023 14:24:21 -0400 Subject: [PATCH 3/8] Allow privileged user/app user/admin mode access to all resources when disabled --- app/controllers/api/databases.php | 84 +++++++++++++++++++++---------- app/controllers/api/functions.php | 18 +++++-- app/controllers/api/storage.php | 80 +++++++++++++++++++++++------ app/controllers/shared/api.php | 10 +++- 4 files changed, 145 insertions(+), 47 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 670e8be776..44ddd68e9f 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -727,7 +727,7 @@ App::post('/v1/databases/:databaseId/collections') $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); - if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { + if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } @@ -790,7 +790,7 @@ App::get('/v1/databases/:databaseId/collections') $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); - if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { + if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } @@ -846,7 +846,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId') $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); - if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { + if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } @@ -986,7 +986,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); - if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { + if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } @@ -1052,7 +1052,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId') $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); - if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { + if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); } @@ -2664,14 +2664,20 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); - if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { - throw new Exception(Exception::DATABASE_NOT_FOUND); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if ($database->isEmpty() || !$database->getAttribute('enabled')) { + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } } $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); - if ($collection->isEmpty() || (!$collection->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { - if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { + if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } } @@ -2883,13 +2889,19 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); - if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { - throw new Exception(Exception::DATABASE_NOT_FOUND); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if ($database->isEmpty() || !$database->getAttribute('enabled')) { + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } } $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); - if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { if (!$collection->getAttribute('documentSecurity', false)) { $validator = new Authorization(Database::PERMISSION_READ); if (!$validator->isValid($collection->getRead())) { @@ -2898,8 +2910,10 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') } } - if ($collection->isEmpty() || (!$collection->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } } // Validate queries @@ -3017,14 +3031,20 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); - if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { - throw new Exception(Exception::DATABASE_NOT_FOUND); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if ($database->isEmpty() || !$database->getAttribute('enabled')) { + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } } $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); - if ($collection->isEmpty() || (!$collection->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { - if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { + if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } } @@ -3229,14 +3249,20 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); - if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { - throw new Exception(Exception::DATABASE_NOT_FOUND); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if ($database->isEmpty() || !$database->getAttribute('enabled')) { + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } } $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); - if ($collection->isEmpty() || (!$collection->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { - if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { + if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } } @@ -3467,14 +3493,20 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); - if ($database->isEmpty() || (!$database->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { - throw new Exception(Exception::DATABASE_NOT_FOUND); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if ($database->isEmpty() || !$database->getAttribute('enabled')) { + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } } $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); - if ($collection->isEmpty() || (!$collection->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { - if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { + if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } } diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index a51e2fa92f..8a2ffd60ce 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -1038,7 +1038,11 @@ App::post('/v1/functions/:functionId/executions') $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); if ($function->isEmpty() || !$function->getAttribute('enabled')) { - if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { + $isAdminMode = $mode === APP_MODE_ADMIN; + $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { throw new Exception(Exception::FUNCTION_NOT_FOUND); } } @@ -1233,7 +1237,11 @@ App::get('/v1/functions/:functionId/executions') $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); if ($function->isEmpty() || !$function->getAttribute('enabled')) { - if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { + $isAdminMode = $mode === APP_MODE_ADMIN; + $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { throw new Exception(Exception::FUNCTION_NOT_FOUND); } } @@ -1305,7 +1313,11 @@ App::get('/v1/functions/:functionId/executions/:executionId') $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); if ($function->isEmpty() || !$function->getAttribute('enabled')) { - if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { + $isAdminMode = $mode === APP_MODE_ADMIN; + $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { throw new Exception(Exception::FUNCTION_NOT_FOUND); } } diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index fbd4a55424..c91dc89509 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -364,8 +364,14 @@ App::post('/v1/storage/buckets/:bucketId/files') $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { - throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { + $isAdminMode = $mode === APP_MODE_ADMIN; + $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + } } $validator = new Authorization(Database::PERMISSION_CREATE); @@ -692,8 +698,14 @@ App::get('/v1/storage/buckets/:bucketId/files') $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { - throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { + $isAdminMode = $mode === APP_MODE_ADMIN; + $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + } } $fileSecurity = $bucket->getAttribute('fileSecurity', false); @@ -768,8 +780,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId') $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { - throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { + $isAdminMode = $mode === APP_MODE_ADMIN; + $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + } } $fileSecurity = $bucket->getAttribute('fileSecurity', false); @@ -837,8 +855,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { - throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { + $isAdminMode = $mode === APP_MODE_ADMIN; + $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + } } $fileSecurity = $bucket->getAttribute('fileSecurity', false); @@ -983,8 +1007,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { - throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { + $isAdminMode = $mode === APP_MODE_ADMIN; + $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + } } $fileSecurity = $bucket->getAttribute('fileSecurity', false); @@ -1123,8 +1153,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { - throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { + $isAdminMode = $mode === APP_MODE_ADMIN; + $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + } } $fileSecurity = $bucket->getAttribute('fileSecurity', false); @@ -1282,8 +1318,14 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { - throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { + $isAdminMode = $mode === APP_MODE_ADMIN; + $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + } } $fileSecurity = $bucket->getAttributes('fileSecurity', false); @@ -1387,8 +1429,14 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $events, string $mode, Device $deviceFiles, Delete $deletes) { $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { - throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { + $isAdminMode = $mode === APP_MODE_ADMIN; + $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + } } $fileSecurity = $bucket->getAttributes('fileSecurity', false); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index c89bf0252e..2d8dd7a1c9 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -231,8 +231,14 @@ App::init() $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) { - throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { + $isAdminMode = $mode === APP_MODE_ADMIN; + $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + } } $fileSecurity = $bucket->getAttribute('fileSecurity', false); From b19691463836b3dffa56557842c25fcb05a0870b Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 8 Aug 2023 15:46:01 -0400 Subject: [PATCH 4/8] Fix console/API key checks --- app/controllers/api/databases.php | 50 +++++++++++++++------------ app/controllers/api/functions.php | 21 +++++++----- app/controllers/api/storage.php | 56 ++++++++++++++++++------------- app/controllers/shared/api.php | 7 ++-- 4 files changed, 77 insertions(+), 57 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 44ddd68e9f..f51a3b80ac 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -2664,12 +2664,13 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isConsole = $isAdminMode && $isPrivilegedUser; if ($database->isEmpty() || !$database->getAttribute('enabled')) { - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::DATABASE_NOT_FOUND); } } @@ -2677,7 +2678,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } } @@ -2889,19 +2890,20 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isConsole = $isAdminMode && $isPrivilegedUser; if ($database->isEmpty() || !$database->getAttribute('enabled')) { - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::DATABASE_NOT_FOUND); } } $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!($isAdminMode && ($isAPIKey || $isPrivilegedUser))) { if (!$collection->getAttribute('documentSecurity', false)) { $validator = new Authorization(Database::PERMISSION_READ); if (!$validator->isValid($collection->getRead())) { @@ -2911,7 +2913,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') } if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } } @@ -3031,12 +3033,14 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isConsole = $isAdminMode && $isPrivilegedUser; + if ($database->isEmpty() || !$database->getAttribute('enabled')) { - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::DATABASE_NOT_FOUND); } } @@ -3044,7 +3048,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } } @@ -3249,12 +3253,14 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isConsole = $isAdminMode && $isPrivilegedUser; + if ($database->isEmpty() || !$database->getAttribute('enabled')) { - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::DATABASE_NOT_FOUND); } } @@ -3262,7 +3268,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } } @@ -3493,12 +3499,14 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isConsole = $isAdminMode && $isPrivilegedUser; + if ($database->isEmpty() || !$database->getAttribute('enabled')) { - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::DATABASE_NOT_FOUND); } } @@ -3506,7 +3514,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } } diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 8a2ffd60ce..57f2cfbb8c 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -1038,11 +1038,12 @@ App::post('/v1/functions/:functionId/executions') $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); if ($function->isEmpty() || !$function->getAttribute('enabled')) { - $isAdminMode = $mode === APP_MODE_ADMIN; - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isConsole = $isAdminMode && $isPrivilegedUser; - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::FUNCTION_NOT_FOUND); } } @@ -1237,11 +1238,12 @@ App::get('/v1/functions/:functionId/executions') $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); if ($function->isEmpty() || !$function->getAttribute('enabled')) { - $isAdminMode = $mode === APP_MODE_ADMIN; - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isConsole = $isAdminMode && $isPrivilegedUser; - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::FUNCTION_NOT_FOUND); } } @@ -1313,11 +1315,12 @@ App::get('/v1/functions/:functionId/executions/:executionId') $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); if ($function->isEmpty() || !$function->getAttribute('enabled')) { - $isAdminMode = $mode === APP_MODE_ADMIN; - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isConsole = $isAdminMode && $isPrivilegedUser; - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::FUNCTION_NOT_FOUND); } } diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index c91dc89509..10ea5a9884 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -365,11 +365,12 @@ App::post('/v1/storage/buckets/:bucketId/files') $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { - $isAdminMode = $mode === APP_MODE_ADMIN; - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isConsole = $isAdminMode && $isPrivilegedUser; - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } } @@ -699,11 +700,12 @@ App::get('/v1/storage/buckets/:bucketId/files') $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { - $isAdminMode = $mode === APP_MODE_ADMIN; - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isConsole = $isAdminMode && $isPrivilegedUser; - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } } @@ -781,11 +783,12 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId') $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { - $isAdminMode = $mode === APP_MODE_ADMIN; - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isConsole = $isAdminMode && $isPrivilegedUser; - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } } @@ -856,11 +859,12 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { - $isAdminMode = $mode === APP_MODE_ADMIN; - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isConsole = $isAdminMode && $isPrivilegedUser; - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } } @@ -1008,11 +1012,12 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { - $isAdminMode = $mode === APP_MODE_ADMIN; - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isConsole = $isAdminMode && $isPrivilegedUser; - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } } @@ -1154,11 +1159,12 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { - $isAdminMode = $mode === APP_MODE_ADMIN; - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isConsole = $isAdminMode && $isPrivilegedUser; - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } } @@ -1319,11 +1325,12 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { - $isAdminMode = $mode === APP_MODE_ADMIN; - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isConsole = $isAdminMode && $isPrivilegedUser; - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } } @@ -1430,11 +1437,12 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { - $isAdminMode = $mode === APP_MODE_ADMIN; - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isConsole = $isAdminMode && $isPrivilegedUser; - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } } diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 2d8dd7a1c9..1b1f911010 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -232,11 +232,12 @@ App::init() $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { - $isAdminMode = $mode === APP_MODE_ADMIN; - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAdminMode = $mode === APP_MODE_ADMIN; + $isConsole = $isAdminMode && $isPrivilegedUser; - if (!($isAdminMode && ($isAppUser || $isPrivilegedUser))) { + if (!$isConsole && !$isAPIKey) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } } From 414d85be8e323f76e7086410ecbf53b448f6d032 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 16 Aug 2023 17:58:25 -0400 Subject: [PATCH 5/8] Update mode checks --- app/controllers/api/databases.php | 111 ++++++++---------------------- app/controllers/api/functions.php | 39 ++++------- app/controllers/api/storage.php | 101 +++++++++------------------ app/controllers/shared/api.php | 12 ++-- 4 files changed, 77 insertions(+), 186 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 825c149952..4a340af58c 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -2604,21 +2604,15 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isConsole = $isAdminMode && $isPrivilegedUser; - if ($database->isEmpty() || !$database->getAttribute('enabled')) { - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } + if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); - if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } + if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $allowedPermissions = [ @@ -2641,8 +2635,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') } // Users can only manage their own roles, API keys and Admin users can manage any - $roles = Authorization::getRoles(); - if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) { + if (!$isAPIKey && !$isPrivilegedUser) { foreach (Database::PERMISSIONS as $type) { foreach ($permissions as $permission) { $permission = Permission::parse($permission); @@ -2823,42 +2816,19 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') ->inject('dbForProject') ->inject('mode') ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, string $mode) { - $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isConsole = $isAdminMode && $isPrivilegedUser; - if ($database->isEmpty() || !$database->getAttribute('enabled')) { - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } + if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); - if (!($isAdminMode && ($isAPIKey || $isPrivilegedUser))) { - if (!$collection->getAttribute('documentSecurity', false)) { - $validator = new Authorization(Database::PERMISSION_READ); - if (!$validator->isValid($collection->getRead())) { - $collection = new Document(); - } - } - } - - if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } - } - - // Validate queries - $queriesValidator = new Documents($collection->getAttribute('attributes'), $collection->getAttribute('indexes')); - $validQueries = $queriesValidator->isValid($queries); - if (!$validQueries) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $queriesValidator->getDescription()); + if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $queries = Query::parseQueries($queries); @@ -2878,13 +2848,13 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $cursor->setValue($cursorDocument); } - $filterQueries = Query::groupByType($queries)['filters']; + $filters = Query::groupByType($queries)['filters']; $documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries); - $total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $filterQueries, APP_LIMIT_COUNT); + $total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $filters, APP_LIMIT_COUNT); // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database): bool { + $processDocument = (function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database): bool { if ($document->isEmpty()) { return false; } @@ -2929,12 +2899,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') } return true; - }; + }); // The linter is forcing this indentation - foreach ($documents as $document) { - $processDocument($collection, $document); - } + foreach ($documents as $document) { + $processDocument($collection, $document); + } $response->dynamic(new Document([ 'total' => $total, @@ -2964,27 +2934,19 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen ->inject('dbForProject') ->inject('mode') ->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, string $mode) { - $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isConsole = $isAdminMode && $isPrivilegedUser; - - if ($database->isEmpty() || !$database->getAttribute('enabled')) { - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } + if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); - if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } + if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); } // Validate queries @@ -3185,22 +3147,15 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isConsole = $isAdminMode && $isPrivilegedUser; - - if ($database->isEmpty() || !$database->getAttribute('enabled')) { - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } + if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); - if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } + if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); } // Read permission should not be required for update @@ -3220,7 +3175,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum // Users can only manage their own roles, API keys and Admin users can manage any $roles = Authorization::getRoles(); - if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles) && !\is_null($permissions)) { + if (!$isAPIKey && !$isPrivilegedUser && !\is_null($permissions)) { foreach (Database::PERMISSIONS as $type) { foreach ($permissions as $permission) { $permission = Permission::parse($permission); @@ -3424,27 +3379,19 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->inject('deletes') ->inject('mode') ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $events, Delete $deletes, string $mode) { - $database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId)); $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isConsole = $isAdminMode && $isPrivilegedUser; - - if ($database->isEmpty() || !$database->getAttribute('enabled')) { - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::DATABASE_NOT_FOUND); - } + if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); - if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); - } + if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); } // Read permission should not be required for delete diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 94bf716bfe..f7148ca93c 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -998,18 +998,13 @@ App::post('/v1/functions/:functionId/executions') ->inject('queueForFunctions') ->inject('queueForUsage') ->action(function (string $functionId, string $data, bool $async, Response $response, Document $project, Database $dbForProject, Document $user, Event $events, string $mode, Func $queueForFunctions, Usage $queueForUsage) { - $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); - if ($function->isEmpty() || !$function->getAttribute('enabled')) { - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isConsole = $isAdminMode && $isPrivilegedUser; + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::FUNCTION_NOT_FOUND); - } + if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $runtimes = Config::getParam('runtimes', []); @@ -1197,18 +1192,13 @@ App::get('/v1/functions/:functionId/executions') ->inject('dbForProject') ->inject('mode') ->action(function (string $functionId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) { - $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); - if ($function->isEmpty() || !$function->getAttribute('enabled')) { - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isConsole = $isAdminMode && $isPrivilegedUser; + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::FUNCTION_NOT_FOUND); - } + if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $queries = Query::parseQueries($queries); @@ -1274,18 +1264,13 @@ App::get('/v1/functions/:functionId/executions/:executionId') ->inject('dbForProject') ->inject('mode') ->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, string $mode) { - $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); - if ($function->isEmpty() || !$function->getAttribute('enabled')) { - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isConsole = $isAdminMode && $isPrivilegedUser; + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::FUNCTION_NOT_FOUND); - } + if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $execution = $dbForProject->getDocument('executions', $executionId); diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index d4d402d4d7..4d907e24ba 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -354,18 +354,13 @@ App::post('/v1/storage/buckets/:bucketId/files') ->inject('deviceFiles') ->inject('deviceLocal') ->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $events, string $mode, Device $deviceFiles, Device $deviceLocal) { - $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isConsole = $isAdminMode && $isPrivilegedUser; + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); - } + if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $validator = new Authorization(Database::PERMISSION_CREATE); @@ -689,18 +684,13 @@ App::get('/v1/storage/buckets/:bucketId/files') ->inject('dbForProject') ->inject('mode') ->action(function (string $bucketId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) { - $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isConsole = $isAdminMode && $isPrivilegedUser; + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); - } + if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); @@ -770,18 +760,13 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('dbForProject') ->inject('mode') ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, string $mode) { - $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isConsole = $isAdminMode && $isPrivilegedUser; + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); - } + if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); @@ -847,15 +832,11 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isConsole = $isAdminMode && $isPrivilegedUser; + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); - } + if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); @@ -998,15 +979,11 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isConsole = $isAdminMode && $isPrivilegedUser; + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); - } + if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); @@ -1140,18 +1117,13 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') ->inject('mode') ->inject('deviceFiles') ->action(function (string $bucketId, string $fileId, Response $response, Request $request, Database $dbForProject, string $mode, Device $deviceFiles) { - $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isConsole = $isAdminMode && $isPrivilegedUser; + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); - } + if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); @@ -1304,18 +1276,13 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('mode') ->inject('events') ->action(function (string $bucketId, string $fileId, ?string $name, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $events) { - $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isConsole = $isAdminMode && $isPrivilegedUser; + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); - } + if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttributes('fileSecurity', false); @@ -1417,15 +1384,11 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $events, string $mode, Device $deviceFiles, Delete $deletes) { $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isConsole = $isAdminMode && $isPrivilegedUser; + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); - } + if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttributes('fileSecurity', false); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index d1297b3458..f0a56b7c27 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -281,15 +281,11 @@ App::init() $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - if ($bucket->isEmpty() || !$bucket->getAttribute('enabled')) { - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAdminMode = $mode === APP_MODE_ADMIN; - $isConsole = $isAdminMode && $isPrivilegedUser; + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - if (!$isConsole && !$isAPIKey) { - throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); - } + if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); From 4b90517dfda81e67caa74a3e00dd115a3d3875da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 21 Aug 2023 23:11:34 +0200 Subject: [PATCH 6/8] Update console --- app/console | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/console b/app/console index 495a9c2530..e23424ed6a 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit 495a9c25302bb79156773ef0668b3eef07ce3dd1 +Subproject commit e23424ed6af1d96a169e87337f32d1d84d5e19f4 From 942705b5f3c47101da00ce8415c285c9e8d48804 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 21 Aug 2023 23:25:55 -0400 Subject: [PATCH 7/8] Fix database upgrade --- app/controllers/api/account.php | 4 +- app/controllers/api/databases.php | 137 +++++++----------- app/controllers/api/functions.php | 12 +- app/controllers/api/migrations.php | 4 +- app/controllers/api/projects.php | 4 +- app/controllers/api/storage.php | 8 +- app/controllers/api/teams.php | 8 +- app/controllers/api/users.php | 8 +- app/init.php | 4 +- composer.json | 2 +- composer.lock | 39 +++-- .../e2e/Services/Databases/DatabasesBase.php | 71 +++++++-- .../DatabasesPermissionsTeamTest.php | 2 +- 13 files changed, 180 insertions(+), 123 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 60b635ef3e..bf70cd44c4 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -900,7 +900,9 @@ App::get('/v1/account/identities') $queries[] = Query::equal('userInternalId', [$user->getInternalId()]); // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + }); $cursor = reset($cursor); if ($cursor) { /** @var Query $cursor */ diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 59a420620b..26c41726bf 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -26,6 +26,7 @@ use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\Restricted as RestrictedException; use Utopia\Database\Exception\Structure as StructureException; +use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -356,7 +357,7 @@ function updateAttribute( ); } - $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key, $attribute); + $attribute = $dbForProject->updateDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key, $attribute); $dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $collection->getId()); $events @@ -469,7 +470,9 @@ App::get('/v1/databases') } // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + }); $cursor = reset($cursor); if ($cursor) { $databaseId = $cursor->getValue(); @@ -790,7 +793,9 @@ App::get('/v1/databases/:databaseId/collections') } // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + }); $cursor = reset($cursor); if ($cursor) { /** @var Query $cursor */ @@ -1645,11 +1650,18 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') $queries = Query::parseQueries($queries); - \array_push($queries, Query::equal('collectionId', [$collectionId]), Query::equal('databaseId', [$databaseId])); + \array_push( + $queries, + Query::equal('collectionId', [$collectionId]), + Query::equal('databaseId', [$databaseId]) + ); // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); - $cursor = reset($cursor); + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + }); + + $cursor = \reset($cursor); if ($cursor) { $attributeId = $cursor->getValue(); @@ -1659,17 +1671,22 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') Query::equal('key', [$attributeId]), Query::limit(1), ])); + if (empty($cursorDocument) || $cursorDocument[0]->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Attribute '{$attributeId}' for the 'cursor' value not found."); } + $cursor->setValue($cursorDocument[0]); } - $filterQueries = Query::groupByType($queries)['filters']; + $filters = Query::groupByType($queries)['filters']; + + $attributes = $dbForProject->find('attributes', $queries); + $total = $dbForProject->count('attributes', $filters, APP_LIMIT_COUNT); $response->dynamic(new Document([ - 'total' => $dbForProject->count('attributes', $filterQueries, APP_LIMIT_COUNT), - 'attributes' => $dbForProject->find('attributes', $queries), + 'attributes' => $attributes, + 'total' => $total, ]), Response::MODEL_ATTRIBUTE_LIST); }); @@ -2474,7 +2491,9 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') \array_push($queries, Query::equal('collectionId', [$collectionId]), Query::equal('databaseId', [$databaseId])); // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + }); $cursor = reset($cursor); if ($cursor) { @@ -2693,7 +2712,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') $permission->getDimension() ))->toString(); if (!Authorization::isRole($role)) { - throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', Authorization::getRoles()) . ')'); } } } @@ -2879,8 +2898,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $queries = Query::parseQueries($queries); // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); - $cursor = reset($cursor); + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + }); + + $cursor = \reset($cursor); + if ($cursor) { $documentId = $cursor->getValue(); @@ -2895,8 +2918,14 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $filters = Query::groupByType($queries)['filters']; - $documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries); - $total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $filters, APP_LIMIT_COUNT); + try { + $documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries); + $total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $filters, APP_LIMIT_COUNT); + } catch (AuthorizationException) { + throw new Exception(Exception::USER_UNAUTHORIZED); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $e->getMessage()); + } // Add $collectionId and $databaseId for all documents $processDocument = (function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database): bool { @@ -2946,7 +2975,6 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') return true; }); - // The linter is forcing this indentation foreach ($documents as $document) { $processDocument($collection, $document); } @@ -2994,16 +3022,15 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen throw new Exception(Exception::COLLECTION_NOT_FOUND); } - // Validate queries - $queriesValidator = new DocumentQueriesValidator($collection->getAttribute('attributes')); - $validQueries = $queriesValidator->isValid($queries); - if (!$validQueries) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $queriesValidator->getDescription()); - } - $queries = Query::parseQueries($queries); - $document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId, $queries); + try { + $document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId, $queries); + } catch (AuthorizationException) { + throw new Exception(Exception::USER_UNAUTHORIZED); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $e->getMessage()); + } if ($document->isEmpty()) { throw new Exception(Exception::DOCUMENT_NOT_FOUND); @@ -3243,10 +3270,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $permissions = $document->getPermissions() ?? []; } - $data = \array_merge($document->getArrayCopy(), $data); // Merge existing data with new data - $data['$collection'] = $document->getAttribute('$collection'); // Make sure user doesn't switch collectionID - $data['$createdAt'] = $document->getCreatedAt(); // Make sure user doesn't switch createdAt - $data['$id'] = $document->getId(); // Make sure user doesn't switch document unique ID $data['$permissions'] = $permissions; $newDocument = new Document($data); @@ -3431,68 +3454,18 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu throw new Exception(Exception::DOCUMENT_NOT_FOUND); } - $checkPermissions = function (Document $collection, Document $document) use (&$checkPermissions, $dbForProject, $database) { - $documentSecurity = $collection->getAttribute('documentSecurity', false); - $validator = new Authorization(Database::PERMISSION_DELETE); - - $valid = $validator->isValid($collection->getDelete()); - if (!$documentSecurity && !$valid) { - throw new Exception(Exception::USER_UNAUTHORIZED); - } - - $valid = $valid || $validator->isValid($document->getDelete()); - if ($documentSecurity && !$valid) { - throw new Exception(Exception::USER_UNAUTHORIZED); - } - - $relationships = \array_filter( - $collection->getAttribute('attributes', []), - fn($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - $related = $document->getAttribute($relationship->getAttribute('key')); - - if (empty($related)) { - continue; - } - if (!\is_array($related)) { - $related = [$related]; - } - - $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = Authorization::skip( - fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) - ); - - foreach ($related as $relation) { - if ( - $relation instanceof Document - && $relationship->getAttribute('onDelete') === Database::RELATION_MUTATE_CASCADE - ) { - $checkPermissions($relatedCollection, $relation); - } - } - } - }; - - $checkPermissions($collection, $document); - - Authorization::skip(fn() => $dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $database, $collection, $documentId) { + $dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $database, $collection, $documentId) { try { $dbForProject->deleteDocument( 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId ); + } catch (AuthorizationException) { + throw new Exception(Exception::USER_UNAUTHORIZED); } catch (RestrictedException) { throw new Exception(Exception::DOCUMENT_DELETE_RESTRICTED); } - })); - - $dbForProject->deleteCachedDocument( - 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), - $documentId - ); + }); // Add $collectionId and $databaseId for all documents $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) { diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index f7148ca93c..9aa7c326b0 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -138,7 +138,9 @@ App::get('/v1/functions') } // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + }); $cursor = reset($cursor); if ($cursor) { /** @var Query $cursor */ @@ -778,7 +780,9 @@ App::get('/v1/functions/:functionId/deployments') $queries[] = Query::equal('resourceType', ['functions']); // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + }); $cursor = reset($cursor); if ($cursor) { /** @var Query $cursor */ @@ -1211,7 +1215,9 @@ App::get('/v1/functions/:functionId/executions') $queries[] = Query::equal('functionId', [$function->getId()]); // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + }); $cursor = reset($cursor); if ($cursor) { /** @var Query $cursor */ diff --git a/app/controllers/api/migrations.php b/app/controllers/api/migrations.php index 8d961a0450..8419463118 100644 --- a/app/controllers/api/migrations.php +++ b/app/controllers/api/migrations.php @@ -380,7 +380,9 @@ App::get('/v1/migrations') } // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + }); $cursor = reset($cursor); if ($cursor) { /** @var Query $cursor */ diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index aa70e389d9..71ca84f7d8 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -245,7 +245,9 @@ App::get('/v1/projects') } // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + }); $cursor = reset($cursor); if ($cursor) { /** @var Query $cursor */ diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 4d907e24ba..c3c3964fd3 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -166,7 +166,9 @@ App::get('/v1/storage/buckets') } // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + }); $cursor = reset($cursor); if ($cursor) { /** @var Query $cursor */ @@ -707,7 +709,9 @@ App::get('/v1/storage/buckets/:bucketId/files') } // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + }); $cursor = reset($cursor); if ($cursor) { /** @var Query $cursor */ diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index e03e481d7c..99783d5660 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -153,7 +153,9 @@ App::get('/v1/teams') } // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + }); $cursor = reset($cursor); if ($cursor) { /** @var Query $cursor */ @@ -649,7 +651,9 @@ App::get('/v1/teams/:teamId/memberships') $queries[] = Query::equal('teamId', [$teamId]); // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + }); $cursor = reset($cursor); if ($cursor) { /** @var Query $cursor */ diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index e3fcca9587..03ac414cda 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -396,7 +396,9 @@ App::get('/v1/users') } // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + }); $cursor = reset($cursor); if ($cursor) { /** @var Query $cursor */ @@ -656,7 +658,9 @@ App::get('/v1/users/identities') } // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + }); $cursor = reset($cursor); if ($cursor) { /** @var Query $cursor */ diff --git a/app/init.php b/app/init.php index 9741c72ed0..107abed5ca 100644 --- a/app/init.php +++ b/app/init.php @@ -286,7 +286,7 @@ Database::addFilter( return $value; }, function (mixed $value, Document $attribute) { - $formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true); + $formatOptions = \json_decode($attribute->getAttribute('formatOptions', '[]'), true); if (isset($formatOptions['elements'])) { $attribute->setAttribute('elements', $formatOptions['elements']); } @@ -356,7 +356,7 @@ Database::addFilter( ->find('indexes', [ Query::equal('collectionInternalId', [$document->getInternalId()]), Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), - Query::limit(64), + Query::limit($database->getLimitForIndexes()), ]); } ); diff --git a/composer.json b/composer.json index 2fd93cf510..4918b68bb9 100644 --- a/composer.json +++ b/composer.json @@ -50,7 +50,7 @@ "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", "utopia-php/database": "0.42.*", - "utopia-php/domains": "1.1.*", + "utopia-php/domains": "0.3.*", "utopia-php/dsn": "0.1.*", "utopia-php/framework": "0.28.*", "utopia-php/image": "0.5.*", diff --git a/composer.lock b/composer.lock index 5aed402688..2427078eb3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2098172fc4b71eb0d41dcdbfea2f5061", + "content-hash": "3c637b7058e55050f09ff2af72c11fdb", "packages": [ { "name": "adhocore/jwt", @@ -1557,19 +1557,20 @@ }, { "name": "utopia-php/database", - "version": "0.42.1", + "version": "0.42.3", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "9ff69a9b9eadc581771798833d423829c9d8cc90" + "reference": "ab0e2f8ad46884f69b354cd8ee84a1a75fee26d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/9ff69a9b9eadc581771798833d423829c9d8cc90", - "reference": "9ff69a9b9eadc581771798833d423829c9d8cc90", + "url": "https://api.github.com/repos/utopia-php/database/zipball/ab0e2f8ad46884f69b354cd8ee84a1a75fee26d1", + "reference": "ab0e2f8ad46884f69b354cd8ee84a1a75fee26d1", "shasum": "" }, "require": { + "ext-mbstring": "*", "ext-pdo": "*", "php": ">=8.0", "utopia-php/cache": "0.8.*", @@ -1607,29 +1608,31 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.42.1" + "source": "https://github.com/utopia-php/database/tree/0.42.3" }, - "time": "2023-08-14T16:09:09+00:00" + "time": "2023-08-22T02:15:28+00:00" }, { "name": "utopia-php/domains", - "version": "v1.1.0", + "version": "0.3.2", "source": { "type": "git", "url": "https://github.com/utopia-php/domains.git", - "reference": "1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73" + "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73", - "reference": "1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", + "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.0", + "utopia-php/framework": "0.*.*" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3" }, "type": "library", "autoload": { @@ -1645,6 +1648,10 @@ { "name": "Eldad Fux", "email": "eldad@appwrite.io" + }, + { + "name": "Wess Cope", + "email": "wess@appwrite.io" } ], "description": "Utopia Domains library is simple and lite library for parsing web domains. This library is aiming to be as simple and easy to learn and use.", @@ -1661,9 +1668,9 @@ ], "support": { "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/master" + "source": "https://github.com/utopia-php/domains/tree/0.3.2" }, - "time": "2020-02-23T07:40:02+00:00" + "time": "2023-07-19T16:39:24+00:00" }, { "name": "utopia-php/dsn", @@ -5377,5 +5384,5 @@ "platform-overrides": { "php": "8.0" }, - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.3.0" } diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index c2c1c70bd6..f982d25cca 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -344,6 +344,63 @@ trait DatabasesBase $this->assertEquals(400, $response['headers']['status-code']); } + public function testUpdateAttributeEnum(): void + { + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'Test Database 2' + ]); + + $players = $this->client->call(Client::METHOD_POST, '/databases/' . $database['body']['$id'] . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Players', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + ], + ]); + + // Create enum attribute + $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $database['body']['$id'] . '/collections/' . $players['body']['$id'] . '/attributes/enum', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'key' => 'position', + 'elements' => ['goalkeeper', 'defender', 'midfielder', 'forward'], + 'required' => true, + 'array' => false, + ]); + + $this->assertEquals(202, $attribute['headers']['status-code']); + $this->assertEquals($attribute['body']['key'], 'position'); + $this->assertEquals($attribute['body']['elements'], ['goalkeeper', 'defender', 'midfielder', 'forward']); + + \sleep(2); + + // Update enum attribute + $attribute = $this->client->call(Client::METHOD_PATCH, '/databases/' . $database['body']['$id'] . '/collections/' . $players['body']['$id'] . '/attributes/enum/' . $attribute['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'elements' => ['goalkeeper', 'defender', 'midfielder', 'forward', 'coach'], + 'required' => true, + 'default' => null + ]); + + $this->assertEquals(200, $attribute['headers']['status-code']); + $this->assertEquals($attribute['body']['elements'], ['goalkeeper', 'defender', 'midfielder', 'forward', 'coach']); + } + /** * @depends testCreateAttributes */ @@ -2862,8 +2919,6 @@ trait DatabasesBase $collectionId = $collection['body']['$id']; - sleep(2); - $attribute = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -2877,8 +2932,7 @@ trait DatabasesBase $this->assertEquals(202, $attribute['headers']['status-code'], 202); $this->assertEquals('attribute', $attribute['body']['key']); - // wait for db to add attribute - sleep(2); + \sleep(2); $index = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/indexes', array_merge([ 'content-type' => 'application/json', @@ -2893,8 +2947,7 @@ trait DatabasesBase $this->assertEquals(202, $index['headers']['status-code']); $this->assertEquals('key_attribute', $index['body']['key']); - // wait for db to add attribute - sleep(2); + \sleep(2); $document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([ 'content-type' => 'application/json', @@ -2993,7 +3046,7 @@ trait DatabasesBase 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2, ]); - // Current user has no collection permissions and document permissions are disabled + // other2 has no collection permissions and document permissions are disabled $this->assertEquals(404, $document3GetWithDocumentRead['headers']['status-code']); $documentsUser2 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [ @@ -3003,8 +3056,8 @@ trait DatabasesBase 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session2, ]); - // Current user has no collection permissions and document permissions are disabled - $this->assertEquals(404, $documentsUser2['headers']['status-code']); + // other2 has no collection permissions and document permissions are disabled + $this->assertEquals(401, $documentsUser2['headers']['status-code']); // Enable document permissions $collection = $this->client->call(CLient::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $collectionId, [ diff --git a/tests/e2e/Services/Databases/DatabasesPermissionsTeamTest.php b/tests/e2e/Services/Databases/DatabasesPermissionsTeamTest.php index dcbf3e4bff..8377b9c803 100644 --- a/tests/e2e/Services/Databases/DatabasesPermissionsTeamTest.php +++ b/tests/e2e/Services/Databases/DatabasesPermissionsTeamTest.php @@ -176,7 +176,7 @@ class DatabasesPermissionsTeamTest extends Scope if ($success) { $this->assertCount(1, $documents['body']['documents']); } else { - $this->assertEquals(404, $documents['headers']['status-code']); + $this->assertEquals(401, $documents['headers']['status-code']); } } From 6c67172fd76b89e1cc49e03713e60a8b8c1032c1 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 21 Aug 2023 23:45:32 -0400 Subject: [PATCH 8/8] Fix missing ID --- app/controllers/api/databases.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 26c41726bf..f2f28ff2a7 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -3270,6 +3270,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $permissions = $document->getPermissions() ?? []; } + $data['$id'] = $documentId; $data['$permissions'] = $permissions; $newDocument = new Document($data);