Merge pull request #5604 from 2002Bishwajeet/feat-add-encrypt-param
feat: add encrypt param to string attribute
This commit is contained in:
commit
171f22daa0
8 changed files with 153 additions and 3 deletions
|
@ -363,6 +363,11 @@ return [
|
||||||
'description' => 'The requested range is not satisfiable. Please check the value of the Range header.',
|
'description' => 'The requested range is not satisfiable. Please check the value of the Range header.',
|
||||||
'code' => 416,
|
'code' => 416,
|
||||||
],
|
],
|
||||||
|
Exception::STORAGE_INVALID_APPWRITE_ID => [
|
||||||
|
'name' => Exception::STORAGE_INVALID_APPWRITE_ID,
|
||||||
|
'description' => 'The value for x-appwrite-id header is invalid. Please check the value of the x-appwrite-id header is valid id and not unique().',
|
||||||
|
'code' => 400,
|
||||||
|
],
|
||||||
|
|
||||||
/** Functions */
|
/** Functions */
|
||||||
Exception::FUNCTION_NOT_FOUND => [
|
Exception::FUNCTION_NOT_FOUND => [
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
"emails.magicSession.signature": "Equipa {{project}}",
|
"emails.magicSession.signature": "Equipa {{project}}",
|
||||||
"emails.recovery.subject": "Redefinição de senha",
|
"emails.recovery.subject": "Redefinição de senha",
|
||||||
"emails.recovery.hello": "Olá {{name}}",
|
"emails.recovery.hello": "Olá {{name}}",
|
||||||
"emails.recovery.body": "tilize este link para redefinir a palavra-passe do seu projecto {{project}}",
|
"emails.recovery.body": "Utilize este link para redefinir a palavra-passe do seu projecto {{project}}",
|
||||||
"emails.recovery.footer": "Se não pediu para redefinir a sua palavra-passe, pode ignorar esta mensagem.",
|
"emails.recovery.footer": "Se não pediu para redefinir a sua palavra-passe, pode ignorar esta mensagem.",
|
||||||
"emails.recovery.thanks": "Obrigado",
|
"emails.recovery.thanks": "Obrigado",
|
||||||
"emails.recovery.signature": "Equipa {{project}}",
|
"emails.recovery.signature": "Equipa {{project}}",
|
||||||
|
|
|
@ -1086,11 +1086,12 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string
|
||||||
->param('required', null, new Boolean(), 'Is attribute required?')
|
->param('required', null, new Boolean(), 'Is attribute required?')
|
||||||
->param('default', null, new Text(0, 0), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
|
->param('default', null, new Text(0, 0), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
|
||||||
->param('array', false, new Boolean(), 'Is attribute an array?', true)
|
->param('array', false, new Boolean(), 'Is attribute an array?', true)
|
||||||
|
->param('encrypt', false, new Boolean(), 'Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.', true)
|
||||||
->inject('response')
|
->inject('response')
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('database')
|
->inject('database')
|
||||||
->inject('events')
|
->inject('events')
|
||||||
->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Event $events) {
|
->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, bool $encrypt, Response $response, Database $dbForProject, EventDatabase $database, Event $events) {
|
||||||
|
|
||||||
// Ensure attribute default is within required size
|
// Ensure attribute default is within required size
|
||||||
$validator = new Text($size, 0);
|
$validator = new Text($size, 0);
|
||||||
|
@ -1098,6 +1099,12 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string
|
||||||
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription());
|
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$filters = [];
|
||||||
|
|
||||||
|
if ($encrypt) {
|
||||||
|
$filters[] = 'encrypt';
|
||||||
|
}
|
||||||
|
|
||||||
$attribute = createAttribute($databaseId, $collectionId, new Document([
|
$attribute = createAttribute($databaseId, $collectionId, new Document([
|
||||||
'key' => $key,
|
'key' => $key,
|
||||||
'type' => Database::VAR_STRING,
|
'type' => Database::VAR_STRING,
|
||||||
|
@ -1105,6 +1112,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string
|
||||||
'required' => $required,
|
'required' => $required,
|
||||||
'default' => $default,
|
'default' => $default,
|
||||||
'array' => $array,
|
'array' => $array,
|
||||||
|
'filters' => $filters,
|
||||||
]), $response, $dbForProject, $database, $events);
|
]), $response, $dbForProject, $database, $events);
|
||||||
|
|
||||||
$response
|
$response
|
||||||
|
@ -1509,6 +1517,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti
|
||||||
->inject('events')
|
->inject('events')
|
||||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Event $events) {
|
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Event $events) {
|
||||||
|
|
||||||
|
$filters[] = 'datetime';
|
||||||
|
|
||||||
$attribute = createAttribute($databaseId, $collectionId, new Document([
|
$attribute = createAttribute($databaseId, $collectionId, new Document([
|
||||||
'key' => $key,
|
'key' => $key,
|
||||||
'type' => Database::VAR_DATETIME,
|
'type' => Database::VAR_DATETIME,
|
||||||
|
@ -1516,7 +1526,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti
|
||||||
'required' => $required,
|
'required' => $required,
|
||||||
'default' => $default,
|
'default' => $default,
|
||||||
'array' => $array,
|
'array' => $array,
|
||||||
'filters' => ['datetime']
|
'filters' => $filters,
|
||||||
]), $response, $dbForProject, $database, $events);
|
]), $response, $dbForProject, $database, $events);
|
||||||
|
|
||||||
$response
|
$response
|
||||||
|
@ -1739,6 +1749,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin
|
||||||
->inject('dbForProject')
|
->inject('dbForProject')
|
||||||
->inject('events')
|
->inject('events')
|
||||||
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $events) {
|
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $events) {
|
||||||
|
|
||||||
$attribute = updateAttribute(
|
$attribute = updateAttribute(
|
||||||
databaseId: $databaseId,
|
databaseId: $databaseId,
|
||||||
collectionId: $collectionId,
|
collectionId: $collectionId,
|
||||||
|
|
|
@ -443,6 +443,11 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||||
throw new Exception(Exception::STORAGE_INVALID_CONTENT_RANGE);
|
throw new Exception(Exception::STORAGE_INVALID_CONTENT_RANGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$idValidator = new UID();
|
||||||
|
if (!$idValidator->isValid($request->getHeader('x-appwrite-id'))) {
|
||||||
|
throw new Exception(Exception::STORAGE_INVALID_APPWRITE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO remove the condition that checks `$end === $fileSize` in next breaking version
|
// TODO remove the condition that checks `$end === $fileSize` in next breaking version
|
||||||
if ($end === $fileSize - 1 || $end === $fileSize) {
|
if ($end === $fileSize - 1 || $end === $fileSize) {
|
||||||
//if it's a last chunks the chunk size might differ, so we set the $chunks and $chunk to -1 notify it's last chunk
|
//if it's a last chunks the chunk size might differ, so we set the $chunks and $chunk to -1 notify it's last chunk
|
||||||
|
|
|
@ -115,6 +115,7 @@ class Exception extends \Exception
|
||||||
public const STORAGE_BUCKET_NOT_FOUND = 'storage_bucket_not_found';
|
public const STORAGE_BUCKET_NOT_FOUND = 'storage_bucket_not_found';
|
||||||
public const STORAGE_INVALID_CONTENT_RANGE = 'storage_invalid_content_range';
|
public const STORAGE_INVALID_CONTENT_RANGE = 'storage_invalid_content_range';
|
||||||
public const STORAGE_INVALID_RANGE = 'storage_invalid_range';
|
public const STORAGE_INVALID_RANGE = 'storage_invalid_range';
|
||||||
|
public const STORAGE_INVALID_APPWRITE_ID = 'storage_invalid_appwrite_id';
|
||||||
|
|
||||||
/** Functions */
|
/** Functions */
|
||||||
public const FUNCTION_NOT_FOUND = 'function_not_found';
|
public const FUNCTION_NOT_FOUND = 'function_not_found';
|
||||||
|
|
|
@ -593,6 +593,110 @@ class DatabasesCustomServerTest extends Scope
|
||||||
$this->assertFalse($collection['body']['enabled']);
|
$this->assertFalse($collection['body']['enabled']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @depends testListCollections
|
||||||
|
*/
|
||||||
|
public function testCreateEncryptedAttribute(array $data): void
|
||||||
|
{
|
||||||
|
|
||||||
|
$databaseId = $data['databaseId'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for SUCCESS
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Create collection
|
||||||
|
$actors = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||||
|
]), [
|
||||||
|
'collectionId' => ID::unique(),
|
||||||
|
'name' => 'Encrypted Actors Data',
|
||||||
|
'permissions' => [
|
||||||
|
Permission::read(Role::any()),
|
||||||
|
Permission::create(Role::any()),
|
||||||
|
Permission::update(Role::any()),
|
||||||
|
Permission::delete(Role::any()),
|
||||||
|
],
|
||||||
|
'documentSecurity' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $actors['headers']['status-code']);
|
||||||
|
$this->assertEquals($actors['body']['name'], 'Encrypted Actors Data');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for creating encrypted attributes
|
||||||
|
*/
|
||||||
|
|
||||||
|
$attributesPath = '/databases/' . $databaseId . '/collections/' . $actors['body']['$id'] . '/attributes';
|
||||||
|
|
||||||
|
$firstName = $this->client->call(Client::METHOD_POST, $attributesPath . '/string', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||||
|
]), [
|
||||||
|
'key' => 'firstName',
|
||||||
|
'size' => 256,
|
||||||
|
'required' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$lastName = $this->client->call(Client::METHOD_POST, $attributesPath . '/string', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||||
|
]), [
|
||||||
|
'key' => 'lastName',
|
||||||
|
'size' => 256,
|
||||||
|
'required' => true,
|
||||||
|
'encrypt' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check status of every attribute
|
||||||
|
*/
|
||||||
|
$this->assertEquals(202, $firstName['headers']['status-code']);
|
||||||
|
$this->assertEquals('firstName', $firstName['body']['key']);
|
||||||
|
$this->assertEquals('string', $firstName['body']['type']);
|
||||||
|
|
||||||
|
$this->assertEquals(202, $lastName['headers']['status-code']);
|
||||||
|
$this->assertEquals('lastName', $lastName['body']['key']);
|
||||||
|
$this->assertEquals('string', $lastName['body']['type']);
|
||||||
|
|
||||||
|
// Wait for database worker to finish creating attributes
|
||||||
|
sleep(2);
|
||||||
|
|
||||||
|
// Creating document to ensure cache is purged on schema change
|
||||||
|
$document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $actors['body']['$id'] . '/documents', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||||
|
]), [
|
||||||
|
'documentId' => ID::unique(),
|
||||||
|
'data' => [
|
||||||
|
'firstName' => 'Jonah',
|
||||||
|
'lastName' => 'Jameson',
|
||||||
|
],
|
||||||
|
'permissions' => [
|
||||||
|
Permission::read(Role::any()),
|
||||||
|
Permission::update(Role::any()),
|
||||||
|
Permission::delete(Role::any()),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Check document to ensure cache is purged on schema change
|
||||||
|
$document = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $actors['body']['$id'] . '/documents/' . $document['body']['$id'], array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||||
|
]));
|
||||||
|
|
||||||
|
$this->assertEquals(200, $document['headers']['status-code']);
|
||||||
|
$this->assertEquals('Jonah', $document['body']['firstName']);
|
||||||
|
$this->assertEquals('Jameson', $document['body']['lastName']);
|
||||||
|
}
|
||||||
|
|
||||||
public function testDeleteAttribute(): array
|
public function testDeleteAttribute(): array
|
||||||
{
|
{
|
||||||
$database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([
|
$database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([
|
||||||
|
|
|
@ -241,6 +241,29 @@ trait StorageBase
|
||||||
|
|
||||||
$this->assertEquals(400, $failedBucket['headers']['status-code']);
|
$this->assertEquals(400, $failedBucket['headers']['status-code']);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for FAILURE set x-appwrite-id to unique()
|
||||||
|
*/
|
||||||
|
$source = realpath(__DIR__ . '/../../../resources/logo.png');
|
||||||
|
$totalSize = \filesize($source);
|
||||||
|
$res = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', array_merge([
|
||||||
|
'content-type' => 'multipart/form-data',
|
||||||
|
'x-appwrite-project' => $this->getProject()['$id'],
|
||||||
|
'content-range' => 'bytes 0-' . $size . '/' . $size,
|
||||||
|
'x-appwrite-id' => 'unique()',
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'fileId' => ID::unique(),
|
||||||
|
'file' => new CURLFile($source, 'image/png', 'logo.png'),
|
||||||
|
'permissions' => [
|
||||||
|
Permission::read(Role::any()),
|
||||||
|
Permission::update(Role::any()),
|
||||||
|
Permission::delete(Role::any()),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(400, $res['headers']['status-code']);
|
||||||
|
$this->assertEquals('The value for x-appwrite-id header is invalid. Please check the value of the x-appwrite-id header is valid id and not unique().', $res['body']['message']);
|
||||||
|
|
||||||
return ['bucketId' => $bucketId, 'fileId' => $file['body']['$id'], 'largeFileId' => $largeFile['body']['$id'], 'largeBucketId' => $bucket2['body']['$id']];
|
return ['bucketId' => $bucketId, 'fileId' => $file['body']['$id'], 'largeFileId' => $largeFile['body']['$id'], 'largeBucketId' => $bucket2['body']['$id']];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ namespace Tests\E2E\Services\Teams;
|
||||||
|
|
||||||
use Tests\E2E\Client;
|
use Tests\E2E\Client;
|
||||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||||
|
use Utopia\Database\Helpers\ID;
|
||||||
|
|
||||||
trait TeamsBaseServer
|
trait TeamsBaseServer
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue