diff --git a/README-CN.md b/README-CN.md index cdc5d90bfc..5c4ce54433 100644 --- a/README-CN.md +++ b/README-CN.md @@ -1,6 +1,3 @@ -

- 🌍 其他语言 -


Appwrite Logo @@ -19,6 +16,8 @@ [![翻译](https://img.shields.io/badge/translate-f02e65?style=flat-square)](docs/tutorials/add-translations.md) [![周边商店](https://img.shields.io/badge/swag%20store-f02e65?style=flat-square)](https://store.appwrite.io) +[English](README.md) | 简体中文 + Appwrite是一个基于dcoker的端到端开发者平台,其容器化的微服务库可应用于网页端,移动端,以及后端。Appwrite 通过视觉化界面极简了从零编写 API 的繁琐过程,在保证软件安全的前提下为开发者创造了一个高效的开发环境。 Appwrite 可以提供给开发者用户验证,外部授权,用户数据读写检索,文件储存, 图像处理,云函数计算,[等多种服务](https:/ /appwrite.io/docs)。 @@ -169,8 +168,4 @@ Appwrite API 界面层利用后台缓存和任务委派来提供极速的响应 ## 版权说明 -版权详情,访问 [BSD 3-Clause License](./LICENSE)。 - -## 其他语言 -- [English](README.md) -- [简体中文](README-CN.md) \ No newline at end of file +版权详情,访问 [BSD 3-Clause License](./LICENSE)。 \ No newline at end of file diff --git a/README.md b/README.md index 71196afdc2..3d1a0db74f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,3 @@ -

- 🌍 Translations -


Appwrite Logo @@ -19,6 +16,8 @@ [![Translate](https://img.shields.io/badge/translate-f02e65?style=flat-square)](docs/tutorials/add-translations.md) [![Swag Store](https://img.shields.io/badge/swag%20store-f02e65?style=flat-square)](https://store.appwrite.io) +English | [简体中文](README-CN.md) + [**Appwrite 0.12 has been released! Learn what's new!**](https://dev.to/appwrite/its-here-announcing-the-release-of-appwrite-012-5c8b) Appwrite is an end-to-end backend server for Web, Mobile, Native, or Backend apps packaged as a set of Docker microservices. Appwrite abstracts the complexity and repetitiveness required to build a modern backend API from scratch and allows you to build secure apps faster. @@ -172,8 +171,4 @@ Join our growing community around the world! See our official [Blog](https://med ## License -This repository is available under the [BSD 3-Clause License](./LICENSE). - -## Translations -- [English](README.md) -- [简体中文](README-CN.md) +This repository is available under the [BSD 3-Clause License](./LICENSE). \ No newline at end of file diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 9d5ac716b3..da3cfc68b0 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -166,8 +166,6 @@ App::post('/v1/database/collections') $collectionId = $collectionId == 'unique()' ? $dbForProject->getId() : $collectionId; try { - $dbForProject->createCollection('collection_' . $collectionId); - $collection = $dbForProject->createDocument('collections', new Document([ '$id' => $collectionId, '$read' => $read ?? [], // Collection permissions for collection documents (based on permission model) @@ -179,8 +177,12 @@ App::post('/v1/database/collections') 'name' => $name, 'search' => implode(' ', [$collectionId, $name]), ])); + + $dbForProject->createCollection('collection_' . $collectionId); } catch (DuplicateException $th) { throw new Exception('Collection already exists', 409); + } catch (LimitException $th) { + throw new Exception('Collection limit exceeded', 400); } $audits diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index d9809bf305..0a5133d6b9 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -16,6 +16,7 @@ use Utopia\Validator\ArrayList; use Utopia\Validator\WhiteList; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Exception\Authorization as AuthorizationException; use Utopia\Database\Exception\Duplicate; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; @@ -761,7 +762,11 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') throw new Exception('Team not found', 404); } - if (!$dbForProject->deleteDocument('memberships', $membership->getId())) { + try { + $dbForProject->deleteDocument('memberships', $membership->getId()); + } catch (AuthorizationException $exception) { + throw new Exception('Unauthorized permissions', 401); + } catch (\Exception $exception) { throw new Exception('Failed to remove membership from DB', 500); } @@ -782,7 +787,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') if ($membership->getAttribute('confirm')) { // Count only confirmed members $team->setAttribute('sum', \max($team->getAttribute('sum', 0) - 1, 0)); - $team = $dbForProject->updateDocument('teams', $team->getId(), $team); + Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team)); } $audits diff --git a/composer.lock b/composer.lock index bf92c77845..2ae45c3928 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": "368924595cfbf7dc32394c09481df6c3", + "content-hash": "d9d66a3e700bc3936fe9a09f3c1b38f9", "packages": [ { "name": "adhocore/jwt", @@ -2141,16 +2141,16 @@ }, { "name": "utopia-php/database", - "version": "0.14.0", + "version": "0.14.1", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "2f2527bb080cf578fba327ea2ec637064561d403" + "reference": "ecc143f2cfe16b23675407035c6b5375ba263285" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/2f2527bb080cf578fba327ea2ec637064561d403", - "reference": "2f2527bb080cf578fba327ea2ec637064561d403", + "url": "https://api.github.com/repos/utopia-php/database/zipball/ecc143f2cfe16b23675407035c6b5375ba263285", + "reference": "ecc143f2cfe16b23675407035c6b5375ba263285", "shasum": "" }, "require": { @@ -2198,9 +2198,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.14.0" + "source": "https://github.com/utopia-php/database/tree/0.14.1" }, - "time": "2022-01-21T16:34:34+00:00" + "time": "2022-01-25T13:01:20+00:00" }, { "name": "utopia-php/domains", @@ -3126,23 +3126,23 @@ }, { "name": "composer/pcre", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "3d322d715c43a1ac36c7fe215fa59336265500f2" + "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/3d322d715c43a1ac36c7fe215fa59336265500f2", - "reference": "3d322d715c43a1ac36c7fe215fa59336265500f2", + "url": "https://api.github.com/repos/composer/pcre/zipball/67a32d7d6f9f560b726ab25a061b38ff3a80c560", + "reference": "67a32d7d6f9f560b726ab25a061b38ff3a80c560", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpstan/phpstan": "^1", + "phpstan/phpstan": "^1.3", "phpstan/phpstan-strict-rules": "^1.1", "symfony/phpunit-bridge": "^4.2 || ^5" }, @@ -3177,7 +3177,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/1.0.0" + "source": "https://github.com/composer/pcre/tree/1.0.1" }, "funding": [ { @@ -3193,7 +3193,7 @@ "type": "tidelift" } ], - "time": "2021-12-06T15:17:27+00:00" + "time": "2022-01-21T20:24:37+00:00" }, { "name": "composer/semver", @@ -3697,6 +3697,9 @@ "require": { "php": "^7.1 || ^8.0" }, + "replace": { + "myclabs/deep-copy": "self.version" + }, "require-dev": { "doctrine/collections": "^1.0", "doctrine/common": "^2.6", @@ -6662,5 +6665,5 @@ "platform-overrides": { "php": "8.0" }, - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.1.0" } diff --git a/tests/e2e/Services/Database/DatabaseCustomServerTest.php b/tests/e2e/Services/Database/DatabaseCustomServerTest.php index b770b2b6c3..18542961db 100644 --- a/tests/e2e/Services/Database/DatabaseCustomServerTest.php +++ b/tests/e2e/Services/Database/DatabaseCustomServerTest.php @@ -169,6 +169,21 @@ class DatabaseCustomServerTest extends Scope ]); $this->assertEquals($response['headers']['status-code'], 400); + + // This collection already exists + $response = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'name' => 'Test 1', + 'collectionId' => 'first', + 'read' => ['role:all'], + 'write' => ['role:all'], + 'permission' => 'document' + ]); + + $this->assertEquals($response['headers']['status-code'], 409); } public function testDeleteAttribute(): array diff --git a/tests/e2e/Services/Teams/TeamsBaseClient.php b/tests/e2e/Services/Teams/TeamsBaseClient.php index 89ac02da74..e03ce89a9c 100644 --- a/tests/e2e/Services/Teams/TeamsBaseClient.php +++ b/tests/e2e/Services/Teams/TeamsBaseClient.php @@ -391,11 +391,75 @@ trait TeamsBaseClient { $teamUid = $data['teamUid'] ?? ''; $membershipUid = $data['membershipUid'] ?? ''; + $session = $data['session'] ?? ''; + + $response = $this->client->call(Client::METHOD_GET, '/teams/'.$teamUid.'/memberships', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(2, $response['body']['sum']); + $ownerMembershipUid = $response['body']['memberships'][0]['$id']; + + /** + * Test for FAILURE + */ + + /** + * Test deleting a membership that does not exists + */ + $response = $this->client->call(Client::METHOD_DELETE, '/teams/'.$teamUid.'/memberships/dne', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_'.$this->getProject()['$id'].'='.$session, + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + /** + * Test deleting another user's membership + */ + $response = $this->client->call(Client::METHOD_DELETE, '/teams/'.$teamUid.'/memberships/'.$ownerMembershipUid, [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_'.$this->getProject()['$id'].'='.$session, + ]); + + $this->assertEquals(401, $response['headers']['status-code']); + /** * Test for SUCCESS */ - $response = $this->client->call(Client::METHOD_DELETE, '/teams/'.$teamUid.'/memberships/'.$membershipUid, array_merge([ + + /** + * Test for when a user other than the owner tries to delete their membership + */ + $response = $this->client->call(Client::METHOD_DELETE, '/teams/'.$teamUid.'/memberships/'.$membershipUid, [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_'.$this->getProject()['$id'].'='.$session, + ]); + + $this->assertEquals(204, $response['headers']['status-code']); + $this->assertEmpty($response['body']); + + $response = $this->client->call(Client::METHOD_GET, '/teams/'.$teamUid.'/memberships', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(1, $response['body']['sum']); + + /** + * Test for when the owner tries to delete their membership + */ + $response = $this->client->call(Client::METHOD_DELETE, '/teams/'.$teamUid.'/memberships/'.$ownerMembershipUid, array_merge([ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -404,10 +468,7 @@ trait TeamsBaseClient $this->assertEquals(204, $response['headers']['status-code']); $this->assertEmpty($response['body']); - /** - * Test for FAILURE - */ - $response = $this->client->call(Client::METHOD_GET, '/teams/'.$teamUid.'/memberships/'.$membershipUid, array_merge([ + $response = $this->client->call(Client::METHOD_GET, '/teams/'.$teamUid.'/memberships/'.$ownerMembershipUid, array_merge([ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'],