diff --git a/tests/e2e/Services/Database/DatabasePermissionsGuestTest.php b/tests/e2e/Services/Database/DatabasePermissionsGuestTest.php new file mode 100644 index 0000000000..1e6c6b7e18 --- /dev/null +++ b/tests/e2e/Services/Database/DatabasePermissionsGuestTest.php @@ -0,0 +1,80 @@ +client->call(Client::METHOD_POST, '/database/collections', $this->getServerHeader(), [ + 'collectionId' => 'unique()', + 'name' => 'Movies', + 'read' => ['role:all'], + 'write' => ['role:all'], + 'permission' => 'document', + ]); + + $collection = ['id' => $movies['body']['$id']]; + + $this->client->call(Client::METHOD_POST, '/database/collections/' . $collection['id'] . '/attributes/string', $this->getServerHeader(), [ + 'attributeId' => 'title', + 'size' => 256, + 'required' => true, + ]); + + sleep(2); + + return $collection; + } + + /** + * [string[] $read, string[] $write] + */ + public function readDocumentsProvider() + { + return [ + [['role:all'], []], + [['role:member'], []], + [[] ,['role:all']], + [['role:all'], ['role:all']], + [['role:member'], ['role:member']], + [['role:all'], ['role:member']], + ]; + } + + /** + * @dataProvider readDocumentsProvider + */ + public function testReadDocuments($read, $write) + { + $collection = $this->createCollection(); + + $response = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collection['id'] . '/documents', $this->getServerHeader(), [ + 'documentId' => 'unique()', + 'data' => [ + 'title' => 'Lorem', + ], + 'read' => $read, + 'write' => $write, + ]); + $this->assertEquals(201, $response['headers']['status-code']); + + $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collection['id'] . '/documents', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]); + + foreach ($documents['body']['documents'] as $document) { + $this->assertContains('role:all', $document['$read']); + } + } +} diff --git a/tests/e2e/Services/Database/DatabasePermissionsMemberTest.php b/tests/e2e/Services/Database/DatabasePermissionsMemberTest.php new file mode 100644 index 0000000000..e28fb559ac --- /dev/null +++ b/tests/e2e/Services/Database/DatabasePermissionsMemberTest.php @@ -0,0 +1,165 @@ + $this->createUser('user1', 'lorem@ipsum.com'), + 'user2' => $this->createUser('user2', 'dolor@ipsum.com'), + ]; + } + + /** + * [string[] $read, string[] $write] + */ + public function readDocumentsProvider() + { + return [ + [['role:all'], []], + [['role:member'], []], + [['user:random'], []], + [['user:lorem'] ,['user:lorem']], + [['user:dolor'] ,['user:dolor']], + [['user:dolor', 'user:lorem'] ,['user:dolor']], + [[], ['role:all']], + [['role:all'], ['role:all']], + [['role:member'], ['role:member']], + [['role:all'], ['role:member']], + ]; + } + + /** + * Setup database + * + * Data providers lose object state + * so explicitly pass [$users, $collections] to each iteration + * @return array + */ + public function testSetupDatabase(): array + { + $this->createUsers(); + + $public = $this->client->call(Client::METHOD_POST, '/database/collections', $this->getServerHeader(), [ + 'collectionId' => 'unique()', + 'name' => 'Movies', + 'read' => ['role:all'], + 'write' => ['role:all'], + 'permission' => 'document', + ]); + $this->assertEquals(201, $public['headers']['status-code']); + + $this->collections = ['public' => $public['body']['$id']]; + + $response = $this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['public'] . '/attributes/string', $this->getServerHeader(), [ + 'attributeId' => 'title', + 'size' => 256, + 'required' => true, + ]); + $this->assertEquals(201, $response['headers']['status-code']); + + $private = $this->client->call(Client::METHOD_POST, '/database/collections', $this->getServerHeader(), [ + 'collectionId' => 'unique()', + 'name' => 'Private Movies', + 'read' => ['role:member'], + 'write' => ['role:member'], + 'permission' => 'document', + ]); + $this->assertEquals(201, $private['headers']['status-code']); + + $this->collections['private'] = $private['body']['$id']; + + $this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['private'] . '/attributes/string', $this->getServerHeader(), [ + 'attributeId' => 'title', + 'size' => 256, + 'required' => true, + ]); + $this->assertEquals(201, $response['headers']['status-code']); + + sleep(2); + + return [ + 'users' => $this->users, + 'collections' => $this->collections + ]; + } + + /** + * Data provider params are passed before test dependencies + * @dataProvider readDocumentsProvider + * @depends testSetupDatabase + */ + public function testReadDocuments($read, $write, $data) + { + $users = $data['users']; + $collections = $data['collections']; + + $response = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collections['public'] . '/documents', $this->getServerHeader(), [ + 'documentId' => 'unique()', + 'data' => [ + 'title' => 'Lorem', + ], + 'read' => $read, + 'write' => $write, + ]); + $this->assertEquals(201, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collections['private'] . '/documents', $this->getServerHeader(), [ + 'documentId' => 'unique()', + 'data' => [ + 'title' => 'Lorem', + ], + 'read' => $read, + 'write' => $write, + ]); + $this->assertEquals(201, $response['headers']['status-code']); + + /** + * Check role:all collection + */ + $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collections['public'] . '/documents', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $users['user1']['session'], + ]); + + foreach ($documents['body']['documents'] as $document) { + $hasPermissions = \array_reduce(['role:all', 'role:member', 'user:' . $users['user1']['$id']], function ($carry, $item) use ($document) { + return $carry ? true : \in_array($item, $document['$read']); + }, false); + $this->assertTrue($hasPermissions); + } + + /** + * Check role:member collection + */ + $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collections['private'] . '/documents', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $users['user1']['session'], + ]); + + foreach ($documents['body']['documents'] as $document) { + $hasPermissions = \array_reduce(['role:all', 'role:member', 'user:' . $users['user1']['$id']], function ($carry, $item) use ($document) { + return $carry ? true : \in_array($item, $document['$read']); + }, false); + $this->assertTrue($hasPermissions); + } + + } +} diff --git a/tests/e2e/Services/Database/DatabasePermissionsScope.php b/tests/e2e/Services/Database/DatabasePermissionsScope.php new file mode 100644 index 0000000000..7a8c3e0186 --- /dev/null +++ b/tests/e2e/Services/Database/DatabasePermissionsScope.php @@ -0,0 +1,86 @@ +client->call(Client::METHOD_POST, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'userId' => $id, + 'email' => $email, + 'password' => $password + ]); + + $this->assertEquals(201, $user['headers']['status-code']); + + $session = $this->client->call(Client::METHOD_POST, '/account/sessions', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'email' => $email, + 'password' => $password, + ]); + + $session = $this->client->parseCookie((string)$session['headers']['set-cookie'])['a_session_' . $this->getProject()['$id']]; + + $user = [ + '$id' => $user['body']['$id'], + 'email' => $user['body']['email'], + 'session' => $session, + ]; + $this->users[$id] = $user; + + return $user; + } + + public function getCreatedUser(string $id): array + { + return $this->users[$id] ?? []; + } + + public function createTeam(string $id, string $name): array + { + $team = $this->client->call(Client::METHOD_POST, '/teams', $this->getServerHeader(), [ + 'teamId' => $id, + 'name' => $name + ]); + $this->teams[$id] = $team['body']; + + return $team['body']; + } + + public function addToTeam(string $user, string $team, array $roles = []): array + { + $membership = $this->client->call(Client::METHOD_POST, '/teams/' . $team . '/memberships', $this->getServerHeader(), [ + 'teamId' => $team, + 'email' => $this->getCreatedUser($user)['email'], + 'roles' => $roles, + 'url' => 'http://localhost:5000/join-us#title' + ]); + + return [ + 'user' => $membership['body']['userId'], + 'membership' => $membership['body']['$id'] + ]; + } + + public function getServerHeader(): array + { + return [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]; + } +} diff --git a/tests/e2e/Services/Database/DatabasePermissionsTeamTest.php b/tests/e2e/Services/Database/DatabasePermissionsTeamTest.php new file mode 100644 index 0000000000..218c9cd78a --- /dev/null +++ b/tests/e2e/Services/Database/DatabasePermissionsTeamTest.php @@ -0,0 +1,192 @@ + $this->createTeam('team1', 'Team 1'), + 'team2' => $this->createTeam('team2', 'Team 2'), + ]; + } + + public function createUsers(): array + { + return [ + 'user1' => $this->createUser('user1', 'lorem@ipsum.com'), + 'user2' => $this->createUser('user2', 'dolor@ipsum.com'), + 'user3' => $this->createUser('user3', 'sit@ipsum.com'), + ]; + } + + public function createCollections($teams) + { + $collection1 = $this->client->call(Client::METHOD_POST, '/database/collections', $this->getServerHeader(), [ + 'collectionId' => 'collection1', + 'name' => 'Collection 1', + 'read' => ['team:' . $teams['team1']['$id']], + 'write' => ['team:' . $teams['team1']['$id'] . '/admin'], + 'permission' => 'collection', + ]); + + $this->collections['collection1'] = $collection1['body']['$id']; + + $this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['collection1'] . '/attributes/string', $this->getServerHeader(), [ + 'attributeId' => 'title', + 'size' => 256, + 'required' => true, + ]); + + $collection2 = $this->client->call(Client::METHOD_POST, '/database/collections', $this->getServerHeader(), [ + 'collectionId' => 'collection2', + 'name' => 'Collection 2', + 'read' => ['team:' . $teams['team2']['$id']], + 'write' => ['team:' . $teams['team2']['$id'] . '/owner'], + 'permission' => 'collection', + ]); + + $this->collections['collection2'] = $collection2['body']['$id']; + + $this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['collection2'] . '/attributes/string', $this->getServerHeader(), [ + 'attributeId' => 'title', + 'size' => 256, + 'required' => true, + ]); + + sleep(2); + + return $this->collections; + } + + /* + * $success = can $user read from $collection + * [$user, $collection, $success] + */ + public function readDocumentsProvider(): array + { + return [ + ['user1', 'collection1', true], + ['user2', 'collection1', false], + ['user3', 'collection1', true], + ['user1', 'collection2', false], + ['user2', 'collection2', true], + ['user3', 'collection2', true], + ]; + } + + /* + * $success = can $user write to $collection + * [$user, $collection, $success] + */ + public function writeDocumentsProvider(): array + { + return [ + ['user1', 'collection1', true], + ['user2', 'collection1', false], + ['user3', 'collection1', false], + ['user1', 'collection2', false], + ['user2', 'collection2', true], + ['user3', 'collection2', false], + ]; + } + + /** + * Setup database + * + * Data providers lose object state + * so explicitly pass $users to each iteration + * @return array $users + */ + public function testSetupDatabase(): array + { + $this->createUsers(); + $this->createTeams(); + + $this->addToTeam('user1', 'team1', ['admin']); + $this->addToTeam('user2', 'team2', ['owner']); + + // user3 in both teams but with no roles + $this->addToTeam('user3', 'team1'); + $this->addToTeam('user3', 'team2'); + + $this->createCollections($this->teams); + + $response = $this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['collection1'] . '/documents', $this->getServerHeader(), [ + 'documentId' => 'unique()', + 'data' => [ + 'title' => 'Lorem', + ], + ]); + $this->assertEquals(201, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['collection2'] . '/documents', $this->getServerHeader(), [ + 'documentId' => 'unique()', + 'data' => [ + 'title' => 'Ipsum', + ], + ]); + $this->assertEquals(201, $response['headers']['status-code']); + + return $this->users; + } + + /** + * Data provider params are passed before test dependencies + * @depends testSetupDatabase + * @dataProvider readDocumentsProvider + */ + public function testReadDocuments($user, $collection, $success, $users) + { + $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collection . '/documents', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $users[$user]['session'], + ]); + + if ($success) { + $this->assertCount(1, $documents['body']['documents']); + } else { + $this->assertEquals(404, $documents['headers']['status-code']); + } + } + + /** + * @depends testSetupDatabase + * @dataProvider writeDocumentsProvider + */ + public function testWriteDocuments($user, $collection, $success, $users) + { + $documents = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collection . '/documents', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $users[$user]['session'], + ], [ + 'documentId' => 'unique()', + 'data' => [ + 'title' => 'Ipsum', + ], + ]); + + if ($success) { + $this->assertEquals(201, $documents['headers']['status-code']); + } else { + // 401 if user is a part of team, 404 otherwise + $this->assertContains($documents['headers']['status-code'], [401, 404]); + } + } +}