From 9a182d615a1ee94afdab72cc3c447b3eec55491d Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 2 Aug 2022 21:18:49 +1200 Subject: [PATCH] Refactor databases to use new permissions --- app/controllers/api/databases.php | 109 ++++++++------- composer.json | 2 +- .../Utopia/Response/Model/Collection.php | 21 +-- .../Utopia/Response/Model/Document.php | 11 +- .../e2e/Services/Databases/DatabasesBase.php | 126 ++++++++---------- .../Databases/DatabasesCustomServerTest.php | 12 +- .../DatabasesPermissionsGuestTest.php | 7 +- .../DatabasesPermissionsMemberTest.php | 10 +- 8 files changed, 142 insertions(+), 156 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index a14e57d6be..f395ce49d5 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -497,15 +497,14 @@ App::post('/v1/databases/:databaseId/collections') ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('name', '', new Text(128), 'Collection name. Max length: 128 chars.') - ->param('permission', null, new WhiteList(['document', 'collection']), 'Specifies the permissions model used in this collection, which accepts either \'collection\' or \'document\'. For \'collection\' level permission, the permissions specified in read and write params are applied to all documents in the collection. For \'document\' level permissions, read and write permissions are specified in each document. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.') - ->param('read', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.') - ->param('write', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.') + ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with permissions. By default no user is granted with any read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.') + ->param('documentSecurity', false, new Boolean(), 'Specifies the permissions model used in this collection, which accepts either \'collection\' or \'document\'. For \'collection\' level permission, the permissions specified in read and write params are applied to all documents in the collection. For \'document\' level permissions, read and write permissions are specified in each document. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.') ->inject('response') ->inject('dbForProject') ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $name, ?string $permission, ?array $read, ?array $write, Response $response, Database $dbForProject, EventAudit $audits, Stats $usage, Event $events) { + ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, Response $response, Database $dbForProject, EventAudit $audits, Stats $usage, Event $events) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -520,9 +519,8 @@ App::post('/v1/databases/:databaseId/collections') '$id' => $collectionId, 'databaseInternalId' => $database->getInternalId(), 'databaseId' => $databaseId, - '$read' => $read ?? [], // Collection permissions for collection documents (based on permission model) - '$write' => $write ?? [], // Collection permissions for collection documents (based on permission model) - 'permission' => $permission, // Permissions model type (document vs collection) + '$permissions' => $permissions ?? [], + 'documentSecurity' => $documentSecurity, 'enabled' => true, 'name' => $name, 'search' => implode(' ', [$collectionId, $name]), @@ -751,16 +749,15 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new UID(), 'Collection ID.') ->param('name', null, new Text(128), 'Collection name. Max length: 128 chars.') - ->param('permission', null, new WhiteList(['document', 'collection']), 'Permissions type model to use for reading documents in this collection. You can use collection-level permission set once on the collection using the `read` and `write` params, or you can set document-level permission where each document read and write params will decide who has access to read and write to each document individually. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.') - ->param('read', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with read permissions. By default inherits the existing read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true) - ->param('write', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with write permissions. By default inherits the existing write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true) + ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with permissions. By default no user is granted with any permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true) + ->param('documentSecurity', false, new Boolean(), 'Whether to enable document-level permission where each document\'s permissions parameter will decide who has access to each file individually. [learn more about permissions](/docs/permissions) and get a full list of available permissions.') ->param('enabled', true, new Boolean(), 'Is collection enabled?', true) ->inject('response') ->inject('dbForProject') ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $name, string $permission, ?array $read, ?array $write, bool $enabled, Response $response, Database $dbForProject, EventAudit $audits, Stats $usage, Event $events) { + ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, EventAudit $audits, Stats $usage, Event $events) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -779,10 +776,9 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') try { $collection = $dbForProject->updateDocument('database_' . $database->getInternalId(), $collectionId, $collection - ->setAttribute('$write', $write) - ->setAttribute('$read', $read) + ->setAttribute('$permissions', $permissions) ->setAttribute('name', $name) - ->setAttribute('permission', $permission) + ->setAttribute('documentSecurity', $documentSecurity) ->setAttribute('enabled', $enabled) ->setAttribute('search', implode(' ', [$collectionId, $name]))); } catch (AuthorizationException $exception) { @@ -1822,8 +1818,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->param('documentId', '', new CustomId(), 'Document ID. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define attributes before creating documents.') ->param('data', [], new JSON(), 'Document data as JSON object.') - ->param('read', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true) - ->param('write', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with write permissions. By default only the current user is granted with write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true) + ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with permissions. By default no user is granted with any permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true) ->inject('response') ->inject('dbForProject') ->inject('user') @@ -1831,7 +1826,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->inject('usage') ->inject('events') ->inject('mode') - ->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $read, ?array $write, Response $response, Database $dbForProject, Document $user, EventAudit $audits, Stats $usage, Event $events, string $mode) { + ->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, EventAudit $audits, Stats $usage, Event $events, string $mode) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -1862,31 +1857,49 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') } // Check collection permissions when enforced - if ($collection->getAttribute('permission') === 'collection') { - $validator = new Authorization('write'); - if (!$validator->isValid($collection->getWrite())) { + if (!$collection->getAttribute('documentSecurity', false)) { + $validator = new Authorization('create'); + if (!$validator->isValid($collection->getCreate())) { throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); } } $data['$collection'] = $collection->getId(); // Adding this param to make API easier for developers $data['$id'] = $documentId == 'unique()' ? $dbForProject->getId() : $documentId; - $data['$read'] = (is_null($read) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $read ?? []; // By default set read permissions for user - $data['$write'] = (is_null($write) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $write ?? []; // By default set write permissions for user + + if (\is_null($permissions)) { + $permissions = []; + if (!$user->isEmpty()) { + $permissions = [ + 'read(user:' . $user->getId() . ') ', + 'write(user:' . $user->getId() . ') ', + ]; + } + } elseif (empty(\preg_grep('#^read\(.+\)$#', $permissions))) { + if (!$user->isEmpty()) { + $permissions[] = 'read(user:' . $user->getId() . ')'; + } + } elseif (empty(\preg_grep('#^write\(.+\)$#', $permissions))) { + if (!$user->isEmpty()) { + $permissions[] = 'write(user:' . $user->getId() . ')'; + } + } + + $data['$permissions'] = $permissions; // Users can only add their roles to documents, API keys and Admin users can add any $roles = Authorization::getRoles(); if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) { - foreach ($data['$read'] as $read) { - if (!Authorization::isRole($read)) { - // TODO: Isn't this a 401: Unauthorized Error ? - throw new Exception('Read permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); - } - } - foreach ($data['$write'] as $write) { - if (!Authorization::isRole($write)) { - throw new Exception('Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + foreach ($permissions as $permission) { + $matches = []; + if (\preg_match('/^(create|write)\((.+)(?:,(.+))*\)$/', $permission, $matches)) { + \array_shift($matches); + foreach ($matches as $match) { + if (!Authorization::isRole($match)) { + throw new Exception('Permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + } + } } } } @@ -2223,15 +2236,14 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum ->param('collectionId', null, new UID(), 'Collection ID.') ->param('documentId', null, new UID(), 'Document ID.') ->param('data', [], new JSON(), 'Document data as JSON object. Include only attribute and value pairs to be updated.', true) - ->param('read', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with read permissions. By default inherits the existing read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true) - ->param('write', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with write permissions. By default inherits the existing write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true) + ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with permissions. By default no user is granted with any permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true) ->inject('response') ->inject('dbForProject') ->inject('audits') ->inject('usage') ->inject('events') ->inject('mode') - ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $read, ?array $write, Response $response, Database $dbForProject, EventAudit $audits, Stats $usage, Event $events, string $mode) { + ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, EventAudit $audits, Stats $usage, Event $events, string $mode) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -2281,31 +2293,32 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $data['$collection'] = $collection->getId(); // Make sure user don't switch collectionID $data['$createdAt'] = $document->getCreatedAt(); // Make sure user don't switch createdAt $data['$id'] = $document->getId(); // Make sure user don't switch document unique ID - $data['$read'] = (is_null($read)) ? ($document->getRead() ?? []) : $read; // By default inherit read permissions - $data['$write'] = (is_null($write)) ? ($document->getWrite() ?? []) : $write; // By default inherit write permissions + + if (\is_null($permissions)) { + $permissions = $document->getPermissions() ?? []; + } + + $data['$permissions'] = $permissions; // Users can only add their roles to documents, API keys and Admin users can add any $roles = Authorization::getRoles(); if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) { - if (!is_null($read)) { - foreach ($data['$read'] as $read) { - if (!Authorization::isRole($read)) { - throw new Exception('Read permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); - } - } - } - if (!is_null($write)) { - foreach ($data['$write'] as $write) { - if (!Authorization::isRole($write)) { - throw new Exception('Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + foreach ($data['$permissions'] as $permission) { + $matches = []; + if (\preg_match('/^(update|write)\((.+)(?:,(.+))*\)$/', $permission, $matches)) { + \array_shift($matches); + foreach ($matches as $match) { + if (!Authorization::isRole($match)) { + throw new Exception('Permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + } } } } } try { - if ($collection->getAttribute('permission') === 'collection') { + if (!$collection->getAttribute('documentSecurity', false)) { /** @var Document $document */ $document = Authorization::skip(fn() => $dbForProject->updateDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $document->getId(), new Document($data))); } else { diff --git a/composer.json b/composer.json index c95dbab819..a04d46e4e8 100644 --- a/composer.json +++ b/composer.json @@ -50,7 +50,7 @@ "utopia-php/cache": "0.6.*", "utopia-php/cli": "0.13.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "0.18.*", + "utopia-php/database": "dev-refactor-permissions as 0.18.9", "utopia-php/locale": "0.4.*", "utopia-php/registry": "0.5.*", "utopia-php/preloader": "0.2.*", diff --git a/src/Appwrite/Utopia/Response/Model/Collection.php b/src/Appwrite/Utopia/Response/Model/Collection.php index 2340cc5297..cd75da2ce6 100644 --- a/src/Appwrite/Utopia/Response/Model/Collection.php +++ b/src/Appwrite/Utopia/Response/Model/Collection.php @@ -28,18 +28,11 @@ class Collection extends Model 'default' => 0, 'example' => 1592981250, ]) - ->addRule('$read', [ + ->addRule('$permissions', [ 'type' => self::TYPE_STRING, - 'description' => 'Collection read permissions.', + 'description' => 'Collection permissions.', 'default' => '', - 'example' => 'role:all', - 'array' => true - ]) - ->addRule('$write', [ - 'type' => self::TYPE_STRING, - 'description' => 'Collection write permissions.', - 'default' => '', - 'example' => 'user:608f9da25e7e1', + 'example' => 'read(any)', 'array' => true ]) ->addRule('databaseId', [ @@ -60,11 +53,11 @@ class Collection extends Model 'default' => true, 'example' => false, ]) - ->addRule('permission', [ - 'type' => self::TYPE_STRING, - 'description' => 'Collection permission model. Possible values: `document` or `collection`', + ->addRule('documentSecurity', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Whether document-level permissions are enabled.', 'default' => '', - 'example' => 'document', + 'example' => true, ]) ->addRule('attributes', [ 'type' => [ diff --git a/src/Appwrite/Utopia/Response/Model/Document.php b/src/Appwrite/Utopia/Response/Model/Document.php index 3106e813b1..1384a67d44 100644 --- a/src/Appwrite/Utopia/Response/Model/Document.php +++ b/src/Appwrite/Utopia/Response/Model/Document.php @@ -54,18 +54,11 @@ class Document extends Any 'default' => 0, 'example' => 1592981250, ]) - ->addRule('$read', [ - 'type' => self::TYPE_STRING, - 'description' => 'Document read permissions.', - 'default' => '', - 'example' => 'role:all', - 'array' => true, - ]) - ->addRule('$write', [ + ->addRule('$permissions', [ 'type' => self::TYPE_STRING, 'description' => 'Document write permissions.', 'default' => '', - 'example' => 'user:608f9da25e7e1', + 'example' => 'read(user:608f9da25e7e1)', 'array' => true, ]) ; diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 34a4160fde..04680ce4d3 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -840,10 +840,8 @@ trait DatabasesBase $this->assertEquals($document1['headers']['status-code'], 201); $this->assertEquals($document1['body']['title'], 'Captain America'); $this->assertEquals($document1['body']['releaseYear'], 1944); - $this->assertIsArray($document1['body']['$read']); - $this->assertIsArray($document1['body']['$write']); - $this->assertCount(1, $document1['body']['$read']); - $this->assertCount(1, $document1['body']['$write']); + $this->assertIsArray($document1['body']['$permissions']); + $this->assertCount(2, $document1['body']['$permissions']); $this->assertCount(2, $document1['body']['actors']); $this->assertEquals($document1['body']['actors'][0], 'Chris Evans'); $this->assertEquals($document1['body']['actors'][1], 'Samuel Jackson'); @@ -852,10 +850,8 @@ trait DatabasesBase $this->assertEquals($document2['body']['title'], 'Spider-Man: Far From Home'); $this->assertEquals($document2['body']['releaseYear'], 2019); $this->assertEquals($document2['body']['duration'], null); - $this->assertIsArray($document2['body']['$read']); - $this->assertIsArray($document2['body']['$write']); - $this->assertCount(1, $document2['body']['$read']); - $this->assertCount(1, $document2['body']['$write']); + $this->assertIsArray($document2['body']['$permissions']); + $this->assertCount(2, $document2['body']['$permissions']); $this->assertCount(3, $document2['body']['actors']); $this->assertEquals($document2['body']['actors'][0], 'Tom Holland'); $this->assertEquals($document2['body']['actors'][1], 'Zendaya Maree Stoermer'); @@ -865,10 +861,8 @@ trait DatabasesBase $this->assertEquals($document3['body']['title'], 'Spider-Man: Homecoming'); $this->assertEquals($document3['body']['releaseYear'], 2017); $this->assertEquals($document3['body']['duration'], 0); - $this->assertIsArray($document3['body']['$read']); - $this->assertIsArray($document3['body']['$write']); - $this->assertCount(1, $document3['body']['$read']); - $this->assertCount(1, $document3['body']['$write']); + $this->assertIsArray($document3['body']['$permissions']); + $this->assertCount(2, $document3['body']['$permisisons']); $this->assertCount(2, $document3['body']['actors']); $this->assertEquals($document3['body']['actors'][0], 'Tom Holland'); $this->assertEquals($document3['body']['actors'][1], 'Zendaya Maree Stoermer'); @@ -996,8 +990,7 @@ trait DatabasesBase $this->assertEquals($response['body']['$collection'], $document['$collection']); $this->assertEquals($response['body']['title'], $document['title']); $this->assertEquals($response['body']['releaseYear'], $document['releaseYear']); - $this->assertEquals($response['body']['$read'], $document['$read']); - $this->assertEquals($response['body']['$write'], $document['$write']); + $this->assertEquals($response['body']['$permissions'], $document['$permissions']); $this->assertFalse(array_key_exists('$internalId', $response['body'])); } } @@ -1417,8 +1410,10 @@ trait DatabasesBase 'actors' => [], '$createdAt' => 5 // Should be ignored ], - 'read' => ['user:' . $this->getUser()['$id']], - 'write' => ['user:' . $this->getUser()['$id']], + 'permissions' => [ + 'read(user: ' . $this->getUser()['$id'] . ')', + 'write(user: ' . $this->getUser()['$id'] . ')', + ], ]); $id = $document['body']['$id']; @@ -1427,8 +1422,8 @@ trait DatabasesBase $this->assertEquals($document['body']['title'], 'Thor: Ragnaroc'); $this->assertEquals($document['body']['releaseYear'], 2017); $this->assertNotEquals($document['body']['$createdAt'], 5); - $this->assertEquals('user:' . $this->getUser()['$id'], $document['body']['$read'][0]); - $this->assertEquals('user:' . $this->getUser()['$id'], $document['body']['$write'][0]); + $this->assertEquals('read(user:' . $this->getUser()['$id'] . ')', $document['body']['$permissions'][0]); + $this->assertEquals('write(user:' . $this->getUser()['$id'] . ')', $document['body']['$permissions'][1]); $document = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $id, array_merge([ 'content-type' => 'application/json', @@ -1437,8 +1432,10 @@ trait DatabasesBase 'data' => [ 'title' => 'Thor: Ragnarok', ], - 'read' => ['role:member'], - 'write' => ['role:member'], + 'permissions' => [ + 'read(users)', + 'write(users)', + ], ]); $this->assertEquals($document['headers']['status-code'], 200); @@ -1446,8 +1443,8 @@ trait DatabasesBase $this->assertEquals($document['body']['$collection'], $data['moviesId']); $this->assertEquals($document['body']['title'], 'Thor: Ragnarok'); $this->assertEquals($document['body']['releaseYear'], 2017); - $this->assertEquals('role:member', $document['body']['$read'][0]); - $this->assertEquals('role:member', $document['body']['$write'][0]); + $this->assertEquals('read(users)', $document['body']['$permissions'][0]); + $this->assertEquals('write(users)', $document['body']['$permissions'][1]); $document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $id, array_merge([ 'content-type' => 'application/json', @@ -1994,21 +1991,17 @@ trait DatabasesBase $this->assertEquals($document['headers']['status-code'], 201); $this->assertEquals($document['body']['title'], 'Captain America'); $this->assertEquals($document['body']['releaseYear'], 1944); - $this->assertIsArray($document['body']['$read']); - $this->assertIsArray($document['body']['$write']); + $this->assertIsArray($document['body']['$permissions']); if ($this->getSide() == 'client') { - $this->assertCount(1, $document['body']['$read']); - $this->assertCount(1, $document['body']['$write']); - $this->assertEquals(['user:' . $this->getUser()['$id']], $document['body']['$read']); - $this->assertEquals(['user:' . $this->getUser()['$id']], $document['body']['$write']); + $this->assertCount(2, $document['body']['$permissions']); + $this->assertEquals(['read(user:' . $this->getUser()['$id'] . ')'], $document['body']['$permissions']); + $this->assertEquals(['write(user:' . $this->getUser()['$id'] . ')'], $document['body']['$permissions']); } if ($this->getSide() == 'server') { - $this->assertCount(0, $document['body']['$read']); - $this->assertCount(0, $document['body']['$write']); - $this->assertEquals([], $document['body']['$read']); - $this->assertEquals([], $document['body']['$write']); + $this->assertCount(0, $document['body']['$permissions']); + $this->assertEquals([], $document['body']['$permissions']); } // Updated and Inherit Permissions @@ -2022,7 +2015,9 @@ trait DatabasesBase 'releaseYear' => 1945, 'actors' => [], ], - 'read' => ['role:all'], + 'permissions' => [ + 'read(any)', + ], ]); $this->assertEquals($document['headers']['status-code'], 200); @@ -2030,17 +2025,14 @@ trait DatabasesBase $this->assertEquals($document['body']['releaseYear'], 1945); if ($this->getSide() == 'client') { - $this->assertCount(1, $document['body']['$read']); - $this->assertCount(1, $document['body']['$write']); - $this->assertEquals(['role:all'], $document['body']['$read']); - $this->assertEquals(['user:' . $this->getUser()['$id']], $document['body']['$write']); + $this->assertCount(2, $document['body']['$permissions']); + $this->assertContains('read(any)', $document['body']['$permissions']); + $this->assertContains('write(user:' . $this->getUser()['$id'] . ')', $document['body']['$permissions']); } if ($this->getSide() == 'server') { - $this->assertCount(1, $document['body']['$read']); - $this->assertCount(0, $document['body']['$write']); - $this->assertEquals(['role:all'], $document['body']['$read']); - $this->assertEquals([], $document['body']['$write']); + $this->assertCount(1, $document['body']['$permissions']); + $this->assertEquals(['read(any)'], $document['body']['$permissions']); } $document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $id, array_merge([ @@ -2053,17 +2045,14 @@ trait DatabasesBase $this->assertEquals($document['body']['releaseYear'], 1945); if ($this->getSide() == 'client') { - $this->assertCount(1, $document['body']['$read']); - $this->assertCount(1, $document['body']['$write']); - $this->assertEquals(['role:all'], $document['body']['$read']); + $this->assertCount(2, $document['body']['$permissions']); + $this->assertEquals(['read(any)'], $document['body']['$permissions']); $this->assertEquals(['user:' . $this->getUser()['$id']], $document['body']['$write']); } if ($this->getSide() == 'server') { - $this->assertCount(1, $document['body']['$read']); - $this->assertCount(0, $document['body']['$write']); - $this->assertEquals(['role:all'], $document['body']['$read']); - $this->assertEquals([], $document['body']['$write']); + $this->assertCount(1, $document['body']['$permissions']); + $this->assertEquals(['read(any)'], $document['body']['$permissions']); } // Reset Permissions @@ -2089,10 +2078,8 @@ trait DatabasesBase $this->assertEquals($document['headers']['status-code'], 200); $this->assertEquals($document['body']['title'], 'Captain America 3'); $this->assertEquals($document['body']['releaseYear'], 1946); - $this->assertCount(0, $document['body']['$read']); - $this->assertCount(0, $document['body']['$write']); - $this->assertEquals([], $document['body']['$read']); - $this->assertEquals([], $document['body']['$write']); + $this->assertCount(0, $document['body']['$permissions']); + $this->assertEquals([], $document['body']['$permissions']); } return $data; @@ -2478,24 +2465,27 @@ trait DatabasesBase 'data' => [ 'title' => 'Captain America', ], - 'read' => ['role:all'], - 'write' => ['role:all'], + 'permissions' => [ + 'read(any)', + 'write(any)', + ], ]); $id = $document['body']['$id']; $this->assertEquals($document['headers']['status-code'], 201); - $this->assertCount(1, $document['body']['$read']); - $this->assertCount(1, $document['body']['$write']); - $this->assertEquals(['role:all'], $document['body']['$read']); - $this->assertEquals(['role:all'], $document['body']['$write']); + $this->assertCount(2, $document['body']['$permissions']); + $this->assertContains('read(any)', $document['body']['$permissions']); + $this->assertContains('write(any)', $document['body']['$permissions']); // Send only read permission $document = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents/' . $id, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'read' => ['user:' . $this->getUser()['$id']], + 'permissions' => [ + 'read(user: ' . $this->getUser()['$id'] . ')', + ] ]); if ($this->getSide() == 'client') { @@ -2504,10 +2494,9 @@ trait DatabasesBase if ($this->getSide() == 'server') { $this->assertEquals($document['headers']['status-code'], 200); - $this->assertCount(1, $document['body']['$read']); - $this->assertCount(1, $document['body']['$write']); - $this->assertEquals(['user:' . $this->getUser()['$id']], $document['body']['$read']); - $this->assertEquals(['role:all'], $document['body']['$write']); + $this->assertCount(2, $document['body']['$permissions']); + $this->assertContains('read(user:' . $this->getUser()['$id'] . ')', $document['body']['$permissions']); + $this->assertContains('write(any)', $document['body']['$permissions']); } // send only write permission @@ -2515,15 +2504,16 @@ trait DatabasesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'write' => ['user:' . $this->getUser()['$id']], + 'permissions' => [ + 'write(user: ' . $this->getUser()['$id'] . ')', + ], ]); if ($this->getSide() == 'server') { $this->assertEquals($document['headers']['status-code'], 200); - $this->assertCount(1, $document['body']['$read']); - $this->assertCount(1, $document['body']['$write']); - $this->assertEquals(['user:' . $this->getUser()['$id']], $document['body']['$read']); - $this->assertEquals(['user:' . $this->getUser()['$id']], $document['body']['$write']); + $this->assertCount(2, $document['body']['$permissions']); + $this->assertContains('read(user:' . $this->getUser()['$id'] . ')', $document['body']['$permissions']); + $this->assertContains('write(user:' . $this->getUser()['$id'] . ')', $document['body']['$permissions']); } // remove collection diff --git a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php index cc75a13a80..f88feea3e2 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php @@ -849,18 +849,14 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals($document1['headers']['status-code'], 201); - $this->assertIsArray($document1['body']['$read']); - $this->assertIsArray($document1['body']['$write']); - $this->assertCount(1, $document1['body']['$read']); - $this->assertCount(1, $document1['body']['$write']); + $this->assertIsArray($document1['body']['$permissions']); + $this->assertCount(2, $document1['body']['$permissions']); $this->assertEquals($document1['body']['firstName'], 'Tom'); $this->assertEquals($document1['body']['lastName'], 'Holland'); $this->assertEquals($document2['headers']['status-code'], 201); - $this->assertIsArray($document2['body']['$read']); - $this->assertIsArray($document2['body']['$write']); - $this->assertCount(1, $document2['body']['$read']); - $this->assertCount(1, $document2['body']['$write']); + $this->assertIsArray($document2['body']['$permissions']); + $this->assertCount(1, $document2['body']['$permissions']); $this->assertEquals($document2['body']['firstName'], 'Samuel'); $this->assertEquals($document2['body']['lastName'], 'Jackson'); diff --git a/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php b/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php index 7b1ce8f3e6..b5d6a8a397 100644 --- a/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php +++ b/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php @@ -66,7 +66,7 @@ class DatabasesPermissionsGuestTest extends Scope /** * @dataProvider readDocumentsProvider */ - public function testReadDocuments($read, $write) + public function testReadDocuments($permissions) { $data = $this->createCollection(); $collectionId = $data['collectionId']; @@ -76,8 +76,7 @@ class DatabasesPermissionsGuestTest extends Scope 'data' => [ 'title' => 'Lorem', ], - 'read' => $read, - 'write' => $write, + 'permissions' => $permissions, ]); $this->assertEquals(201, $response['headers']['status-code']); @@ -87,7 +86,7 @@ class DatabasesPermissionsGuestTest extends Scope ]); foreach ($documents['body']['documents'] as $document) { - $this->assertContains('role:all', $document['$read']); + $this->assertContains('role:all', $document['$permissions']); } } } diff --git a/tests/e2e/Services/Databases/DatabasesPermissionsMemberTest.php b/tests/e2e/Services/Databases/DatabasesPermissionsMemberTest.php index 0c3f3a08a3..6c857b172a 100644 --- a/tests/e2e/Services/Databases/DatabasesPermissionsMemberTest.php +++ b/tests/e2e/Services/Databases/DatabasesPermissionsMemberTest.php @@ -148,8 +148,9 @@ class DatabasesPermissionsMemberTest extends Scope ]); 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']); + $hasPermissions = \array_reduce(['any', 'users', 'user:' . $users['user1']['$id']], function ($carry, $item) use ($document) { + // TODO: Fix this + return $carry ? true : \in_array($item, $document['$permissions']); }, false); $this->assertTrue($hasPermissions); } @@ -165,8 +166,9 @@ class DatabasesPermissionsMemberTest extends Scope ]); 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']); + $hasPermissions = \array_reduce(['any', 'users', 'user:' . $users['user1']['$id']], function ($carry, $item) use ($document) { + // TODO: Fix this + return $carry ? true : \in_array($item, $document['$permissions']); }, false); $this->assertTrue($hasPermissions); }