diff --git a/.gitmodules b/.gitmodules index a4bea14d38..15c60d9713 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "app/console"] path = app/console url = https://github.com/appwrite/console - branch = 2.3.3 + branch = 2.3.4 diff --git a/CHANGES.md b/CHANGES.md index 21501b1bd5..df4bc7105e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,10 @@ +# Version 1.3.2 + +## Bugs +- Fixed auto-setting custom ID on nested documents [#5363](https://github.com/appwrite/appwrite/pull/5363) +- Fixed listDocuments not returning all the documents [#5395](https://github.com/appwrite/appwrite/pull/5395) +- Fixed deleting keys, webhooks, platforms and domains after deleting project [#5395](https://github.com/appwrite/appwrite/pull/5395) + # Version 1.3.1 ## Bugs diff --git a/README-CN.md b/README-CN.md index bfbd98fb74..8233cfe873 100644 --- a/README-CN.md +++ b/README-CN.md @@ -67,7 +67,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.3.1 + appwrite/appwrite:1.3.2 ``` ### Windows @@ -79,7 +79,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.3.1 + appwrite/appwrite:1.3.2 ``` #### PowerShell @@ -89,7 +89,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.3.1 + appwrite/appwrite:1.3.2 ``` 运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。 diff --git a/README.md b/README.md index 962fab5e01..7b05cac9bc 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.3.1 + appwrite/appwrite:1.3.2 ``` ### Windows @@ -88,7 +88,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.3.1 + appwrite/appwrite:1.3.2 ``` #### PowerShell @@ -98,7 +98,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.3.1 + appwrite/appwrite:1.3.2 ``` Once the Docker installation is complete, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after completing the installation. diff --git a/app/console b/app/console index ac4181aea4..9174d8f8cb 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit ac4181aea403d888e63cb527c700e80013c68ea8 +Subproject commit 9174d8f8cb584744dd7a53f69d324f490ee82ee3 diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index b309b74894..8419d0d437 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -743,7 +743,7 @@ App::post('/v1/databases/:databaseId/collections') ])); $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); - $dbForProject->createCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + $dbForProject->createCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), permissions: $permissions ?? [], documentSecurity: $documentSecurity); } catch (DuplicateException) { throw new Exception(Exception::COLLECTION_ALREADY_EXISTS); } catch (LimitException) { @@ -1001,6 +1001,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') ->setAttribute('documentSecurity', $documentSecurity) ->setAttribute('enabled', $enabled) ->setAttribute('search', implode(' ', [$collectionId, $name]))); + $dbForProject->updateCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $permissions, $documentSecurity); } catch (AuthorizationException) { throw new Exception(Exception::USER_UNAUTHORIZED); } catch (StructureException $exception) { @@ -2743,8 +2744,13 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') if (empty($related)) { continue; } - if (!\is_array($related)) { - $related = [$related]; + + $isList = \is_array($related) && \array_values($related) === $related; + + if ($isList) { + $relations = $related; + } else { + $relations = [$related]; } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); @@ -2752,7 +2758,15 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); - foreach ($related as $relation) { + foreach ($relations as &$relation) { + if ( + \is_array($relation) + && \array_values($relation) !== $relation + && !isset($relation['$id']) + ) { + $relation['$id'] = ID::unique(); + $relation = new Document($relation); + } if ($relation instanceof Document) { $current = Authorization::skip( fn() => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), $relation->getId()) @@ -2761,7 +2775,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') if ($current->isEmpty()) { $type = Database::PERMISSION_CREATE; - if (!isset($relation['$id']) || $relation['$id'] === 'unique()') { + if (isset($relation['$id']) && $relation['$id'] === 'unique()') { $relation['$id'] = ID::unique(); } } else { @@ -2774,6 +2788,12 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') $checkPermissions($relatedCollection, $relation, $type); } } + + if ($isList) { + $document->setAttribute($relationship->getAttribute('key'), \array_values($relations)); + } else { + $document->setAttribute($relationship->getAttribute('key'), \reset($relations)); + } } }; @@ -2866,12 +2886,19 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $collection = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); - if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { - if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { - throw new Exception(Exception::COLLECTION_NOT_FOUND); + if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { + 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')) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + // Validate queries $queriesValidator = new Documents($collection->getAttribute('attributes'), $collection->getAttribute('indexes')); $validQueries = $queriesValidator->isValid($queries); @@ -2898,34 +2925,16 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $filterQueries = Query::groupByType($queries)['filters']; - $documents = Authorization::skip(fn () => $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries)); - - $documentSecurity = $collection->getAttribute('documentSecurity', false); - $validator = new Authorization(Database::PERMISSION_READ); - $valid = $validator->isValid($collection->getRead()); - if (!$valid) { - $total = $documentSecurity - ? $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $filterQueries, APP_LIMIT_COUNT) - : 0; - } else { - $total = Authorization::skip(fn() => $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $filterQueries, APP_LIMIT_COUNT)); - } + $documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries); + $total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $filterQueries, APP_LIMIT_COUNT); // Add $collectionId and $databaseId for all documents $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database): bool { - $documentSecurity = $collection->getAttribute('documentSecurity', false); - $validator = new Authorization(Database::PERMISSION_READ); - - $valid = $validator->isValid($collection->getRead()); - if (!$documentSecurity && !$valid) { - return false; - } - - $valid = $valid || $validator->isValid($document->getRead()); - if ($documentSecurity && !$valid) { + if ($document->isEmpty()) { return false; } + $document->removeAttribute('$collection'); $document->setAttribute('$databaseId', $database->getId()); $document->setAttribute('$collectionId', $collection->getId()); @@ -2969,18 +2978,10 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') }; // The linter is forcing this indentation - foreach ($documents as $index => $document) { - if (!$processDocument($collection, $document)) { - unset($documents[$index]); - - if ($valid) { - $total--; - } - } + foreach ($documents as $document) { + $processDocument($collection, $document); } - $documents = \array_values($documents); - $response->dynamic(new Document([ 'total' => $total, 'documents' => $documents, @@ -3035,7 +3036,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $queries = Query::parseQueries($queries); - $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId, $queries)); + $document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId, $queries); if ($document->isEmpty()) { throw new Exception(Exception::DOCUMENT_NOT_FOUND); @@ -3043,17 +3044,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen // Add $collectionId and $databaseId for all documents $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) { - $documentSecurity = $collection->getAttribute('documentSecurity', false); - $validator = new Authorization(Database::PERMISSION_READ); - - $valid = $validator->isValid($collection->getRead()); - if (!$documentSecurity && !$valid) { - throw new Exception(Exception::DOCUMENT_NOT_FOUND); - } - - $valid = $valid || $validator->isValid($document->getRead()); - if ($documentSecurity && !$valid) { - throw new Exception(Exception::DOCUMENT_NOT_FOUND); + if ($document->isEmpty()) { + return; } $document->setAttribute('$databaseId', $database->getId()); @@ -3287,7 +3279,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $permissions = $document->getPermissions() ?? []; } - $data = \array_merge($document->getArrayCopy(), $data); $data['$collection'] = $collection->getId(); // 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 @@ -3321,8 +3312,13 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum if (empty($related)) { continue; } - if (!\is_array($related)) { - $related = [$related]; + + $isList = \is_array($related) && \array_values($related) === $related; + + if ($isList) { + $relations = $related; + } else { + $relations = [$related]; } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); @@ -3330,7 +3326,15 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum fn() => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); - foreach ($related as $relation) { + foreach ($relations as &$relation) { + if ( + \is_array($relation) + && \array_values($relation) !== $relation + && !isset($relation['$id']) + ) { + $relation['$id'] = ID::unique(); + $relation = new Document($relation); + } if ($relation instanceof Document) { $oldDocument = Authorization::skip(fn() => $dbForProject->getDocument( 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), @@ -3340,7 +3344,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum if ($oldDocument->isEmpty()) { $type = Database::PERMISSION_CREATE; - if (!isset($relation['$id']) || $relation['$id'] === 'unique()') { + if (isset($relation['$id']) && $relation['$id'] === 'unique()') { $relation['$id'] = ID::unique(); } } else { @@ -3353,20 +3357,28 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $checkPermissions($relatedCollection, $relation, $oldDocument, $type); } } + + if ($isList) { + $document->setAttribute($relationship->getAttribute('key'), \array_values($relations)); + } else { + $document->setAttribute($relationship->getAttribute('key'), \reset($relations)); + } } }; $checkPermissions($collection, $newDocument, $document, Database::PERMISSION_UPDATE); + $newDocument = new Document(\array_merge($document->getArrayCopy(), $data)); + try { - $document = Authorization::skip(fn() => $dbForProject->withRequestTimestamp( + $document = $dbForProject->withRequestTimestamp( $requestTimestamp, fn () => $dbForProject->updateDocument( 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $document->getId(), $newDocument ) - )); + ); } catch (AuthorizationException) { throw new Exception(Exception::USER_UNAUTHORIZED); } catch (DuplicateException) { diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 44329724b1..64ce9c8a67 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -1237,7 +1237,7 @@ App::get('/v1/projects/:projectId/platforms') } $platforms = $dbForConsole->find('platforms', [ - Query::equal('projectId', [$project->getId()]), + Query::equal('projectInternalId', [$project->getInternalId()]), Query::limit(5000), ]); diff --git a/app/http.php b/app/http.php index 4a0f1046dd..7ee38958b9 100644 --- a/app/http.php +++ b/app/http.php @@ -115,12 +115,6 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) { if (!$dbForConsole->getCollection($key)->isEmpty()) { continue; } - /** - * Skip to prevent 0.16 migration issues. - */ - if (in_array($key, ['cache', 'variables']) && $dbForConsole->exists(App::getEnv('_APP_DB_SCHEMA', 'appwrite'), 'bucket_1')) { - continue; - } Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...'); diff --git a/app/init.php b/app/init.php index 02fd9246f7..a35da92d0c 100644 --- a/app/init.php +++ b/app/init.php @@ -101,7 +101,7 @@ const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours const APP_CACHE_BUSTER = 503; -const APP_VERSION_STABLE = '1.3.1'; +const APP_VERSION_STABLE = '1.3.2'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; diff --git a/app/tasks/install.php b/app/tasks/install.php index 3519b58d0b..0f15af2eda 100644 --- a/app/tasks/install.php +++ b/app/tasks/install.php @@ -95,9 +95,9 @@ $cli if (is_null($value)) { continue; } - foreach ($vars as &$var) { + foreach ($vars as $i => $var) { if ($var['name'] === $key) { - $var['default'] = $value; + $vars[$i]['default'] = $value; } } } @@ -114,9 +114,9 @@ $cli if (is_null($value)) { continue; } - foreach ($vars as &$var) { + foreach ($vars as $i => $var) { if ($var['name'] === $key) { - $var['default'] = $value; + $vars[$i]['default'] = $value; } } } @@ -146,7 +146,7 @@ $cli $input = []; - foreach ($vars as $key => $var) { + foreach ($vars as $var) { if (!empty($var['filter']) && ($interactive !== 'Y' || !Console::isInteractive())) { if ($data && $var['default'] !== null) { $input[$var['name']] = $var['default']; diff --git a/app/workers/deletes.php b/app/workers/deletes.php index 42ec0d2565..f27bc4feb9 100644 --- a/app/workers/deletes.php +++ b/app/workers/deletes.php @@ -9,7 +9,6 @@ use Utopia\Database\Document; use Utopia\Database\Query; use Appwrite\Resque\Worker; use Executor\Executor; -use Utopia\Storage\Device\Local; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\TimeLimit; use Utopia\CLI\Console; @@ -291,12 +290,13 @@ class DeletesV1 extends Worker protected function deleteProject(Document $document): void { $projectId = $document->getId(); + $projectInternalId = $document->getInternalId(); - // Delete project domains and certificates + // Delete project certificates $dbForConsole = $this->getConsoleDB(); $domains = $dbForConsole->find('domains', [ - Query::equal('projectInternalId', [$document->getInternalId()]) + Query::equal('projectInternalId', [$projectInternalId]) ]); foreach ($domains as $domain) { @@ -318,6 +318,26 @@ class DeletesV1 extends Worker } } + // Delete Platforms + $this->deleteByGroup('platforms', [ + Query::equal('projectInternalId', [$projectInternalId]) + ], $dbForConsole); + + // Delete Domains + $this->deleteByGroup('domains', [ + Query::equal('projectInternalId', [$projectInternalId]) + ], $dbForConsole); + + // Delete Keys + $this->deleteByGroup('keys', [ + Query::equal('projectInternalId', [$projectInternalId]) + ], $dbForConsole); + + // Delete Webhooks + $this->deleteByGroup('webhooks', [ + Query::equal('projectInternalId', [$projectInternalId]) + ], $dbForConsole); + // Delete metadata tables try { $dbForProject->deleteCollection('_metadata'); diff --git a/composer.json b/composer.json index fdd5df4535..78f960d3d1 100644 --- a/composer.json +++ b/composer.json @@ -43,13 +43,13 @@ "ext-sockets": "*", "appwrite/php-clamav": "1.1.*", "appwrite/php-runtimes": "0.11.*", - "utopia-php/abuse": "0.24.*", + "utopia-php/abuse": "0.25.*", "utopia-php/analytics": "0.2.*", - "utopia-php/audit": "0.25.*", + "utopia-php/audit": "0.26.*", "utopia-php/cache": "0.8.*", "utopia-php/cli": "0.13.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "0.35.*", + "utopia-php/database": "0.36.*", "utopia-php/domains": "1.1.*", "utopia-php/framework": "0.28.*", "utopia-php/image": "0.5.*", diff --git a/composer.lock b/composer.lock index 57c0a09235..61826fd9b1 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": "87de4ea3130e576470a63b21628e30fb", + "content-hash": "169ab6e7dd540e45309d3c5093506fad", "packages": [ { "name": "adhocore/jwt", @@ -300,16 +300,16 @@ }, { "name": "colinmollenhour/credis", - "version": "v1.14.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/colinmollenhour/credis.git", - "reference": "dccc8a46586475075fbb012d8bd523b8a938c2dc" + "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/dccc8a46586475075fbb012d8bd523b8a938c2dc", - "reference": "dccc8a46586475075fbb012d8bd523b8a938c2dc", + "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/28810439de1d9597b7ba11794ed9479fb6f3de7c", + "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c", "shasum": "" }, "require": { @@ -341,9 +341,9 @@ "homepage": "https://github.com/colinmollenhour/credis", "support": { "issues": "https://github.com/colinmollenhour/credis/issues", - "source": "https://github.com/colinmollenhour/credis/tree/v1.14.0" + "source": "https://github.com/colinmollenhour/credis/tree/v1.15.0" }, - "time": "2022-11-09T01:18:39+00:00" + "time": "2023-04-18T15:34:23+00:00" }, { "name": "composer/package-versions-deprecated", @@ -481,22 +481,22 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.5.0", + "version": "7.5.1", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba" + "reference": "b964ca597e86b752cd994f27293e9fa6b6a95ed9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba", - "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b964ca597e86b752cd994f27293e9fa6b6a95ed9", + "reference": "b964ca597e86b752cd994f27293e9fa6b6a95ed9", "shasum": "" }, "require": { "ext-json": "*", "guzzlehttp/promises": "^1.5", - "guzzlehttp/psr7": "^1.9 || ^2.4", + "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -589,7 +589,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.5.0" + "source": "https://github.com/guzzle/guzzle/tree/7.5.1" }, "funding": [ { @@ -605,7 +605,7 @@ "type": "tidelift" } ], - "time": "2022-08-28T15:39:27+00:00" + "time": "2023-04-17T16:30:08+00:00" }, { "name": "guzzlehttp/promises", @@ -693,22 +693,22 @@ }, { "name": "guzzlehttp/psr7", - "version": "2.4.4", + "version": "2.5.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf" + "reference": "b635f279edd83fc275f822a1188157ffea568ff6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf", - "reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/b635f279edd83fc275f822a1188157ffea568ff6", + "reference": "b635f279edd83fc275f822a1188157ffea568ff6", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", "psr/http-factory": "^1.0", - "psr/http-message": "^1.0", + "psr/http-message": "^1.1 || ^2.0", "ralouphie/getallheaders": "^3.0" }, "provide": { @@ -728,9 +728,6 @@ "bamarni-bin": { "bin-links": true, "forward-command": false - }, - "branch-alias": { - "dev-master": "2.4-dev" } }, "autoload": { @@ -792,7 +789,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.4.4" + "source": "https://github.com/guzzle/psr7/tree/2.5.0" }, "funding": [ { @@ -808,7 +805,7 @@ "type": "tidelift" } ], - "time": "2023-03-09T13:19:02+00:00" + "time": "2023-04-17T16:11:26+00:00" }, { "name": "influxdb/influxdb-php", @@ -1372,16 +1369,16 @@ }, { "name": "psr/http-message", - "version": "1.1", + "version": "2.0", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", - "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba" + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba", - "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", "shasum": "" }, "require": { @@ -1390,7 +1387,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -1405,7 +1402,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for HTTP messages", @@ -1419,9 +1416,9 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/1.1" + "source": "https://github.com/php-fig/http-message/tree/2.0" }, - "time": "2023-04-04T09:50:52+00:00" + "time": "2023-04-04T09:54:51+00:00" }, { "name": "psr/log", @@ -1808,26 +1805,26 @@ }, { "name": "utopia-php/abuse", - "version": "0.24.0", + "version": "0.25.0", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "403641f16a53b81ac40b91111a86e5672da49e8c" + "reference": "49a180cab5316cddec9676d900d5112d03e97ffc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/403641f16a53b81ac40b91111a86e5672da49e8c", - "reference": "403641f16a53b81ac40b91111a86e5672da49e8c", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/49a180cab5316cddec9676d900d5112d03e97ffc", + "reference": "49a180cab5316cddec9676d900d5112d03e97ffc", "shasum": "" }, "require": { "ext-curl": "*", "ext-pdo": "*", "php": ">=8.0", - "utopia-php/database": "0.35.*" + "utopia-php/database": "0.36.*" }, "require-dev": { - "laravel/pint": "1.2.*", + "laravel/pint": "1.5.*", "phpstan/phpstan": "^1.9", "phpunit/phpunit": "^9.4" }, @@ -1851,9 +1848,9 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.24.0" + "source": "https://github.com/utopia-php/abuse/tree/0.25.0" }, - "time": "2023-04-11T05:31:55+00:00" + "time": "2023-04-27T15:43:47+00:00" }, { "name": "utopia-php/analytics", @@ -1912,24 +1909,24 @@ }, { "name": "utopia-php/audit", - "version": "0.25.0", + "version": "0.26.0", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "adc209f2e16878e5468f0b9cfd9f7f7ab497db31" + "reference": "e7228080f14df28737fbb050c180c26d86ac0403" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/adc209f2e16878e5468f0b9cfd9f7f7ab497db31", - "reference": "adc209f2e16878e5468f0b9cfd9f7f7ab497db31", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/e7228080f14df28737fbb050c180c26d86ac0403", + "reference": "e7228080f14df28737fbb050c180c26d86ac0403", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "0.35.*" + "utopia-php/database": "0.36.*" }, "require-dev": { - "laravel/pint": "1.2.*", + "laravel/pint": "1.5.*", "phpstan/phpstan": "^1.8", "phpunit/phpunit": "^9.3" }, @@ -1953,9 +1950,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.25.0" + "source": "https://github.com/utopia-php/audit/tree/0.26.0" }, - "time": "2023-04-11T05:31:15+00:00" + "time": "2023-04-27T15:43:50+00:00" }, { "name": "utopia-php/cache", @@ -2112,16 +2109,16 @@ }, { "name": "utopia-php/database", - "version": "0.35.0", + "version": "0.36.1", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "f162c142fd61753c4b413b15c3c4041f3cd00bb2" + "reference": "f6ab65e59a199da5155c114564077b1ab8c4daef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/f162c142fd61753c4b413b15c3c4041f3cd00bb2", - "reference": "f162c142fd61753c4b413b15c3c4041f3cd00bb2", + "url": "https://api.github.com/repos/utopia-php/database/zipball/f6ab65e59a199da5155c114564077b1ab8c4daef", + "reference": "f6ab65e59a199da5155c114564077b1ab8c4daef", "shasum": "" }, "require": { @@ -2164,9 +2161,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.35.0" + "source": "https://github.com/utopia-php/database/tree/0.36.1" }, - "time": "2023-04-11T04:02:22+00:00" + "time": "2023-04-27T08:39:55+00:00" }, { "name": "utopia-php/domains", @@ -3787,16 +3784,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.18.1", + "version": "1.20.3", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f" + "reference": "6c04009f6cae6eda2f040745b6b846080ef069c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/22dcdfd725ddf99583bfe398fc624ad6c5004a0f", - "reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6c04009f6cae6eda2f040745b6b846080ef069c2", + "reference": "6c04009f6cae6eda2f040745b6b846080ef069c2", "shasum": "" }, "require": { @@ -3826,9 +3823,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.18.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.3" }, - "time": "2023-04-07T11:51:11+00:00" + "time": "2023-04-25T09:01:03+00:00" }, { "name": "phpunit/php-code-coverage", diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index 4a79ffafbf..1017728eee 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -37,6 +37,9 @@ abstract class Migration */ protected Database $consoleDB; + /** + * @var \PDO + */ protected \PDO $pdo; /** @@ -54,6 +57,7 @@ abstract class Migration '1.2.1' => 'V17', '1.3.0' => 'V18', '1.3.1' => 'V18', + '1.3.2' => 'V18', ]; /** @@ -102,6 +106,12 @@ abstract class Migration return $this; } + /** + * Set PDO for Migration. + * + * @param \PDO $pdo + * @return \Appwrite\Migration\Migration + */ public function setPDO(\PDO $pdo): self { $this->pdo = $pdo; diff --git a/src/Appwrite/Migration/Version/V18.php b/src/Appwrite/Migration/Version/V18.php index eba19fa861..1cbb337c04 100644 --- a/src/Appwrite/Migration/Version/V18.php +++ b/src/Appwrite/Migration/Version/V18.php @@ -6,6 +6,8 @@ use Appwrite\Migration\Migration; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Helpers\Permission; +use Utopia\Database\Helpers\Role; class V18 extends Migration { @@ -25,6 +27,7 @@ class V18 extends Migration Console::log('Migrating Project: ' . $this->project->getAttribute('name') . ' (' . $this->project->getId() . ')'); $this->projectDB->setNamespace("_{$this->project->getInternalId()}"); + $this->addDocumentSecurityToProject(); Console::info('Migrating Databases'); $this->migrateDatabases(); @@ -58,6 +61,15 @@ class V18 extends Migration } $this->changeAttributeInternalType($collectionTable, $attribute['key'], 'DOUBLE'); } + + try { + $documentSecurity = $collection->getAttribute('documentSecurity', false); + $permissions = $collection->getPermissions(); + + $this->projectDB->updateCollection($collectionTable, $permissions, $documentSecurity); + } catch (\Throwable $th) { + Console::warning($th->getMessage()); + } } } } @@ -81,6 +93,12 @@ class V18 extends Migration $this->changeAttributeInternalType($id, $attribute['$id'], 'DOUBLE'); } + try { + $this->projectDB->updateCollection($id, [Permission::create(Role::any())], true); + } catch (\Throwable $th) { + Console::warning($th->getMessage()); + } + switch ($id) { case 'users': try { @@ -168,4 +186,25 @@ class V18 extends Migration return $document; } + + protected function addDocumentSecurityToProject(): void + { + try { + /** + * Create 'documentSecurity' column + */ + $this->pdo->prepare("ALTER TABLE `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}__metadata` ADD COLUMN IF NOT EXISTS documentSecurity TINYINT(1);")->execute(); + } catch (\Throwable $th) { + Console::warning($th->getMessage()); + } + + try { + /** + * Set 'documentSecurity' column to 1 if NULL + */ + $this->pdo->prepare("UPDATE `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}__metadata` SET documentSecurity = 1 WHERE documentSecurity IS NULL")->execute(); + } catch (\Throwable $th) { + Console::warning($th->getMessage()); + } + } } diff --git a/src/Appwrite/Utopia/Response/Model/Team.php b/src/Appwrite/Utopia/Response/Model/Team.php index 62df089438..d080a82bb1 100644 --- a/src/Appwrite/Utopia/Response/Model/Team.php +++ b/src/Appwrite/Utopia/Response/Model/Team.php @@ -4,6 +4,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; +use Utopia\Database\Document; class Team extends Model { @@ -49,6 +50,24 @@ class Team extends Model ; } + /** + * Process Document before returning it to the client + * + * @return Document + */ + public function filter(Document $document): Document + { + $prefs = $document->getAttribute('prefs'); + if ($prefs instanceof Document) { + $prefs = $prefs->getArrayCopy(); + } + + if (is_array($prefs) && empty($prefs)) { + $document->setAttribute('prefs', new \stdClass()); + } + return $document; + } + /** * Get Name * diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 42f83ea046..abff3437b8 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -2853,9 +2853,7 @@ trait DatabasesBase ]); // Current user has no collection permissions and document permissions are disabled - $this->assertEquals(200, $documentsUser2['headers']['status-code']); - $this->assertEquals(0, $documentsUser2['body']['total']); - $this->assertEquals(true, empty($documentsUser2['body']['documents'])); + $this->assertEquals(404, $documentsUser2['headers']['status-code']); // Enable document permissions $collection = $this->client->call(CLient::METHOD_PUT, '/databases/' . $databaseId . '/collections/' . $collectionId, [ @@ -3339,6 +3337,26 @@ trait DatabasesBase $this->assertEquals('Library 1', $person1['body']['library']['libraryName']); + // Create without nested ID + $person2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $person['body']['$id'] . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => ID::unique(), + 'data' => [ + 'library' => [ + 'libraryName' => 'Library 2', + ], + ], + 'permissions' => [ + Permission::read(Role::user($this->getUser()['$id'])), + Permission::update(Role::user($this->getUser()['$id'])), + Permission::delete(Role::user($this->getUser()['$id'])), + ] + ]); + + $this->assertEquals('Library 2', $person2['body']['library']['libraryName']); + // Ensure IDs were set and internal IDs removed $this->assertEquals($databaseId, $person1['body']['$databaseId']); $this->assertEquals($databaseId, $person1['body']['library']['$databaseId']); @@ -3901,7 +3919,7 @@ trait DatabasesBase ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(1, count($response['body']['documents'])); + $this->assertEquals(2, count($response['body']['documents'])); $this->assertEquals(null, $response['body']['documents'][0]['fullName']); $this->assertArrayNotHasKey("libraries", $response['body']['documents'][0]); } diff --git a/tests/e2e/Services/Databases/DatabasesPermissionsTeamTest.php b/tests/e2e/Services/Databases/DatabasesPermissionsTeamTest.php index 1bb42d8cb6..dcbf3e4bff 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->assertCount(0, $documents['body']['documents']); + $this->assertEquals(404, $documents['headers']['status-code']); } }