diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index cbb7d9dbe0..692861d44d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -24,7 +24,7 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Login to Docker Hub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bc706b1ec9..84fc4c9fba 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,7 +26,7 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Login to Docker Hub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} diff --git a/app/cli.php b/app/cli.php index 14ebea6e1c..0f8426afd9 100644 --- a/app/cli.php +++ b/app/cli.php @@ -30,9 +30,6 @@ use Utopia\Pools\Group; use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Queue\Publisher; use Utopia\Registry\Registry; -use Utopia\Span\Exporter; -use Utopia\Span\Span; -use Utopia\Span\Storage; use Utopia\System\System; use Utopia\Telemetry\Adapter\None as NoTelemetry; @@ -340,6 +337,5 @@ $cli $cli->shutdown()->action(fn () => Timer::clearAll()); Runtime::enableCoroutine(SWOOLE_HOOK_ALL); -Span::setStorage(new Storage\Coroutine()); -Span::addExporter(new Exporter\Stdout()); +require_once __DIR__ . '/init/span.php'; run($cli->run(...)); diff --git a/app/config/cors.php b/app/config/cors.php new file mode 100644 index 0000000000..ef1adeb998 --- /dev/null +++ b/app/config/cors.php @@ -0,0 +1,53 @@ + ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], + 'allowedHeaders' => [ + 'Accept', + 'Origin', + 'Cookie', + 'Set-Cookie', + // Content + 'Content-Type', + 'Content-Range', + // Appwrite + 'X-Appwrite-Project', + 'X-Appwrite-Key', + 'X-Appwrite-Dev-Key', + 'X-Appwrite-Locale', + 'X-Appwrite-Mode', + 'X-Appwrite-JWT', + 'X-Appwrite-Response-Format', + 'X-Appwrite-Timeout', + 'X-Appwrite-ID', + 'X-Appwrite-Timestamp', + 'X-Appwrite-Session', + 'X-Appwrite-Platform', + // SDK generator + 'X-SDK-Version', + 'X-SDK-Name', + 'X-SDK-Language', + 'X-SDK-Platform', + 'X-SDK-GraphQL', + 'X-SDK-Profile', + // Caching + 'Range', + 'Cache-Control', + 'Expires', + 'Pragma', + // Server to server + 'X-Fallback-Cookies', + 'X-Requested-With', + 'X-Forwarded-For', + 'X-Forwarded-User-Agent', + ], + 'exposedHeaders' => [ + 'X-Appwrite-Session', + 'X-Fallback-Cookies', + ], +]; diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index 91975d2d2e..b57aea9ddc 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -9721,6 +9721,11 @@ "type": "boolean", "description": "Is attribute an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "x-example": false } }, "required": [ @@ -9944,6 +9949,11 @@ "type": "boolean", "description": "Is attribute an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "x-example": false } }, "required": [ @@ -11034,6 +11044,11 @@ "type": "boolean", "description": "Is attribute an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "x-example": false } }, "required": [ @@ -11496,6 +11511,11 @@ "type": "boolean", "description": "Is attribute an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "x-example": false } }, "required": [ @@ -40701,6 +40721,11 @@ "type": "boolean", "description": "Is column an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "x-example": false } }, "required": [ @@ -40930,6 +40955,11 @@ "type": "boolean", "description": "Is column an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "x-example": false } }, "required": [ @@ -42027,6 +42057,11 @@ "type": "boolean", "description": "Is column an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "x-example": false } }, "required": [ @@ -42493,6 +42528,11 @@ "type": "boolean", "description": "Is column an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "x-example": false } }, "required": [ @@ -54361,6 +54401,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -54383,7 +54429,8 @@ "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "size": 128, - "default": "default" + "default": "default", + "encrypt": false } }, "attributeText": { @@ -54444,6 +54491,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -54464,7 +54517,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "attributeMediumtext": { @@ -54525,6 +54579,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -54545,7 +54605,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "attributeLongtext": { @@ -54606,6 +54667,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -54626,7 +54693,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "table": { @@ -56182,6 +56250,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -56204,7 +56278,8 @@ "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "size": 128, - "default": "default" + "default": "default", + "encrypt": false } }, "columnText": { @@ -56265,6 +56340,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -56285,7 +56366,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "columnMediumtext": { @@ -56346,6 +56428,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -56366,7 +56454,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "columnLongtext": { @@ -56427,6 +56516,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -56447,7 +56542,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "index": { diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index 89ad0cb0ff..016d44fbc4 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -9217,6 +9217,11 @@ "type": "boolean", "description": "Is attribute an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "x-example": false } }, "required": [ @@ -9442,6 +9447,11 @@ "type": "boolean", "description": "Is attribute an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "x-example": false } }, "required": [ @@ -10541,6 +10551,11 @@ "type": "boolean", "description": "Is attribute an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "x-example": false } }, "required": [ @@ -11007,6 +11022,11 @@ "type": "boolean", "description": "Is attribute an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "x-example": false } }, "required": [ @@ -30357,6 +30377,11 @@ "type": "boolean", "description": "Is column an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "x-example": false } }, "required": [ @@ -30588,6 +30613,11 @@ "type": "boolean", "description": "Is column an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "x-example": false } }, "required": [ @@ -31694,6 +31724,11 @@ "type": "boolean", "description": "Is column an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "x-example": false } }, "required": [ @@ -32164,6 +32199,11 @@ "type": "boolean", "description": "Is column an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "x-example": false } }, "required": [ @@ -42407,6 +42447,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -42429,7 +42475,8 @@ "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "size": 128, - "default": "default" + "default": "default", + "encrypt": false } }, "attributeText": { @@ -42490,6 +42537,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -42510,7 +42563,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "attributeMediumtext": { @@ -42571,6 +42625,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -42591,7 +42651,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "attributeLongtext": { @@ -42652,6 +42713,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -42672,7 +42739,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "table": { @@ -44228,6 +44296,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -44250,7 +44324,8 @@ "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "size": 128, - "default": "default" + "default": "default", + "encrypt": false } }, "columnText": { @@ -44311,6 +44386,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -44331,7 +44412,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "columnMediumtext": { @@ -44392,6 +44474,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -44412,7 +44500,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "columnLongtext": { @@ -44473,6 +44562,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -44493,7 +44588,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "index": { diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 91975d2d2e..b57aea9ddc 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -9721,6 +9721,11 @@ "type": "boolean", "description": "Is attribute an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "x-example": false } }, "required": [ @@ -9944,6 +9949,11 @@ "type": "boolean", "description": "Is attribute an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "x-example": false } }, "required": [ @@ -11034,6 +11044,11 @@ "type": "boolean", "description": "Is attribute an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "x-example": false } }, "required": [ @@ -11496,6 +11511,11 @@ "type": "boolean", "description": "Is attribute an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "x-example": false } }, "required": [ @@ -40701,6 +40721,11 @@ "type": "boolean", "description": "Is column an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "x-example": false } }, "required": [ @@ -40930,6 +40955,11 @@ "type": "boolean", "description": "Is column an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "x-example": false } }, "required": [ @@ -42027,6 +42057,11 @@ "type": "boolean", "description": "Is column an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "x-example": false } }, "required": [ @@ -42493,6 +42528,11 @@ "type": "boolean", "description": "Is column an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "x-example": false } }, "required": [ @@ -54361,6 +54401,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -54383,7 +54429,8 @@ "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "size": 128, - "default": "default" + "default": "default", + "encrypt": false } }, "attributeText": { @@ -54444,6 +54491,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -54464,7 +54517,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "attributeMediumtext": { @@ -54525,6 +54579,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -54545,7 +54605,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "attributeLongtext": { @@ -54606,6 +54667,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -54626,7 +54693,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "table": { @@ -56182,6 +56250,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -56204,7 +56278,8 @@ "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "size": 128, - "default": "default" + "default": "default", + "encrypt": false } }, "columnText": { @@ -56265,6 +56340,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -56285,7 +56366,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "columnMediumtext": { @@ -56346,6 +56428,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -56366,7 +56454,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "columnLongtext": { @@ -56427,6 +56516,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -56447,7 +56542,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "index": { diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 89ad0cb0ff..016d44fbc4 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -9217,6 +9217,11 @@ "type": "boolean", "description": "Is attribute an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "x-example": false } }, "required": [ @@ -9442,6 +9447,11 @@ "type": "boolean", "description": "Is attribute an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "x-example": false } }, "required": [ @@ -10541,6 +10551,11 @@ "type": "boolean", "description": "Is attribute an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "x-example": false } }, "required": [ @@ -11007,6 +11022,11 @@ "type": "boolean", "description": "Is attribute an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "x-example": false } }, "required": [ @@ -30357,6 +30377,11 @@ "type": "boolean", "description": "Is column an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "x-example": false } }, "required": [ @@ -30588,6 +30613,11 @@ "type": "boolean", "description": "Is column an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "x-example": false } }, "required": [ @@ -31694,6 +31724,11 @@ "type": "boolean", "description": "Is column an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "x-example": false } }, "required": [ @@ -32164,6 +32199,11 @@ "type": "boolean", "description": "Is column an array?", "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "x-example": false } }, "required": [ @@ -42407,6 +42447,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -42429,7 +42475,8 @@ "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "size": 128, - "default": "default" + "default": "default", + "encrypt": false } }, "attributeText": { @@ -42490,6 +42537,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -42510,7 +42563,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "attributeMediumtext": { @@ -42571,6 +42625,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -42591,7 +42651,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "attributeLongtext": { @@ -42652,6 +42713,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -42672,7 +42739,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "table": { @@ -44228,6 +44296,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -44250,7 +44324,8 @@ "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "size": 128, - "default": "default" + "default": "default", + "encrypt": false } }, "columnText": { @@ -44311,6 +44386,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -44331,7 +44412,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "columnMediumtext": { @@ -44392,6 +44474,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -44412,7 +44500,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "columnLongtext": { @@ -44473,6 +44562,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -44493,7 +44588,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "index": { diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index 41b48860a1..06673c7ad9 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -9803,6 +9803,12 @@ "description": "Is attribute an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -10023,6 +10029,12 @@ "description": "Is attribute an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -11076,6 +11088,12 @@ "description": "Is attribute an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -11533,6 +11551,12 @@ "description": "Is attribute an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -40692,6 +40716,12 @@ "description": "Is column an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -40918,6 +40948,12 @@ "description": "Is column an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -41978,6 +42014,12 @@ "description": "Is column an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -42439,6 +42481,12 @@ "description": "Is column an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -54165,6 +54213,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -54187,7 +54241,8 @@ "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "size": 128, - "default": "default" + "default": "default", + "encrypt": false } }, "attributeText": { @@ -54248,6 +54303,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -54268,7 +54329,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "attributeMediumtext": { @@ -54329,6 +54391,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -54349,7 +54417,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "attributeLongtext": { @@ -54410,6 +54479,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -54430,7 +54505,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "table": { @@ -55987,6 +56063,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -56009,7 +56091,8 @@ "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "size": 128, - "default": "default" + "default": "default", + "encrypt": false } }, "columnText": { @@ -56070,6 +56153,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -56090,7 +56179,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "columnMediumtext": { @@ -56151,6 +56241,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -56171,7 +56267,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "columnLongtext": { @@ -56232,6 +56329,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -56252,7 +56355,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "index": { diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index 29262b5dd9..d61880e9b8 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -9283,6 +9283,12 @@ "description": "Is attribute an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -9505,6 +9511,12 @@ "description": "Is attribute an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -10567,6 +10579,12 @@ "description": "Is attribute an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -11028,6 +11046,12 @@ "description": "Is attribute an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -30404,6 +30428,12 @@ "description": "Is column an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -30632,6 +30662,12 @@ "description": "Is column an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -31701,6 +31737,12 @@ "description": "Is column an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -32166,6 +32208,12 @@ "description": "Is column an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -42308,6 +42356,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -42330,7 +42384,8 @@ "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "size": 128, - "default": "default" + "default": "default", + "encrypt": false } }, "attributeText": { @@ -42391,6 +42446,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -42411,7 +42472,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "attributeMediumtext": { @@ -42472,6 +42534,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -42492,7 +42560,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "attributeLongtext": { @@ -42553,6 +42622,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -42573,7 +42648,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "table": { @@ -44130,6 +44206,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -44152,7 +44234,8 @@ "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "size": 128, - "default": "default" + "default": "default", + "encrypt": false } }, "columnText": { @@ -44213,6 +44296,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -44233,7 +44322,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "columnMediumtext": { @@ -44294,6 +44384,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -44314,7 +44410,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "columnLongtext": { @@ -44375,6 +44472,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -44395,7 +44498,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "index": { diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 41b48860a1..06673c7ad9 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -9803,6 +9803,12 @@ "description": "Is attribute an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -10023,6 +10029,12 @@ "description": "Is attribute an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -11076,6 +11088,12 @@ "description": "Is attribute an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -11533,6 +11551,12 @@ "description": "Is attribute an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -40692,6 +40716,12 @@ "description": "Is column an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -40918,6 +40948,12 @@ "description": "Is column an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -41978,6 +42014,12 @@ "description": "Is column an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -42439,6 +42481,12 @@ "description": "Is column an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -54165,6 +54213,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -54187,7 +54241,8 @@ "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "size": 128, - "default": "default" + "default": "default", + "encrypt": false } }, "attributeText": { @@ -54248,6 +54303,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -54268,7 +54329,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "attributeMediumtext": { @@ -54329,6 +54391,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -54349,7 +54417,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "attributeLongtext": { @@ -54410,6 +54479,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -54430,7 +54505,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "table": { @@ -55987,6 +56063,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -56009,7 +56091,8 @@ "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "size": 128, - "default": "default" + "default": "default", + "encrypt": false } }, "columnText": { @@ -56070,6 +56153,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -56090,7 +56179,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "columnMediumtext": { @@ -56151,6 +56241,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -56171,7 +56267,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "columnLongtext": { @@ -56232,6 +56329,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -56252,7 +56355,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "index": { diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index 29262b5dd9..d61880e9b8 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -9283,6 +9283,12 @@ "description": "Is attribute an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -9505,6 +9511,12 @@ "description": "Is attribute an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -10567,6 +10579,12 @@ "description": "Is attribute an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -11028,6 +11046,12 @@ "description": "Is attribute an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -30404,6 +30428,12 @@ "description": "Is column an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -30632,6 +30662,12 @@ "description": "Is column an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -31701,6 +31737,12 @@ "description": "Is column an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -32166,6 +32208,12 @@ "description": "Is column an array?", "default": false, "x-example": false + }, + "encrypt": { + "type": "boolean", + "description": "Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.", + "default": false, + "x-example": false } }, "required": [ @@ -42308,6 +42356,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -42330,7 +42384,8 @@ "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "size": 128, - "default": "default" + "default": "default", + "encrypt": false } }, "attributeText": { @@ -42391,6 +42446,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -42411,7 +42472,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "attributeMediumtext": { @@ -42472,6 +42534,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -42492,7 +42560,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "attributeLongtext": { @@ -42553,6 +42622,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -42573,7 +42648,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "table": { @@ -44130,6 +44206,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -44152,7 +44234,8 @@ "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "size": 128, - "default": "default" + "default": "default", + "encrypt": false } }, "columnText": { @@ -44213,6 +44296,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -44233,7 +44322,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "columnMediumtext": { @@ -44294,6 +44384,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -44314,7 +44410,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "columnLongtext": { @@ -44375,6 +44472,12 @@ "description": "Default value for column when not provided. Cannot be set when column is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this column is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -44395,7 +44498,8 @@ "array": false, "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", - "default": "default" + "default": "default", + "encrypt": false } }, "index": { diff --git a/app/controllers/general.php b/app/controllers/general.php index a4f364485e..2d3bde69d6 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -52,6 +52,7 @@ use Utopia\Logger\Log; use Utopia\Logger\Log\User; use Utopia\Logger\Logger; use Utopia\Platform\Service; +use Utopia\Span\Span; use Utopia\System\System; use Utopia\Validator; use Utopia\Validator\Text; @@ -1245,17 +1246,7 @@ Http::error() $trace = $error->getTrace(); if (php_sapi_name() === 'cli') { - Console::error('[Error] Timestamp: ' . date('c', time())); - - if ($route) { - Console::error('[Error] Method: ' . $route->getMethod()); - Console::error('[Error] URL: ' . $route->getPath()); - } - - Console::error('[Error] Type: ' . get_class($error)); - Console::error('[Error] Message: ' . $message); - Console::error('[Error] File: ' . $file); - Console::error('[Error] Line: ' . $line); + Span::error($error); } switch ($class) { diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 4c2dc323e9..ecea032a89 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -30,6 +30,7 @@ use Utopia\Database\Document; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Authorization\Input; +use Utopia\Database\Validator\Roles; use Utopia\Http\Http; use Utopia\System\System; use Utopia\Telemetry\Adapter as Telemetry; @@ -169,8 +170,10 @@ Http::init() // Handle special app role case if ($apiKey->getRole() === User::ROLE_APPS) { - // Disable authorization checks for API keys - $authorization->setDefaultStatus(false); + // Disable authorization checks for project API keys + if (($apiKey->getType() === API_KEY_STANDARD || $apiKey->getType() === API_KEY_DYNAMIC) && $apiKey->getProjectId() === $project->getId()) { + $authorization->setDefaultStatus(false); + } $user = new User([ '$id' => '', @@ -247,6 +250,35 @@ Http::init() $queueForAudits->setUser($user); } + + // Apply permission + if ($apiKey->getType() === API_KEY_ORGANIZATION) { + $authorization->addRole(Role::team($team->getId())->toString()); + $authorization->addRole(Role::team($team->getId(), 'owner')->toString()); + } elseif ($apiKey->getType() === API_KEY_ACCOUNT) { + $authorization->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::users()->toString()); + + if ($user->getAttribute('emailVerification', false) || $user->getAttribute('phoneVerification', false)) { + $authorization->addRole(Role::user($user->getId(), Roles::DIMENSION_VERIFIED)->toString()); + $authorization->addRole(Role::users(Roles::DIMENSION_VERIFIED)->toString()); + } else { + $authorization->addRole(Role::user($user->getId(), Roles::DIMENSION_UNVERIFIED)->toString()); + $authorization->addRole(Role::users(Roles::DIMENSION_UNVERIFIED)->toString()); + } + + foreach (\array_filter($user->getAttribute('memberships', []), fn ($membership) => ($membership['confirm'] ?? false) === true) as $nodeMembership) { + $authorization->addRole(Role::team($nodeMembership['teamId'])->toString()); + $authorization->addRole(Role::member($nodeMembership->getId())->toString()); + foreach (($nodeMembership['roles'] ?? []) as $nodeRole) { + $authorization->addRole(Role::team($nodeMembership['teamId'], $nodeRole)->toString()); + } + } + + foreach ($user->getAttribute('labels', []) as $nodeLabel) { + $authorization->addRole('label:' . $nodeLabel); + } + } } // Admin User Authentication elseif (($project->getId() === 'console' && !$team->isEmpty() && !$user->isEmpty()) || ($project->getId() !== 'console' && !$user->isEmpty() && $mode === APP_MODE_ADMIN)) { $teamId = $team->getId(); @@ -536,6 +568,7 @@ Http::init() $queueForMessaging->setProject($project); $queueForFunctions->setProject($project); $queueForBuilds->setProject($project); + $queueForMails->setProject($project); /* Auto-set platforms */ $queueForFunctions->setPlatform($platform); diff --git a/app/http.php b/app/http.php index d5af8eacea..1d7949da86 100644 --- a/app/http.php +++ b/app/http.php @@ -1,6 +1,7 @@ on(Constant::EVENT_WORKER_START, function ($server, $workerId) use (&$fil $files = new Files(); $files->load(__DIR__ . '/../public'); } - Console::success('Worker ' . ++$workerId . ' started successfully'); }); $http->on(Constant::EVENT_WORKER_STOP, function ($server, $workerId) { @@ -207,17 +208,18 @@ function createDatabase(Http $app, string $resourceKey, string $dbName, array $c } } - Console::success("[Setup] - $dbName database init started..."); + Span::init("database.setup"); + Span::add('database.name', $dbName); // Attempt to create the database try { - Console::info(" └── Creating database: $dbName..."); $database->create(); } catch (\Exception $e) { - Console::info(" └── Skip: metadata table already exists"); + Span::add('database.exists', true); } // Process collections + $collectionsCreated = 0; foreach ($collections as $key => $collection) { if (($collection['$collection'] ?? '') !== Database::METADATA) { continue; @@ -227,8 +229,6 @@ function createDatabase(Http $app, string $resourceKey, string $dbName, array $c continue; } - Console::info(" └── Creating collection: {$collection['$id']}..."); - $attributes = array_map(fn ($attr) => new Document([ '$id' => ID::custom($attr['$id']), 'type' => $attr['type'], @@ -250,14 +250,19 @@ function createDatabase(Http $app, string $resourceKey, string $dbName, array $c ]), $collection['indexes']); $database->createCollection($key, $attributes, $indexes); + $collectionsCreated++; } + Span::add('database.collections_created', $collectionsCreated); + if ($extraSetup) { $extraSetup($database); } + + Span::current()?->finish(); } -$http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $register) { +$http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $totalWorkers, $register) { $app = new Http('UTC'); go(function () use ($register, $app) { @@ -282,7 +287,6 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg } if ($dbForPlatform->getDocument('buckets', 'default')->isEmpty()) { - Console::info(" └── Creating default bucket..."); $dbForPlatform->createDocument('buckets', new Document([ '$id' => ID::custom('default'), '$collection' => ID::custom('buckets'), @@ -305,7 +309,6 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg $bucket = $dbForPlatform->getDocument('buckets', 'default'); - Console::info(" └── Creating files collection for default bucket..."); $files = $collections['buckets']['files'] ?? []; if (empty($files)) { throw new Exception('Files collection is not configured.'); @@ -335,7 +338,6 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg } if ($authorization->skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots')->isEmpty())) { - Console::info(" └── Creating screenshots bucket..."); $authorization->skip(fn () => $dbForPlatform->createDocument('buckets', new Document([ '$id' => ID::custom('screenshots'), '$collection' => ID::custom('buckets'), @@ -353,7 +355,6 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg $bucket = $authorization->skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots')); - Console::info(" └── Creating files collection for screenshots bucket..."); $files = $collections['buckets']['files'] ?? []; if (empty($files)) { throw new Exception('Files collection is not configured.'); @@ -391,6 +392,9 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg $cache = $app->getResource('cache'); foreach ($sharedTablesV2 as $hostname) { + Span::init('database.setup'); + Span::add('database.hostname', $hostname); + $adapter = new DatabasePool($pools->get($hostname)); $dbForProject = (new Database($adapter, $cache)) ->setDatabase('appwrite') @@ -399,10 +403,9 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg ->setNamespace(System::getEnv('_APP_DATABASE_SHARED_NAMESPACE', '')); try { - Console::success('[Setup] - Creating project database: ' . $hostname . '...'); $dbForProject->create(); } catch (DuplicateException) { - Console::success('[Setup] - Skip: metadata table already exists'); + Span::add('database.exists', true); } if ($dbForProject->getCollection(AuditAdapterSQL::COLLECTION)->isEmpty()) { @@ -411,6 +414,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg $audit->setup(); } + $collectionsCreated = 0; foreach ($projectCollections as $key => $collection) { if (($collection['$collection'] ?? '') !== Database::METADATA) { continue; @@ -422,17 +426,21 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg $attributes = \array_map(fn ($attribute) => new Document($attribute), $collection['attributes']); $indexes = \array_map(fn (array $index) => new Document($index), $collection['indexes']); - Console::success('[Setup] - Creating project collection: ' . $collection['$id'] . '...'); - $dbForProject->createCollection($key, $attributes, $indexes); + $collectionsCreated++; } - } - Console::success('[Setup] - Server database init completed...'); + Span::add('database.collections_created', $collectionsCreated); + Span::current()?->finish(); + } }); - Console::success('Server started successfully (max payload is ' . number_format($payloadSize) . ' bytes)'); - Console::info("Master pid {$http->master_pid}, manager pid {$http->manager_pid}"); + Span::init('http.server.start'); + Span::add('server.workers', $totalWorkers); + Span::add('server.payload_size', $payloadSize); + Span::add('server.master_pid', $http->master_pid); + Span::add('server.manager_pid', $http->manager_pid); + Span::current()?->finish(); // Start the task that starts fetching custom domains $http->task([], 0); @@ -445,12 +453,16 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg }); $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) use ($register, &$files) { + Span::init('http.request'); + Http::setResource('swooleRequest', fn () => $swooleRequest); Http::setResource('swooleResponse', fn () => $swooleResponse); $request = new Request($swooleRequest); $response = new Response($swooleResponse); + Span::add('http.method', $request->getMethod()); + if ($files instanceof Files && $files->isFileLoaded($request->getURI())) { $time = (60 * 60 * 24 * 45); // 45 days cache @@ -479,7 +491,12 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool $authorization->addRole(Role::any()->toString()); $app->run($request, $response); + + $route = $app->getRoute(); + Span::add('http.path', $route?->getPath() ?? 'unknown'); } catch (\Throwable $th) { + Span::error($th); + $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); $logger = $app->getResource("logger"); @@ -542,12 +559,6 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool } } - Console::error('[Error] Type: ' . get_class($th)); - Console::error('[Error] Message: ' . $th->getMessage()); - Console::error('[Error] File: ' . $th->getFile()); - Console::error('[Error] Line: ' . $th->getLine()); - Console::error('[Error] Trace: ' . $th->getTraceAsString()); - $swooleResponse->setStatusCode(500); $output = ((Http::isDevelopment())) ? [ @@ -564,6 +575,9 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool ]; $swooleResponse->end(\json_encode($output)); + } finally { + Span::add('http.response.code', $response->getStatusCode()); + Span::current()?->finish(); } }); diff --git a/app/init/configs.php b/app/init/configs.php index d5748707cf..35c8e3899d 100644 --- a/app/init/configs.php +++ b/app/init/configs.php @@ -46,3 +46,4 @@ Config::load('storage-outputs', __DIR__ . '/../config/storage/outputs.php', $con Config::load('specifications', __DIR__ . '/../config/specifications.php', $configAdapter); Config::load('templates-function', __DIR__ . '/../config/templates/function.php', $configAdapter); Config::load('templates-site', __DIR__ . '/../config/templates/site.php', $configAdapter); +Config::load('cors', __DIR__ . '/../config/cors.php', $configAdapter); diff --git a/app/init/resources.php b/app/init/resources.php index cdc2e8a367..0bb5b17ce6 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -300,54 +300,17 @@ Http::setResource('rule', function (Request $request, Database $dbForPlatform, D /** * CORS service */ -Http::setResource('cors', fn (array $allowedHostnames) => new Cors( - $allowedHostnames, - allowedMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], - allowedHeaders: [ - 'Accept', - 'Origin', - 'Cookie', - 'Set-Cookie', - // Content - 'Content-Type', - 'Content-Range', - // Appwrite - 'X-Appwrite-Project', - 'X-Appwrite-Key', - 'X-Appwrite-Dev-Key', - 'X-Appwrite-Locale', - 'X-Appwrite-Mode', - 'X-Appwrite-JWT', - 'X-Appwrite-Response-Format', - 'X-Appwrite-Timeout', - 'X-Appwrite-ID', - 'X-Appwrite-Timestamp', - 'X-Appwrite-Session', - 'X-Appwrite-Platform', // for `$platform` injection and SDK generator - // SDK generator - 'X-SDK-Version', - 'X-SDK-Name', - 'X-SDK-Language', - 'X-SDK-Platform', - 'X-SDK-GraphQL', - 'X-SDK-Profile', - // Caching - 'Range', - 'Cache-Control', - 'Expires', - 'Pragma', - // Server to server - 'X-Fallback-Cookies', - 'X-Requested-With', - 'X-Forwarded-For', - 'X-Forwarded-User-Agent', - ], - allowCredentials: true, - exposedHeaders: [ - 'X-Appwrite-Session', - 'X-Fallback-Cookies', - ], -), ['allowedHostnames']); +Http::setResource('cors', function (array $allowedHostnames) { + $corsConfig = Config::getParam('cors'); + + return new Cors( + $allowedHostnames, + allowedMethods: $corsConfig['allowedMethods'], + allowedHeaders: $corsConfig['allowedHeaders'], + allowCredentials: true, + exposedHeaders: $corsConfig['exposedHeaders'], + ); +}, ['allowedHostnames']); Http::setResource('originValidator', function (Document $devKey, array $allowedHostnames, array $allowedSchemes) { if (!$devKey->isEmpty()) { @@ -1304,6 +1267,7 @@ Http::setResource('team', function (Document $project, Database $dbForPlatform, } else { $route = $utopia->match($request); $path = !empty($route) ? $route->getPath() : $request->getURI(); + $orgHeader = $request->getHeader('x-appwrite-organization', ''); if (str_starts_with($path, '/v1/projects/:projectId')) { $uri = $request->getURI(); $pid = explode('/', $uri)[3]; @@ -1318,6 +1282,8 @@ Http::setResource('team', function (Document $project, Database $dbForPlatform, $team = $authorization->skip(fn () => $dbForPlatform->getDocument('teams', $teamId)); return $team; + } elseif (!empty($orgHeader)) { + return $authorization->skip(fn () => $dbForPlatform->getDocument('teams', $orgHeader)); } } diff --git a/app/init/span.php b/app/init/span.php new file mode 100644 index 0000000000..76f37f5300 --- /dev/null +++ b/app/init/span.php @@ -0,0 +1,8 @@ + $register); diff --git a/composer.json b/composer.json index 354762e90b..b7d3bc5483 100644 --- a/composer.json +++ b/composer.json @@ -57,7 +57,7 @@ "utopia-php/detector": "0.2.*", "utopia-php/domains": "1.*", "utopia-php/emails": "0.6.*", - "utopia-php/dns": "1.5.*", + "utopia-php/dns": "1.6.*", "utopia-php/dsn": "0.2.1", "utopia-php/framework": "0.33.*", "utopia-php/fetch": "0.5.*", @@ -65,9 +65,10 @@ "utopia-php/locale": "0.8.*", "utopia-php/logger": "0.6.*", "utopia-php/messaging": "0.20.*", - "utopia-php/migration": "1.5.*", + "utopia-php/migration": "1.6.*", "utopia-php/platform": "0.7.*", "utopia-php/pools": "1.*", + "utopia-php/span": "1.1.*", "utopia-php/preloader": "0.2.*", "utopia-php/queue": "0.15.*", "utopia-php/registry": "0.5.*", diff --git a/composer.lock b/composer.lock index 58179608ab..ddc2754e98 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": "bc64aa37fc3ab6fa2acf7ac8f5456e9f", + "content-hash": "fe847eccf6ba73bfca1e08d26e9fc7ab", "packages": [ { "name": "adhocore/jwt", @@ -3797,16 +3797,16 @@ }, { "name": "utopia-php/database", - "version": "5.1.1", + "version": "5.2.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "94815bfa605282096272625827d0314f9ed99066" + "reference": "5c89b39de00f2b3126d0fbbdea36786341293df7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/94815bfa605282096272625827d0314f9ed99066", - "reference": "94815bfa605282096272625827d0314f9ed99066", + "url": "https://api.github.com/repos/utopia-php/database/zipball/5c89b39de00f2b3126d0fbbdea36786341293df7", + "reference": "5c89b39de00f2b3126d0fbbdea36786341293df7", "shasum": "" }, "require": { @@ -3849,9 +3849,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/5.1.1" + "source": "https://github.com/utopia-php/database/tree/5.2.0" }, - "time": "2026-02-12T11:44:58+00:00" + "time": "2026-02-14T09:37:28+00:00" }, { "name": "utopia-php/detector", @@ -3948,22 +3948,22 @@ }, { "name": "utopia-php/dns", - "version": "1.5.4", + "version": "1.6.2", "source": { "type": "git", "url": "https://github.com/utopia-php/dns.git", - "reference": "ee831a6f2ceb28babb042ea65539c26ea4530bf6" + "reference": "98c70520213a41e2fe1867e5b110273c06bf1cab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/dns/zipball/ee831a6f2ceb28babb042ea65539c26ea4530bf6", - "reference": "ee831a6f2ceb28babb042ea65539c26ea4530bf6", + "url": "https://api.github.com/repos/utopia-php/dns/zipball/98c70520213a41e2fe1867e5b110273c06bf1cab", + "reference": "98c70520213a41e2fe1867e5b110273c06bf1cab", "shasum": "" }, "require": { "php": ">=8.3", "utopia-php/domains": "1.0.*", - "utopia-php/span": "1.0.*", + "utopia-php/span": "1.1.*", "utopia-php/telemetry": "*", "utopia-php/validators": "0.*" }, @@ -3999,9 +3999,9 @@ ], "support": { "issues": "https://github.com/utopia-php/dns/issues", - "source": "https://github.com/utopia-php/dns/tree/1.5.4" + "source": "https://github.com/utopia-php/dns/tree/1.6.2" }, - "time": "2026-02-02T10:40:38+00:00" + "time": "2026-02-13T12:29:08+00:00" }, { "name": "utopia-php/domains", @@ -4464,16 +4464,16 @@ }, { "name": "utopia-php/migration", - "version": "1.5.2", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "93904948f6dd07491821615fd9b9acbcaadec12e" + "reference": "aa07cf9ae8cc4b8df0ab64588e033693b5ad6849" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/93904948f6dd07491821615fd9b9acbcaadec12e", - "reference": "93904948f6dd07491821615fd9b9acbcaadec12e", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/aa07cf9ae8cc4b8df0ab64588e033693b5ad6849", + "reference": "aa07cf9ae8cc4b8df0ab64588e033693b5ad6849", "shasum": "" }, "require": { @@ -4513,9 +4513,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/1.5.2" + "source": "https://github.com/utopia-php/migration/tree/1.6.0" }, - "time": "2026-02-11T06:19:35+00:00" + "time": "2026-02-16T07:19:27+00:00" }, { "name": "utopia-php/mongo", @@ -4909,25 +4909,26 @@ }, { "name": "utopia-php/span", - "version": "1.0.0", + "version": "1.1.5", "source": { "type": "git", "url": "https://github.com/utopia-php/span.git", - "reference": "f2f6c499ded3a776e8019902e83d140ff0f89693" + "reference": "028406940ca92bdc88099f0b1a123a3b2cbdd4e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/span/zipball/f2f6c499ded3a776e8019902e83d140ff0f89693", - "reference": "f2f6c499ded3a776e8019902e83d140ff0f89693", + "url": "https://api.github.com/repos/utopia-php/span/zipball/028406940ca92bdc88099f0b1a123a3b2cbdd4e5", + "reference": "028406940ca92bdc88099f0b1a123a3b2cbdd4e5", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { "laravel/pint": "^1.0", "phpstan/phpstan": "^2.0", "phpunit/phpunit": "^10.0", + "rector/rector": "^2.3", "swoole/ide-helper": "^5.0" }, "suggest": { @@ -4946,9 +4947,9 @@ "description": "Simple span tracing library for PHP with coroutine support", "support": { "issues": "https://github.com/utopia-php/span/issues", - "source": "https://github.com/utopia-php/span/tree/1.0.0" + "source": "https://github.com/utopia-php/span/tree/1.1.5" }, - "time": "2026-01-12T20:05:10+00:00" + "time": "2026-02-13T18:00:11+00:00" }, { "name": "utopia-php/storage", @@ -5389,16 +5390,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.9.0", + "version": "1.9.2", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "94a2d7ef55ea63c6e8afb166d39a82c07d01c8c0" + "reference": "74de906ea5051030c5299a5d4aa74d963a531130" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/94a2d7ef55ea63c6e8afb166d39a82c07d01c8c0", - "reference": "94a2d7ef55ea63c6e8afb166d39a82c07d01c8c0", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/74de906ea5051030c5299a5d4aa74d963a531130", + "reference": "74de906ea5051030c5299a5d4aa74d963a531130", "shasum": "" }, "require": { @@ -5434,9 +5435,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/1.9.0" + "source": "https://github.com/appwrite/sdk-generator/tree/1.9.2" }, - "time": "2026-02-12T12:08:13+00:00" + "time": "2026-02-16T06:59:54+00:00" }, { "name": "doctrine/annotations", @@ -8889,7 +8890,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { @@ -8913,5 +8914,5 @@ "platform-overrides": { "php": "8.3" }, - "plugin-api-version": "2.9.0" + "plugin-api-version": "2.6.0" } diff --git a/docker-compose.yml b/docker-compose.yml index 5a6367f402..635b1cb2cf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -738,6 +738,7 @@ services: depends_on: - redis - maildev + - ${_APP_DB_HOST:-mariadb} # - smtp environment: - _APP_ENV @@ -750,6 +751,12 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_DB_ADAPTER - _APP_SMTP_HOST - _APP_SMTP_PORT - _APP_SMTP_SECURE diff --git a/docs/sdks/agent-skills/GETTING_STARTED.md b/docs/sdks/agent-skills/GETTING_STARTED.md index 4dcfd9447e..4bbeccc015 100644 --- a/docs/sdks/agent-skills/GETTING_STARTED.md +++ b/docs/sdks/agent-skills/GETTING_STARTED.md @@ -9,7 +9,7 @@ These skills follow the Agent Skills Open Standard: https://agentskills.io/home Install directly with the Skills CLI: ```bash -npx skills add chiragagg5k/appwrite-agent-skills +npx skills add appwrite/agent-skills ``` This installs the packaged `appwrite-*` skills into your local skills directory. diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Longtext/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Longtext/Create.php index 7ec249de28..2fc9de8699 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Longtext/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Longtext/Create.php @@ -4,6 +4,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attribu use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; +use Appwrite\Extend\Exception; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Action; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -15,6 +16,7 @@ use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Http\Adapter\Swoole\Response as SwooleResponse; +use Utopia\Http\Http; use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; use Utopia\Validator\Text; @@ -62,10 +64,12 @@ class Create extends Action ->param('required', null, new Boolean(), 'Is attribute required?') ->param('default', null, new Nullable(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('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('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') + ->inject('plan') ->inject('authorization') ->callback($this->action(...)); } @@ -77,12 +81,23 @@ class Create extends Action ?bool $required, ?string $default, bool $array, + bool $encrypt, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, + array $plan, Authorization $authorization ): void { + if (!Http::isDevelopment() && $encrypt && !empty($plan) && !($plan['databasesAllowEncrypt'] ?? false)) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Encrypted ' . $this->getSDKGroup() . ' are not available on your plan. Please upgrade to create encrypted ' . $this->getSDKGroup() . '.'); + } + + $filters = []; + if ($encrypt) { + $filters[] = 'encrypt'; + } + $attribute = $this->createAttribute( $databaseId, $collectionId, @@ -93,6 +108,7 @@ class Create extends Action 'required' => $required, 'default' => $default, 'array' => $array, + 'filters' => $filters, ]), $response, $dbForProject, @@ -101,6 +117,8 @@ class Create extends Action $authorization ); + $attribute->setAttribute('encrypt', $encrypt); + $response ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) ->dynamic($attribute, $this->getResponseModel()); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Mediumtext/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Mediumtext/Create.php index 918379d2a0..5776e51917 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Mediumtext/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Mediumtext/Create.php @@ -4,6 +4,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attribu use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; +use Appwrite\Extend\Exception; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Action; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -15,6 +16,7 @@ use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Http\Adapter\Swoole\Response as SwooleResponse; +use Utopia\Http\Http; use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; use Utopia\Validator\Text; @@ -62,10 +64,12 @@ class Create extends Action ->param('required', null, new Boolean(), 'Is attribute required?') ->param('default', null, new Nullable(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('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('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') + ->inject('plan') ->inject('authorization') ->callback($this->action(...)); } @@ -77,12 +81,23 @@ class Create extends Action ?bool $required, ?string $default, bool $array, + bool $encrypt, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, + array $plan, Authorization $authorization ): void { + if (!Http::isDevelopment() && $encrypt && !empty($plan) && !($plan['databasesAllowEncrypt'] ?? false)) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Encrypted ' . $this->getSDKGroup() . ' are not available on your plan. Please upgrade to create encrypted ' . $this->getSDKGroup() . '.'); + } + + $filters = []; + if ($encrypt) { + $filters[] = 'encrypt'; + } + $attribute = $this->createAttribute( $databaseId, $collectionId, @@ -93,6 +108,7 @@ class Create extends Action 'required' => $required, 'default' => $default, 'array' => $array, + 'filters' => $filters, ]), $response, $dbForProject, @@ -101,6 +117,8 @@ class Create extends Action $authorization ); + $attribute->setAttribute('encrypt', $encrypt); + $response ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) ->dynamic($attribute, $this->getResponseModel()); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Text/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Text/Create.php index bc9b853d0f..eb6b2f9691 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Text/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Text/Create.php @@ -4,6 +4,7 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attribu use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; +use Appwrite\Extend\Exception; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Action; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -15,6 +16,7 @@ use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Http\Adapter\Swoole\Response as SwooleResponse; +use Utopia\Http\Http; use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; use Utopia\Validator\Text; @@ -62,10 +64,12 @@ class Create extends Action ->param('required', null, new Boolean(), 'Is attribute required?') ->param('default', null, new Nullable(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('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('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') + ->inject('plan') ->inject('authorization') ->callback($this->action(...)); } @@ -77,12 +81,23 @@ class Create extends Action ?bool $required, ?string $default, bool $array, + bool $encrypt, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, + array $plan, Authorization $authorization ): void { + if (!Http::isDevelopment() && $encrypt && !empty($plan) && !($plan['databasesAllowEncrypt'] ?? false)) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Encrypted ' . $this->getSDKGroup() . ' are not available on your plan. Please upgrade to create encrypted ' . $this->getSDKGroup() . '.'); + } + + $filters = []; + if ($encrypt) { + $filters[] = 'encrypt'; + } + $attribute = $this->createAttribute( $databaseId, $collectionId, @@ -93,6 +108,7 @@ class Create extends Action 'required' => $required, 'default' => $default, 'array' => $array, + 'filters' => $filters, ]), $response, $dbForProject, @@ -101,6 +117,8 @@ class Create extends Action $authorization ); + $attribute->setAttribute('encrypt', $encrypt); + $response ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) ->dynamic($attribute, $this->getResponseModel()); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Varchar/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Varchar/Create.php index c35ad518ab..24a36725c8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Varchar/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Varchar/Create.php @@ -16,6 +16,7 @@ use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Http\Adapter\Swoole\Response as SwooleResponse; +use Utopia\Http\Http; use Utopia\Validator; use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; @@ -66,10 +67,12 @@ class Create extends Action ->param('required', null, new Boolean(), 'Is attribute required?') ->param('default', null, new Nullable(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('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('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') + ->inject('plan') ->inject('authorization') ->callback($this->action(...)); } @@ -82,18 +85,36 @@ class Create extends Action ?bool $required, ?string $default, bool $array, + bool $encrypt, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, + array $plan, Authorization $authorization ): void { + if (!Http::isDevelopment() && $encrypt && !empty($plan) && !($plan['databasesAllowEncrypt'] ?? false)) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Encrypted ' . $this->getSDKGroup() . ' are not available on your plan. Please upgrade to create encrypted ' . $this->getSDKGroup() . '.'); + } + + if ($encrypt && $size < APP_DATABASE_ENCRYPT_SIZE_MIN) { + throw new Exception( + Exception::GENERAL_BAD_REQUEST, + "Size too small. Encrypted strings require a minimum size of " . APP_DATABASE_ENCRYPT_SIZE_MIN . " characters." + ); + } + // Ensure default fits in the given size $validator = new Text($size, 0); if (!is_null($default) && !$validator->isValid($default)) { throw new Exception($this->getInvalidValueException(), $validator->getDescription()); } + $filters = []; + if ($encrypt) { + $filters[] = 'encrypt'; + } + $attribute = $this->createAttribute( $databaseId, $collectionId, @@ -104,6 +125,7 @@ class Create extends Action 'required' => $required, 'default' => $default, 'array' => $array, + 'filters' => $filters, ]), $response, $dbForProject, @@ -112,6 +134,8 @@ class Create extends Action $authorization ); + $attribute->setAttribute('encrypt', $encrypt); + $response ->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED) ->dynamic($attribute, $this->getResponseModel()); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Longtext/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Longtext/Create.php index fa06fb3ab3..da9471f37c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Longtext/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Longtext/Create.php @@ -57,10 +57,12 @@ class Create extends LongtextCreate ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Text(0, 0)), 'Default value for column when not provided. Cannot be set when column is required.', true) ->param('array', false, new Boolean(), 'Is column an array?', true) + ->param('encrypt', false, new Boolean(), 'Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') + ->inject('plan') ->inject('authorization') ->callback($this->action(...)); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Mediumtext/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Mediumtext/Create.php index 6b5eba648e..585856cab9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Mediumtext/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Mediumtext/Create.php @@ -57,10 +57,12 @@ class Create extends MediumtextCreate ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Text(0, 0)), 'Default value for column when not provided. Cannot be set when column is required.', true) ->param('array', false, new Boolean(), 'Is column an array?', true) + ->param('encrypt', false, new Boolean(), 'Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') + ->inject('plan') ->inject('authorization') ->callback($this->action(...)); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Text/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Text/Create.php index 4024537c2d..2c68431d8c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Text/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Text/Create.php @@ -57,10 +57,12 @@ class Create extends TextCreate ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Text(0, 0)), 'Default value for column when not provided. Cannot be set when column is required.', true) ->param('array', false, new Boolean(), 'Is column an array?', true) + ->param('encrypt', false, new Boolean(), 'Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') + ->inject('plan') ->inject('authorization') ->callback($this->action(...)); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Varchar/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Varchar/Create.php index fada172d2a..0ee04f5f63 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Varchar/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Varchar/Create.php @@ -60,10 +60,12 @@ class Create extends VarcharCreate ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Text(0, 0)), 'Default value for column when not provided. Cannot be set when column is required.', true) ->param('array', false, new Boolean(), 'Is column an array?', true) + ->param('encrypt', false, new Boolean(), 'Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') + ->inject('plan') ->inject('authorization') ->callback($this->action(...)); } diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php index 0c497df6ea..4f1c7822ec 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php @@ -301,7 +301,7 @@ class Create extends Base if ($async) { if (is_null($scheduledAt)) { - if (System::getEnv('_APP_REGION') !== 'nyc') { // TODO: Remove region check + if ($project->getId() != '6862e6a6000cce69f9da') { $execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution)); } $queueForFunctions @@ -344,7 +344,7 @@ class Create extends Base ->setAttribute('scheduleInternalId', $schedule->getSequence()) ->setAttribute('scheduledAt', $scheduledAt); - if (System::getEnv('_APP_REGION') !== 'nyc') { // TODO: Remove region check + if ($project->getId() != '6862e6a6000cce69f9da') { $execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution)); } } @@ -505,7 +505,7 @@ class Create extends Base ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT))) ; - if (System::getEnv('_APP_REGION') !== 'nyc') { // TODO: Remove region check + if ($project->getId() != '6862e6a6000cce69f9da') { $execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution)); } } diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index aaa5baf3cf..0245084b5e 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -932,16 +932,6 @@ class Builds extends Action $this->runGitAction('ready', $github, $providerCommitHash, $owner, $repositoryName, $project, $resource, $deployment->getId(), $dbForProject, $dbForPlatform, $queueForRealtime, $platform); } - /** Screenshot site */ - if ($resource->getCollection() === 'sites') { - $queueForScreenshots - ->setDeploymentId($deployment->getId()) - ->setProject($project) - ->trigger(); - - Console::log('Site screenshot queued'); - } - /** Set auto deploy */ $activateBuild = false; if ($deployment->getAttribute('activate') === true) { @@ -1023,6 +1013,16 @@ class Builds extends Action Console::log('Deployment activated'); } + /** Screenshot site */ + if ($resource->getCollection() === 'sites') { + $queueForScreenshots + ->setDeploymentId($deployment->getId()) + ->setProject($project) + ->trigger(); + + Console::log('Site screenshot queued'); + } + $this->afterDeploymentSuccess( $project, $deployment, diff --git a/src/Appwrite/Platform/Workers/Executions.php b/src/Appwrite/Platform/Workers/Executions.php index 300a84162c..d874e26267 100644 --- a/src/Appwrite/Platform/Workers/Executions.php +++ b/src/Appwrite/Platform/Workers/Executions.php @@ -7,7 +7,6 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Platform\Action; use Utopia\Queue\Message; -use Utopia\System\System; class Executions extends Action { @@ -45,7 +44,8 @@ class Executions extends Action throw new Exception('Missing execution'); } - if (System::getEnv('_APP_REGION') !== 'nyc') { // TODO: Remove region check + $project = new Document($payload['project'] ?? []); + if ($project->getId() != '6862e6a6000cce69f9da') { $dbForProject->upsertDocument('executions', $execution); } } diff --git a/src/Appwrite/Platform/Workers/Mails.php b/src/Appwrite/Platform/Workers/Mails.php index b1f17fc648..72f7cddd06 100644 --- a/src/Appwrite/Platform/Workers/Mails.php +++ b/src/Appwrite/Platform/Workers/Mails.php @@ -6,6 +6,7 @@ use Appwrite\Template\Template; use Exception; use PHPMailer\PHPMailer\PHPMailer; use Swoole\Runtime; +use Utopia\Database\Document; use Utopia\Logger\Log; use Utopia\Platform\Action; use Utopia\Queue\Message; @@ -32,6 +33,7 @@ class Mails extends Action $this ->desc('Mails worker') ->inject('message') + ->inject('project') ->inject('register') ->inject('log') ->callback($this->action(...)); @@ -53,7 +55,7 @@ class Mails extends Action * @return void * @throws Exception */ - public function action(Message $message, Registry $register, Log $log): void + public function action(Message $message, Document $project, Registry $register, Log $log): void { Runtime::setHookFlags(SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_TCP); $payload = $message->getPayload() ?? []; diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index c6b005c334..94cbcf341c 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -735,6 +735,7 @@ class Migrations extends Action ]; $queueForMails + ->setProject($project) ->setSubject($subject) ->setPreview($preview) ->setBody($emailBody) diff --git a/src/Appwrite/Platform/Workers/Webhooks.php b/src/Appwrite/Platform/Workers/Webhooks.php index dbfbe591a6..56839058de 100644 --- a/src/Appwrite/Platform/Workers/Webhooks.php +++ b/src/Appwrite/Platform/Workers/Webhooks.php @@ -253,6 +253,7 @@ class Webhooks extends Action ->setParam('{{year}}', date("Y")); $queueForMails + ->setProject($project) ->setSubject($subject) ->setPreview($preview) ->setBody($body->render()); diff --git a/src/Appwrite/PubSub/Adapter/Redis.php b/src/Appwrite/PubSub/Adapter/Redis.php index 187eb9cd95..f5218df93c 100644 --- a/src/Appwrite/PubSub/Adapter/Redis.php +++ b/src/Appwrite/PubSub/Adapter/Redis.php @@ -16,7 +16,7 @@ class Redis implements Adapter public function ping($message = null): bool { - return $this->client->ping($message); + return (bool) $this->client->ping($message); } public function subscribe($channels, $callback) diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Migrations.php b/src/Appwrite/Utopia/Database/Validator/Queries/Migrations.php index 436a95534b..c49788872e 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Migrations.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Migrations.php @@ -10,6 +10,8 @@ class Migrations extends Base 'source', 'destination', 'resources', + 'resourceId', + 'resourceType', 'statusCounters', 'resourceData', 'errors' diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index fd518e4a9a..3fc043725d 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -456,6 +456,8 @@ class Response extends SwooleResponse foreach ($data[$key] as $index => $item) { if ($item instanceof Document) { + $ruleType = null; + if (\is_array($rule['type'])) { foreach ($rule['type'] as $type) { $condition = false; @@ -474,7 +476,7 @@ class Response extends SwooleResponse $ruleType = $rule['type']; } - if (!self::hasModel($ruleType)) { + if ($ruleType === null || !self::hasModel($ruleType)) { throw new Exception('Missing model for rule: ' . $ruleType); } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeList.php b/src/Appwrite/Utopia/Response/Model/AttributeList.php index 6b9a7365bd..0b17a655ab 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeList.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeList.php @@ -30,6 +30,10 @@ class AttributeList extends Model Response::MODEL_ATTRIBUTE_POINT, Response::MODEL_ATTRIBUTE_LINE, Response::MODEL_ATTRIBUTE_POLYGON, + Response::MODEL_ATTRIBUTE_VARCHAR, + Response::MODEL_ATTRIBUTE_TEXT, + Response::MODEL_ATTRIBUTE_MEDIUMTEXT, + Response::MODEL_ATTRIBUTE_LONGTEXT, Response::MODEL_ATTRIBUTE_STRING // needs to be last, since its condition would dominate any other string attribute ], 'description' => 'List of attributes.', diff --git a/src/Appwrite/Utopia/Response/Model/AttributeLongtext.php b/src/Appwrite/Utopia/Response/Model/AttributeLongtext.php index 02e2f637e4..f0ab1b267c 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeLongtext.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeLongtext.php @@ -18,6 +18,13 @@ class AttributeLongtext 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/src/Appwrite/Utopia/Response/Model/AttributeMediumtext.php b/src/Appwrite/Utopia/Response/Model/AttributeMediumtext.php index de9316ff03..4f8b9a8392 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeMediumtext.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeMediumtext.php @@ -18,6 +18,13 @@ class AttributeMediumtext 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/src/Appwrite/Utopia/Response/Model/AttributeText.php b/src/Appwrite/Utopia/Response/Model/AttributeText.php index 64a8c8316b..4424db88b6 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeText.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeText.php @@ -18,6 +18,13 @@ class AttributeText 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/src/Appwrite/Utopia/Response/Model/AttributeVarchar.php b/src/Appwrite/Utopia/Response/Model/AttributeVarchar.php index 21741b7ca3..aa464a013e 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeVarchar.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeVarchar.php @@ -24,6 +24,13 @@ class AttributeVarchar 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/src/Appwrite/Utopia/Response/Model/Collection.php b/src/Appwrite/Utopia/Response/Model/Collection.php index 407db3aea9..4ab7de8e4d 100644 --- a/src/Appwrite/Utopia/Response/Model/Collection.php +++ b/src/Appwrite/Utopia/Response/Model/Collection.php @@ -73,6 +73,10 @@ class Collection extends Model Response::MODEL_ATTRIBUTE_POINT, Response::MODEL_ATTRIBUTE_LINE, Response::MODEL_ATTRIBUTE_POLYGON, + Response::MODEL_ATTRIBUTE_VARCHAR, + Response::MODEL_ATTRIBUTE_TEXT, + Response::MODEL_ATTRIBUTE_MEDIUMTEXT, + Response::MODEL_ATTRIBUTE_LONGTEXT, Response::MODEL_ATTRIBUTE_STRING, // needs to be last, since its condition would dominate any other string attribute ], 'description' => 'Collection attributes.', diff --git a/src/Appwrite/Utopia/Response/Model/ColumnLongtext.php b/src/Appwrite/Utopia/Response/Model/ColumnLongtext.php index 86361596fe..9ce4599eee 100644 --- a/src/Appwrite/Utopia/Response/Model/ColumnLongtext.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnLongtext.php @@ -18,6 +18,13 @@ class ColumnLongtext extends Column 'required' => false, 'example' => 'default', ]) + ->addRule('encrypt', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Defines whether this column is encrypted or not.', + 'default' => false, + 'required' => false, + 'example' => false, + ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/ColumnMediumtext.php b/src/Appwrite/Utopia/Response/Model/ColumnMediumtext.php index c060dcbc60..a97c642fe9 100644 --- a/src/Appwrite/Utopia/Response/Model/ColumnMediumtext.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnMediumtext.php @@ -18,6 +18,13 @@ class ColumnMediumtext extends Column 'required' => false, 'example' => 'default', ]) + ->addRule('encrypt', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Defines whether this column is encrypted or not.', + 'default' => false, + 'required' => false, + 'example' => false, + ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/ColumnText.php b/src/Appwrite/Utopia/Response/Model/ColumnText.php index acd997d18c..e0db0c47cc 100644 --- a/src/Appwrite/Utopia/Response/Model/ColumnText.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnText.php @@ -18,6 +18,13 @@ class ColumnText extends Column 'required' => false, 'example' => 'default', ]) + ->addRule('encrypt', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Defines whether this column is encrypted or not.', + 'default' => false, + 'required' => false, + 'example' => false, + ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/ColumnVarchar.php b/src/Appwrite/Utopia/Response/Model/ColumnVarchar.php index eba0fbd973..5c57e8be13 100644 --- a/src/Appwrite/Utopia/Response/Model/ColumnVarchar.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnVarchar.php @@ -24,6 +24,13 @@ class ColumnVarchar extends Column 'required' => false, 'example' => 'default', ]) + ->addRule('encrypt', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Defines whether this column is encrypted or not.', + 'default' => false, + 'required' => false, + 'example' => false, + ]) ; } diff --git a/tests/e2e/General/HTTPTest.php b/tests/e2e/General/HTTPTest.php index 4012745682..261bab9348 100644 --- a/tests/e2e/General/HTTPTest.php +++ b/tests/e2e/General/HTTPTest.php @@ -6,6 +6,7 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectNone; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideNone; +use Utopia\Config\Config; class HTTPTest extends Scope { @@ -28,11 +29,16 @@ class HTTPTest extends Scope 'content-type' => 'application/json', ]), []); + $corsConfig = Config::getParam('cors'); + $allowedMethods = \implode(', ', $corsConfig['allowedMethods']); + $allowedHeaders = \implode(', ', $corsConfig['allowedHeaders']); + $exposedHeaders = \implode(', ', $corsConfig['exposedHeaders']); + $this->assertEquals(204, $response['headers']['status-code']); $this->assertEquals('Appwrite', $response['headers']['server']); - $this->assertEquals('GET, POST, PUT, PATCH, DELETE', $response['headers']['access-control-allow-methods']); - $this->assertEquals('Accept, Origin, Cookie, Set-Cookie, Content-Type, Content-Range, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Dev-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-Appwrite-ID, X-Appwrite-Timestamp, X-Appwrite-Session, X-Appwrite-Platform, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-SDK-Profile, Range, Cache-Control, Expires, Pragma, X-Fallback-Cookies, X-Requested-With, X-Forwarded-For, X-Forwarded-User-Agent', $response['headers']['access-control-allow-headers']); - $this->assertEquals('X-Appwrite-Session, X-Fallback-Cookies', $response['headers']['access-control-expose-headers']); + $this->assertEquals($allowedMethods, $response['headers']['access-control-allow-methods']); + $this->assertEquals($allowedHeaders, $response['headers']['access-control-allow-headers']); + $this->assertEquals($exposedHeaders, $response['headers']['access-control-expose-headers']); $this->assertEquals('http://localhost', $response['headers']['access-control-allow-origin']); $this->assertEquals('true', $response['headers']['access-control-allow-credentials']); $this->assertEmpty($response['body']); diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesStringTypesTest.php b/tests/e2e/Services/Databases/Legacy/DatabasesStringTypesTest.php index 3a9c7927db..d1ea6ae921 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesStringTypesTest.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesStringTypesTest.php @@ -88,6 +88,7 @@ class DatabasesStringTypesTest extends Scope $this->assertEquals(255, $varchar['body']['size']); $this->assertEquals(false, $varchar['body']['required']); $this->assertNull($varchar['body']['default']); + $this->assertFalse($varchar['body']['encrypt']); // Test SUCCESS: Create varchar with default value $varcharWithDefault = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/varchar', [ @@ -147,6 +148,21 @@ class DatabasesStringTypesTest extends Scope $this->assertEquals(202, $varcharMin['headers']['status-code']); $this->assertEquals(1, $varcharMin['body']['size']); + // Test SUCCESS: Create encrypted varchar attribute + $varcharEncrypted = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/varchar', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'varchar_encrypted', + 'size' => 256, + 'required' => false, + 'encrypt' => true, + ]); + + $this->assertEquals(202, $varcharEncrypted['headers']['status-code']); + $this->assertTrue($varcharEncrypted['body']['encrypt']); + return $data; } @@ -235,6 +251,20 @@ class DatabasesStringTypesTest extends Scope ]); $this->assertEquals(409, $varcharDuplicate['headers']['status-code']); + + // Test FAILURE: Encrypted varchar with size too small + $varcharEncryptTooSmall = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/varchar', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'varchar_encrypt_small', + 'size' => 149, + 'required' => false, + 'encrypt' => true, + ]); + + $this->assertEquals(400, $varcharEncryptTooSmall['headers']['status-code']); } /** @@ -259,6 +289,7 @@ class DatabasesStringTypesTest extends Scope $this->assertEquals('text_field', $text['body']['key']); $this->assertEquals('text', $text['body']['type']); $this->assertEquals(false, $text['body']['required']); + $this->assertFalse($text['body']['encrypt']); // Test SUCCESS: Create text with default value $textWithDefault = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/text', [ @@ -301,6 +332,20 @@ class DatabasesStringTypesTest extends Scope $this->assertEquals(202, $textArray['headers']['status-code']); $this->assertEquals(true, $textArray['body']['array']); + // Test SUCCESS: Create encrypted text attribute + $textEncrypted = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/text', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'text_encrypted', + 'required' => false, + 'encrypt' => true, + ]); + + $this->assertEquals(202, $textEncrypted['headers']['status-code']); + $this->assertTrue($textEncrypted['body']['encrypt']); + return $data; } @@ -326,6 +371,7 @@ class DatabasesStringTypesTest extends Scope $this->assertEquals('mediumtext_field', $mediumtext['body']['key']); $this->assertEquals('mediumtext', $mediumtext['body']['type']); $this->assertEquals(false, $mediumtext['body']['required']); + $this->assertFalse($mediumtext['body']['encrypt']); // Test SUCCESS: Create mediumtext with default $mediumtextWithDefault = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/mediumtext', [ @@ -367,6 +413,20 @@ class DatabasesStringTypesTest extends Scope $this->assertEquals(202, $mediumtextArray['headers']['status-code']); $this->assertEquals(true, $mediumtextArray['body']['array']); + // Test SUCCESS: Create encrypted mediumtext attribute + $mediumtextEncrypted = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/mediumtext', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'mediumtext_encrypted', + 'required' => false, + 'encrypt' => true, + ]); + + $this->assertEquals(202, $mediumtextEncrypted['headers']['status-code']); + $this->assertTrue($mediumtextEncrypted['body']['encrypt']); + return $data; } @@ -392,6 +452,7 @@ class DatabasesStringTypesTest extends Scope $this->assertEquals('longtext_field', $longtext['body']['key']); $this->assertEquals('longtext', $longtext['body']['type']); $this->assertEquals(false, $longtext['body']['required']); + $this->assertFalse($longtext['body']['encrypt']); // Test SUCCESS: Create longtext with default $longtextWithDefault = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/longtext', [ @@ -433,12 +494,83 @@ class DatabasesStringTypesTest extends Scope $this->assertEquals(202, $longtextArray['headers']['status-code']); $this->assertEquals(true, $longtextArray['body']['array']); + // Test SUCCESS: Create encrypted longtext attribute + $longtextEncrypted = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/longtext', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'longtext_encrypted', + 'required' => false, + 'encrypt' => true, + ]); + + $this->assertEquals(202, $longtextEncrypted['headers']['status-code']); + $this->assertTrue($longtextEncrypted['body']['encrypt']); + return $data; } /** * @depends testCreateLongtextAttribute */ + public function testListStringTypeAttributes(array $data): array + { + $databaseId = $data['databaseId']; + $collectionId = $data['collectionId']; + + // Wait for attributes to be created + sleep(2); + + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + $attributes = $response['body']['attributes']; + $types = array_column($attributes, 'type'); + + $this->assertContains('varchar', $types); + $this->assertContains('text', $types); + $this->assertContains('mediumtext', $types); + $this->assertContains('longtext', $types); + + return $data; + } + + /** + * @depends testListStringTypeAttributes + */ + public function testGetCollectionWithStringTypeAttributes(array $data): array + { + $databaseId = $data['databaseId']; + $collectionId = $data['collectionId']; + + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + $attributes = $response['body']['attributes']; + $types = array_column($attributes, 'type'); + + $this->assertContains('varchar', $types); + $this->assertContains('text', $types); + $this->assertContains('mediumtext', $types); + $this->assertContains('longtext', $types); + + return $data; + } + + /** + * @depends testGetCollectionWithStringTypeAttributes + */ public function testUpdateVarcharAttribute(array $data): array { $this->markTestSkipped('Skipped until utopia-php/database updateAttribute supports VARCHAR type'); diff --git a/tests/e2e/Services/Databases/TablesDB/DatabasesStringTypesTest.php b/tests/e2e/Services/Databases/TablesDB/DatabasesStringTypesTest.php index d38e03ec89..e87e57bf16 100644 --- a/tests/e2e/Services/Databases/TablesDB/DatabasesStringTypesTest.php +++ b/tests/e2e/Services/Databases/TablesDB/DatabasesStringTypesTest.php @@ -88,6 +88,7 @@ class DatabasesStringTypesTest extends Scope $this->assertEquals(255, $varchar['body']['size']); $this->assertEquals(false, $varchar['body']['required']); $this->assertNull($varchar['body']['default']); + $this->assertFalse($varchar['body']['encrypt']); // Test SUCCESS: Create varchar with default value $varcharWithDefault = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/varchar', [ @@ -147,6 +148,21 @@ class DatabasesStringTypesTest extends Scope $this->assertEquals(202, $varcharMin['headers']['status-code']); $this->assertEquals(1, $varcharMin['body']['size']); + // Test SUCCESS: Create encrypted varchar column + $varcharEncrypted = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/varchar', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'varchar_encrypted', + 'size' => 256, + 'required' => false, + 'encrypt' => true, + ]); + + $this->assertEquals(202, $varcharEncrypted['headers']['status-code']); + $this->assertTrue($varcharEncrypted['body']['encrypt']); + return $data; } @@ -235,6 +251,20 @@ class DatabasesStringTypesTest extends Scope ]); $this->assertEquals(409, $varcharDuplicate['headers']['status-code']); + + // Test FAILURE: Encrypted varchar with size too small + $varcharEncryptTooSmall = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/varchar', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'varchar_encrypt_small', + 'size' => 149, + 'required' => false, + 'encrypt' => true, + ]); + + $this->assertEquals(400, $varcharEncryptTooSmall['headers']['status-code']); } /** @@ -259,6 +289,7 @@ class DatabasesStringTypesTest extends Scope $this->assertEquals('text_field', $text['body']['key']); $this->assertEquals('text', $text['body']['type']); $this->assertEquals(false, $text['body']['required']); + $this->assertFalse($text['body']['encrypt']); // Test SUCCESS: Create text with default value $textWithDefault = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/text', [ @@ -301,6 +332,20 @@ class DatabasesStringTypesTest extends Scope $this->assertEquals(202, $textArray['headers']['status-code']); $this->assertEquals(true, $textArray['body']['array']); + // Test SUCCESS: Create encrypted text column + $textEncrypted = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/text', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'text_encrypted', + 'required' => false, + 'encrypt' => true, + ]); + + $this->assertEquals(202, $textEncrypted['headers']['status-code']); + $this->assertTrue($textEncrypted['body']['encrypt']); + return $data; } @@ -326,6 +371,7 @@ class DatabasesStringTypesTest extends Scope $this->assertEquals('mediumtext_field', $mediumtext['body']['key']); $this->assertEquals('mediumtext', $mediumtext['body']['type']); $this->assertEquals(false, $mediumtext['body']['required']); + $this->assertFalse($mediumtext['body']['encrypt']); // Test SUCCESS: Create mediumtext with default $mediumtextWithDefault = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/mediumtext', [ @@ -367,6 +413,20 @@ class DatabasesStringTypesTest extends Scope $this->assertEquals(202, $mediumtextArray['headers']['status-code']); $this->assertEquals(true, $mediumtextArray['body']['array']); + // Test SUCCESS: Create encrypted mediumtext column + $mediumtextEncrypted = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/mediumtext', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'mediumtext_encrypted', + 'required' => false, + 'encrypt' => true, + ]); + + $this->assertEquals(202, $mediumtextEncrypted['headers']['status-code']); + $this->assertTrue($mediumtextEncrypted['body']['encrypt']); + return $data; } @@ -392,6 +452,7 @@ class DatabasesStringTypesTest extends Scope $this->assertEquals('longtext_field', $longtext['body']['key']); $this->assertEquals('longtext', $longtext['body']['type']); $this->assertEquals(false, $longtext['body']['required']); + $this->assertFalse($longtext['body']['encrypt']); // Test SUCCESS: Create longtext with default $longtextWithDefault = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/longtext', [ @@ -433,6 +494,20 @@ class DatabasesStringTypesTest extends Scope $this->assertEquals(202, $longtextArray['headers']['status-code']); $this->assertEquals(true, $longtextArray['body']['array']); + // Test SUCCESS: Create encrypted longtext column + $longtextEncrypted = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/longtext', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'longtext_encrypted', + 'required' => false, + 'encrypt' => true, + ]); + + $this->assertEquals(202, $longtextEncrypted['headers']['status-code']); + $this->assertTrue($longtextEncrypted['body']['encrypt']); + return $data; } diff --git a/tests/e2e/Services/Migrations/MigrationsBase.php b/tests/e2e/Services/Migrations/MigrationsBase.php index aad0b73aa9..a919974b29 100644 --- a/tests/e2e/Services/Migrations/MigrationsBase.php +++ b/tests/e2e/Services/Migrations/MigrationsBase.php @@ -1253,6 +1253,51 @@ trait MigrationsBase $this->assertEquals(202, $email['headers']['status-code']); + $text = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/text', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'regulartext', + 'required' => false, + ]); + + $this->assertEquals(202, $text['headers']['status-code']); + + $varchar = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/varchar', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'varchar', + 'size' => 1000, + 'required' => false, + ]); + + $this->assertEquals(202, $varchar['headers']['status-code']); + + $mediumtext = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/mediumtext', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'mediumtext', + 'required' => false, + ]); + + $this->assertEquals(202, $mediumtext['headers']['status-code']); + + $longtext = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/longtext', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'key' => 'longtext', + 'required' => false, + ]); + + $this->assertEquals(202, $longtext['headers']['status-code']); + \sleep(3); // Create sample documents @@ -1265,7 +1310,11 @@ trait MigrationsBase 'documentId' => ID::unique(), 'data' => [ 'name' => 'Test User ' . $i, - 'email' => 'user' . $i . '@appwrite.io' + 'email' => 'user' . $i . '@appwrite.io', + 'regulartext' => 'regularText', + 'mediumtext' => 'mediumText', + 'longtext' => 'longText', + 'varchar' => 'varchar', ] ]); @@ -1344,11 +1393,17 @@ trait MigrationsBase // Verify the downloaded content is valid CSV $csvData = $downloadWithJwt['body']; + $this->assertNotEmpty($csvData, 'CSV export should not be empty'); $this->assertStringContainsString('name', $csvData, 'CSV should contain the name column header'); $this->assertStringContainsString('email', $csvData, 'CSV should contain the email column header'); $this->assertStringContainsString('Test User 1', $csvData, 'CSV should contain test data'); + $this->assertStringContainsString('regularText', $csvData, 'CSV should contain the text column header'); + $this->assertStringContainsString('mediumText', $csvData, 'CSV should contain the medium column header'); + $this->assertStringContainsString('longText', $csvData, 'CSV should contain the long text column header'); + $this->assertStringContainsString('varchar', $csvData, 'CSV should contain the varchar column header'); + // Cleanup $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, [ 'x-appwrite-project' => $this->getProject()['$id'], diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index fb841adb29..31fd935ab4 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -5376,7 +5376,7 @@ class ProjectsConsoleClientTest extends Scope ]); $this->assertEquals(400, $response['headers']['status-code']); - /** Test oauth2 with devKey and now get oauth2 is disabled */ + /** Test oauth2 with devKey and now flow works with untrusted URL too */ $response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, @@ -5384,8 +5384,37 @@ class ProjectsConsoleClientTest extends Scope ], [ 'success' => 'https://example.com', 'failure' => 'https://example.com' - ]); - $this->assertEquals(200, $response['headers']['status-code']); + ], followRedirects: false); + + $this->assertEquals(301, $response['headers']['status-code']); + $this->assertArrayHasKey('location', $response['headers']); + + $location = $response['headers']['location']; + + + $locationClient = new Client(); + $locationClient->setEndpoint(''); + $locationClient->addHeader('x-appwrite-dev-key', $devKey['secret']); + + $response = $locationClient->call(Client::METHOD_GET, $location, followRedirects: false); + + $this->assertEquals(301, $response['headers']['status-code']); + $this->assertArrayHasKey('location', $response['headers']); + + $location = $response['headers']['location']; + $this->assertStringStartsWith('http://appwrite:/v1/account/sessions/oauth2/callback/mock/', $response['headers']['location']); + + $response = $locationClient->call(Client::METHOD_GET, $location, followRedirects: false); + $this->assertEquals(301, $response['headers']['status-code']); + $this->assertArrayHasKey('location', $response['headers']); + + $location = $response['headers']['location']; + $this->assertStringStartsWith('http://appwrite:/v1/account/sessions/oauth2/mock/redirect', $response['headers']['location']); + + $response = $locationClient->call(Client::METHOD_GET, $location, followRedirects: false); + + $this->assertEquals(301, $response['headers']['status-code']); + $this->assertSame('https://example.com/#', $response['headers']['location']); /** Ensure any hostname is allowed */ $response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, [ diff --git a/tests/resources/docker/docker-compose.yml b/tests/resources/docker/docker-compose.yml index 8530df0db6..02593f8123 100644 --- a/tests/resources/docker/docker-compose.yml +++ b/tests/resources/docker/docker-compose.yml @@ -279,6 +279,12 @@ services: - _APP_ENV - _APP_REDIS_HOST - _APP_REDIS_PORT + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_DB_ADAPTER - _APP_SMTP_HOST - _APP_SMTP_PORT