From 1a2a725cb0eb7fe7489bb8a10f4577df9cd6e16c Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 23 May 2025 09:03:42 +0000 Subject: [PATCH 1/2] Merge pull request #9868 from ArnabChatterjee20k/dat-532 added encrypt property in the attribute string response model --- app/controllers/api/databases.php | 13 ++++++--- app/init/database/filters.php | 21 ++++++++++----- .../Utopia/Response/Model/AttributeString.php | 7 +++++ .../e2e/Services/Databases/DatabasesBase.php | 2 +- .../Databases/DatabasesCustomServerTest.php | 27 ++++++++++++++++++- 5 files changed, 59 insertions(+), 11 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 68d8891067..9c4b8e7668 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -1368,7 +1368,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string 'array' => $array, 'filters' => $filters, ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); - + $attribute->setAttribute('encrypt', $encrypt); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) ->dynamic($attribute, Response::MODEL_ATTRIBUTE_STRING); @@ -2047,6 +2047,13 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') throw new Exception(Exception::GENERAL_QUERY_INVALID); } + foreach ($attributes as $attribute) { + if ($attribute->getAttribute('type') === Database::VAR_STRING) { + $filters = $attribute->getAttribute('filters', []); + $attribute->setAttribute('encrypt', in_array('encrypt', $filters)); + } + } + $response->dynamic(new Document([ 'attributes' => $attributes, 'total' => $total, @@ -2111,7 +2118,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') $type = $attribute->getAttribute('type'); $format = $attribute->getAttribute('format'); $options = $attribute->getAttribute('options', []); - + $filters = $attribute->getAttribute('filters', []); foreach ($options as $key => $option) { $attribute->setAttribute($key, $option); } @@ -2131,7 +2138,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') }, default => Response::MODEL_ATTRIBUTE, }; - + $attribute->setAttribute('encrypt', in_array('encrypt', $filters)); $response->dynamic($attribute, $model); }); diff --git a/app/init/database/filters.php b/app/init/database/filters.php index 8223b1c677..49d7845699 100644 --- a/app/init/database/filters.php +++ b/app/init/database/filters.php @@ -77,12 +77,21 @@ Database::addFilter( ]); foreach ($attributes as $attribute) { - if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { - $options = $attribute->getAttribute('options'); - foreach ($options as $key => $value) { - $attribute->setAttribute($key, $value); - } - $attribute->removeAttribute('options'); + $attributeType = $attribute->getAttribute('type'); + + switch ($attributeType) { + case Database::VAR_RELATIONSHIP: + $options = $attribute->getAttribute('options'); + foreach ($options as $key => $value) { + $attribute->setAttribute($key, $value); + } + $attribute->removeAttribute('options'); + break; + + case Database::VAR_STRING: + $filters = $attribute->getAttribute('filters', []); + $attribute->setAttribute('encrypt', in_array('encrypt', $filters)); + break; } } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeString.php b/src/Appwrite/Utopia/Response/Model/AttributeString.php index 12bb42009d..fded48fddc 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeString.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeString.php @@ -24,6 +24,13 @@ class AttributeString extends Attribute 'required' => false, 'example' => 'default', ]) + ->addRule('encrypt', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Defines whether this attribute is encrypted or not.', + 'default' => false, + 'required' => false, + 'example' => false, + ]) ; } diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 7c0060ecaa..9aed3684de 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -298,7 +298,7 @@ trait DatabasesBase $this->assertEquals($title['body']['type'], 'string'); $this->assertEquals($title['body']['size'], 256); $this->assertEquals($title['body']['required'], true); - + $this->assertFalse($title['body']['encrypt']); $this->assertEquals(202, $description['headers']['status-code']); $this->assertEquals($description['body']['key'], 'description'); $this->assertEquals($description['body']['type'], 'string'); diff --git a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php index 829960b54f..bae86066ee 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php @@ -697,7 +697,14 @@ class DatabasesCustomServerTest extends Scope 'required' => true, 'encrypt' => true, ]); - + $this->assertTrue($lastName['body']['encrypt']); + sleep(1); + $response = $this->client->call(Client::METHOD_GET, $attributesPath . '/lastName', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + $this->assertTrue($response['body']['encrypt']); /** * Check status of every attribute @@ -741,6 +748,24 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $document['headers']['status-code']); $this->assertEquals('Jonah', $document['body']['firstName']); $this->assertEquals('Jameson', $document['body']['lastName']); + + + $actors = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $actors['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), []); + $attributes = $actors['body']['attributes']; + foreach ($attributes as $attribute) { + $this->assertArrayHasKey('encrypt', $attribute); + if ($attribute['key'] === 'firstName') { + $this->assertFalse($attribute['encrypt']); + } + if ($attribute['key'] === 'lastName') { + $this->assertTrue($attribute['encrypt']); + } + } + } public function testDeleteAttribute(): array From d27dba05d2b3bfe309a1b7e0eeb136d1ffc0e2a0 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 21 May 2025 08:51:31 +0000 Subject: [PATCH 2/2] Merge pull request #9841 from ArnabChatterjee20k/dat-532 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit added checking for encrypt and plan allowing encryption of string att… --- app/controllers/api/databases.php | 6 +++++- tests/e2e/Services/Databases/DatabasesCustomServerTest.php | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 9c4b8e7668..54a4c565f4 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -1347,7 +1347,11 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, bool $encrypt, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->inject('plan') + ->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, bool $encrypt, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, array $plan) { + if ($encrypt && !empty($plan) && !($plan['databasesAllowEncrypt'] ?? false)) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Encrypted string attributes are not available on your plan. Please upgrade to create encrypted string attributes.'); + } // Ensure attribute default is within required size $validator = new Text($size, 0); if (!is_null($default) && !$validator->isValid($default)) { diff --git a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php index bae86066ee..c0d8763aa7 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php @@ -695,7 +695,7 @@ class DatabasesCustomServerTest extends Scope 'key' => 'lastName', 'size' => 256, 'required' => true, - 'encrypt' => true, + 'encrypt' => true ]); $this->assertTrue($lastName['body']['encrypt']); sleep(1);