diff --git a/.env b/.env index 76af83a946..4d7c038a6b 100644 --- a/.env +++ b/.env @@ -21,6 +21,7 @@ _APP_OPTIONS_ROUTER_PROTECTION=disabled _APP_OPTIONS_FORCE_HTTPS=disabled _APP_OPTIONS_ROUTER_FORCE_HTTPS=disabled _APP_OPENSSL_KEY_V1=your-secret-key +_APP_DNS=8.8.8.8 _APP_DOMAIN=traefik _APP_CONSOLE_DOMAIN=localhost _APP_DOMAIN_FUNCTIONS=functions.localhost @@ -28,6 +29,7 @@ _APP_DOMAIN_SITES=sites.localhost _APP_DOMAIN_TARGET_CNAME=test.localhost _APP_DOMAIN_TARGET_A=127.0.0.1 _APP_DOMAIN_TARGET_AAAA=::1 +_APP_DOMAIN_TARGET_CAA=digicert.com _APP_RULES_FORMAT=md5 _APP_REDIS_HOST=redis _APP_REDIS_PORT=6379 diff --git a/Dockerfile b/Dockerfile index 30b017b573..845c5f6c1d 100755 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \ --no-plugins --no-scripts --prefer-dist \ `if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi` -FROM appwrite/base:0.10.1 AS final +FROM appwrite/base:0.10.3 AS final LABEL maintainer="team@appwrite.io" @@ -50,13 +50,13 @@ RUN mkdir -p /storage/uploads && \ mkdir -p /storage/certificates && \ mkdir -p /storage/functions && \ mkdir -p /storage/debug && \ - chown -Rf www-data.www-data /storage/uploads && chmod -Rf 0755 /storage/uploads && \ - chown -Rf www-data.www-data /storage/imports && chmod -Rf 0755 /storage/imports && \ - chown -Rf www-data.www-data /storage/cache && chmod -Rf 0755 /storage/cache && \ - chown -Rf www-data.www-data /storage/config && chmod -Rf 0755 /storage/config && \ - chown -Rf www-data.www-data /storage/certificates && chmod -Rf 0755 /storage/certificates && \ - chown -Rf www-data.www-data /storage/functions && chmod -Rf 0755 /storage/functions && \ - chown -Rf www-data.www-data /storage/debug && chmod -Rf 0755 /storage/debug + chown -Rf www-data:www-data /storage/uploads && chmod -Rf 0755 /storage/uploads && \ + chown -Rf www-data:www-data /storage/imports && chmod -Rf 0755 /storage/imports && \ + chown -Rf www-data:www-data /storage/cache && chmod -Rf 0755 /storage/cache && \ + chown -Rf www-data:www-data /storage/config && chmod -Rf 0755 /storage/config && \ + chown -Rf www-data:www-data /storage/certificates && chmod -Rf 0755 /storage/certificates && \ + chown -Rf www-data:www-data /storage/functions && chmod -Rf 0755 /storage/functions && \ + chown -Rf www-data:www-data /storage/debug && chmod -Rf 0755 /storage/debug # Executables RUN chmod +x /usr/local/bin/doctor && \ diff --git a/app/config/platforms.php b/app/config/platforms.php index 280ffc30a6..1b22522a24 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -116,7 +116,7 @@ return [ [ 'key' => 'android', 'name' => 'Android', - 'version' => '8.2.0', + 'version' => '8.2.2', 'url' => 'https://github.com/appwrite/sdk-for-android', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android', 'enabled' => true, @@ -413,7 +413,7 @@ return [ [ 'key' => 'kotlin', 'name' => 'Kotlin', - 'version' => '9.1.0', + 'version' => '9.1.2', 'url' => 'https://github.com/appwrite/sdk-for-kotlin', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin', 'enabled' => true, diff --git a/app/config/specs/open-api3-1.7.x-client.json b/app/config/specs/open-api3-1.7.x-client.json index 6813c71f74..dc16932fd4 100644 --- a/app/config/specs/open-api3-1.7.x-client.json +++ b/app/config/specs/open-api3-1.7.x-client.json @@ -8479,17 +8479,20 @@ "type": "integer", "description": "Document automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$collectionId": { "type": "string", "description": "Collection ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/app/config/specs/open-api3-1.7.x-console.json b/app/config/specs/open-api3-1.7.x-console.json index 358b282600..0063b8b557 100644 --- a/app/config/specs/open-api3-1.7.x-console.json +++ b/app/config/specs/open-api3-1.7.x-console.json @@ -37978,17 +37978,20 @@ "type": "integer", "description": "Document automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$collectionId": { "type": "string", "description": "Collection ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", @@ -44635,6 +44638,11 @@ "description": "AAAA target for your Appwrite custom domains.", "x-example": "::1" }, + "_APP_DOMAIN_TARGET_CAA": { + "type": "string", + "description": "CAA target for your Appwrite custom domains.", + "x-example": "digicert.com" + }, "_APP_STORAGE_LIMIT": { "type": "integer", "description": "Maximum file size allowed for file upload in bytes.", @@ -44692,6 +44700,7 @@ "_APP_DOMAIN_TARGET_CNAME", "_APP_DOMAIN_TARGET_A", "_APP_DOMAIN_TARGET_AAAA", + "_APP_DOMAIN_TARGET_CAA", "_APP_STORAGE_LIMIT", "_APP_COMPUTE_SIZE_LIMIT", "_APP_USAGE_STATS", @@ -44707,6 +44716,7 @@ "_APP_DOMAIN_TARGET_CNAME": "appwrite.io", "_APP_DOMAIN_TARGET_A": "127.0.0.1", "_APP_DOMAIN_TARGET_AAAA": "::1", + "_APP_DOMAIN_TARGET_CAA": "digicert.com", "_APP_STORAGE_LIMIT": "30000000", "_APP_COMPUTE_SIZE_LIMIT": "30000000", "_APP_USAGE_STATS": "enabled", diff --git a/app/config/specs/open-api3-1.7.x-server.json b/app/config/specs/open-api3-1.7.x-server.json index 35a1066924..b3917036f0 100644 --- a/app/config/specs/open-api3-1.7.x-server.json +++ b/app/config/specs/open-api3-1.7.x-server.json @@ -27864,17 +27864,20 @@ "type": "integer", "description": "Document automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$collectionId": { "type": "string", "description": "Collection ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index 69cdf83180..ceb6d6ecfd 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -8969,17 +8969,20 @@ "type": "integer", "description": "Document automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$collectionId": { "type": "string", "description": "Collection ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 639817ac35..d0f11523ab 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -33744,17 +33744,20 @@ "type": "integer", "description": "Document automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$collectionId": { "type": "string", "description": "Collection ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/app/config/specs/swagger2-1.7.x-client.json b/app/config/specs/swagger2-1.7.x-client.json index 07a12f0dbb..9941e3db2c 100644 --- a/app/config/specs/swagger2-1.7.x-client.json +++ b/app/config/specs/swagger2-1.7.x-client.json @@ -8516,17 +8516,20 @@ "type": "integer", "description": "Document automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$collectionId": { "type": "string", "description": "Collection ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/app/config/specs/swagger2-1.7.x-console.json b/app/config/specs/swagger2-1.7.x-console.json index 32afa9e180..4b458c3a95 100644 --- a/app/config/specs/swagger2-1.7.x-console.json +++ b/app/config/specs/swagger2-1.7.x-console.json @@ -38139,17 +38139,20 @@ "type": "integer", "description": "Document automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$collectionId": { "type": "string", "description": "Collection ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", @@ -44900,6 +44903,11 @@ "description": "AAAA target for your Appwrite custom domains.", "x-example": "::1" }, + "_APP_DOMAIN_TARGET_CAA": { + "type": "string", + "description": "CAA target for your Appwrite custom domains.", + "x-example": "digicert.com" + }, "_APP_STORAGE_LIMIT": { "type": "integer", "description": "Maximum file size allowed for file upload in bytes.", @@ -44957,6 +44965,7 @@ "_APP_DOMAIN_TARGET_CNAME", "_APP_DOMAIN_TARGET_A", "_APP_DOMAIN_TARGET_AAAA", + "_APP_DOMAIN_TARGET_CAA", "_APP_STORAGE_LIMIT", "_APP_COMPUTE_SIZE_LIMIT", "_APP_USAGE_STATS", @@ -44972,6 +44981,7 @@ "_APP_DOMAIN_TARGET_CNAME": "appwrite.io", "_APP_DOMAIN_TARGET_A": "127.0.0.1", "_APP_DOMAIN_TARGET_AAAA": "::1", + "_APP_DOMAIN_TARGET_CAA": "digicert.com", "_APP_STORAGE_LIMIT": "30000000", "_APP_COMPUTE_SIZE_LIMIT": "30000000", "_APP_USAGE_STATS": "enabled", diff --git a/app/config/specs/swagger2-1.7.x-server.json b/app/config/specs/swagger2-1.7.x-server.json index 391adc2dfc..20cf716590 100644 --- a/app/config/specs/swagger2-1.7.x-server.json +++ b/app/config/specs/swagger2-1.7.x-server.json @@ -28092,17 +28092,20 @@ "type": "integer", "description": "Document automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$collectionId": { "type": "string", "description": "Collection ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index 21264f2afb..f875b23616 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -8999,17 +8999,20 @@ "type": "integer", "description": "Document automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$collectionId": { "type": "string", "description": "Collection ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index c8597b8b80..d44f1ee6cb 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -44308,17 +44308,20 @@ "type": "integer", "description": "Document automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$collectionId": { "type": "string", "description": "Collection ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", @@ -50232,6 +50235,11 @@ "description": "AAAA target for your Appwrite custom domains.", "x-example": "::1" }, + "_APP_DOMAIN_TARGET_CAA": { + "type": "string", + "description": "CAA target for your Appwrite custom domains.", + "x-example": "digicert.com" + }, "_APP_STORAGE_LIMIT": { "type": "integer", "description": "Maximum file size allowed for file upload in bytes.", @@ -50289,6 +50297,7 @@ "_APP_DOMAIN_TARGET_CNAME", "_APP_DOMAIN_TARGET_A", "_APP_DOMAIN_TARGET_AAAA", + "_APP_DOMAIN_TARGET_CAA", "_APP_STORAGE_LIMIT", "_APP_COMPUTE_SIZE_LIMIT", "_APP_USAGE_STATS", diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index a7f93152d1..f929384a42 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -33889,17 +33889,20 @@ "type": "integer", "description": "Document automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$collectionId": { "type": "string", "description": "Collection ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/app/config/variables.php b/app/config/variables.php index 71ed13a483..8fd00557b3 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -151,6 +151,24 @@ return [ 'question' => '', 'filter' => '' ], + [ + 'name' => '_APP_DOMAIN_TARGET_CAA', + 'description' => 'A CAA record domain that can be used to validate custom domains. Value should be domain\'s hostname.', + 'introduction' => '', + 'default' => '', + 'required' => false, + 'question' => '', + 'filter' => '' + ], + [ + 'name' => '_APP_DNS', + 'description' => 'DNS server to use for domain validation. Default: 8.8.8.8', + 'introduction' => '', + 'default' => '8.8.8.8', + 'required' => false, + 'question' => '', + 'filter' => '' + ], [ 'name' => '_APP_CONSOLE_WHITELIST_ROOT', 'description' => 'This option allows you to disable the creation of new users on the Appwrite console. When enabled only 1 user will be able to use the registration form. New users can be added by inviting them to your project. By default this option is enabled.', diff --git a/app/controllers/api/console.php b/app/controllers/api/console.php index 558dc0e4ef..b0619df3b3 100644 --- a/app/controllers/api/console.php +++ b/app/controllers/api/console.php @@ -71,6 +71,8 @@ App::get('/v1/console/variables') '_APP_DOMAIN_TARGET_CNAME' => System::getEnv('_APP_DOMAIN_TARGET_CNAME'), '_APP_DOMAIN_TARGET_AAAA' => System::getEnv('_APP_DOMAIN_TARGET_AAAA'), '_APP_DOMAIN_TARGET_A' => System::getEnv('_APP_DOMAIN_TARGET_A'), + // Combine CAA domain with most common flags and tag (no parameters) + '_APP_DOMAIN_TARGET_CAA' => '0 issue "' . System::getEnv('_APP_DOMAIN_TARGET_CAA') . '"', '_APP_STORAGE_LIMIT' => +System::getEnv('_APP_STORAGE_LIMIT'), '_APP_COMPUTE_SIZE_LIMIT' => +System::getEnv('_APP_COMPUTE_SIZE_LIMIT'), '_APP_USAGE_STATS' => System::getEnv('_APP_USAGE_STATS'), diff --git a/app/controllers/api/proxy.php b/app/controllers/api/proxy.php index 417ea602ba..4a64448335 100644 --- a/app/controllers/api/proxy.php +++ b/app/controllers/api/proxy.php @@ -286,6 +286,19 @@ App::patch('/v1/proxy/rules/:ruleId/verification') throw new Exception(Exception::RULE_VERIFICATION_FAILED); } + // Ensure CAA won't block certificate issuance + if (!empty(System::getEnv('_APP_DOMAIN_TARGET_CAA', ''))) { + $validationStart = \microtime(true); + $validator = new DNS(System::getEnv('_APP_DOMAIN_TARGET_CAA', ''), DNS::RECORD_CAA); + if (!$validator->isValid($domain->get())) { + $log->addExtra('dnsTimingCaa', \strval(\microtime(true) - $validationStart)); + $log->addTag('dnsDomain', $domain->get()); + $error = $validator->getDescription(); + $log->addExtra('dnsResponse', \is_array($error) ? \json_encode($error) : \strval($error)); + throw new Exception(Exception::RULE_VERIFICATION_FAILED, 'Domain verification failed because CAA records do not allow Appwrite\'s certificate issuer.'); + } + } + $dbForPlatform->updateDocument('rules', $rule->getId(), $rule->setAttribute('status', 'verifying')); // Issue a TLS certificate when domain is verified diff --git a/app/controllers/general.php b/app/controllers/general.php index ee580e6f07..5accae6a32 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1038,11 +1038,19 @@ App::init() /** * Deprecation Warning */ + /** @var \Appwrite\SDK\Method $sdk */ $sdk = $route->getLabel('sdk', false); $deprecationWarning = 'This route is deprecated. See the updated documentation for improved compatibility and migration details.'; $sdkItems = is_array($sdk) ? $sdk : (!empty($sdk) ? [$sdk] : []); - foreach ($sdkItems as $sdkItem) { - if ($sdkItem->isDeprecated()) { + if (!empty($sdkItems) && count($sdkItems) > 0) { + $allDeprecated = true; + foreach ($sdkItems as $sdkItem) { + if (!$sdkItem->isDeprecated()) { + $allDeprecated = false; + break; + } + } + if ($allDeprecated) { $warnings[] = $deprecationWarning; } } diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index c526687d8f..8e165e45e5 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -95,6 +95,8 @@ $image = $this->getParam('image', ''); - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA - _APP_DOMAIN_TARGET_A + - _APP_DOMAIN_TARGET_CAA + - _APP_DNS - _APP_DOMAIN_FUNCTIONS - _APP_REDIS_HOST - _APP_REDIS_PORT @@ -472,6 +474,8 @@ $image = $this->getParam('image', ''); - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA - _APP_DOMAIN_TARGET_A + - _APP_DOMAIN_TARGET_CAA + - _APP_DNS - _APP_DOMAIN_FUNCTIONS - _APP_EMAIL_CERTIFICATES - _APP_REDIS_HOST @@ -629,6 +633,8 @@ $image = $this->getParam('image', ''); - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA - _APP_DOMAIN_TARGET_A + - _APP_DOMAIN_TARGET_CAA + - _APP_DNS - _APP_EMAIL_SECURITY - _APP_REDIS_HOST - _APP_REDIS_PORT @@ -660,6 +666,8 @@ $image = $this->getParam('image', ''); - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA - _APP_DOMAIN_TARGET_A + - _APP_DOMAIN_TARGET_CAA + - _APP_DNS - _APP_DOMAIN_FUNCTIONS - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST diff --git a/composer.json b/composer.json index f00b07d198..35d685c80e 100644 --- a/composer.json +++ b/composer.json @@ -55,6 +55,7 @@ "utopia-php/database": "dev-get-sequence-default as 0.77.3", "utopia-php/detector": "0.1.*", "utopia-php/domains": "0.8.*", + "utopia-php/dns": "0.3.*", "utopia-php/dsn": "0.2.1", "utopia-php/framework": "0.33.*", "utopia-php/fetch": "0.4.*", diff --git a/composer.lock b/composer.lock index 208092f865..606ef7b074 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": "5bdc8315d4b8a0d8004905a08c72336c", + "content-hash": "72786a5d87326f396250f18d725dea23", "packages": [ { "name": "adhocore/jwt", @@ -3545,16 +3545,16 @@ }, { "name": "utopia-php/database", - "version": "dev-get-sequence-default", + "version": "0.77.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "a50279437a3ce76e610c58a9988a0074c9f80c2e" + "reference": "a8ab3a7b25a5a1edf7e7afc83eda9f7aec5ba57d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/a50279437a3ce76e610c58a9988a0074c9f80c2e", - "reference": "a50279437a3ce76e610c58a9988a0074c9f80c2e", + "url": "https://api.github.com/repos/utopia-php/database/zipball/a8ab3a7b25a5a1edf7e7afc83eda9f7aec5ba57d", + "reference": "a8ab3a7b25a5a1edf7e7afc83eda9f7aec5ba57d", "shasum": "" }, "require": { @@ -3595,9 +3595,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/get-sequence-default" + "source": "https://github.com/utopia-php/database/tree/0.77.0" }, - "time": "2025-08-11T10:20:16+00:00" + "time": "2025-08-07T09:30:32+00:00" }, { "name": "utopia-php/detector", @@ -3644,6 +3644,62 @@ }, "time": "2025-05-19T11:01:28+00:00" }, + { + "name": "utopia-php/dns", + "version": "0.3.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/dns.git", + "reference": "8fd4161bc3a8021a670c1101b40f6b09a97f1a54" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/dns/zipball/8fd4161bc3a8021a670c1101b40f6b09a97f1a54", + "reference": "8fd4161bc3a8021a670c1101b40f6b09a97f1a54", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/cli": "0.15.*", + "utopia-php/telemetry": "^0.1.1" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.3", + "rregeer/phpunit-coverage-check": "^0.3.1", + "swoole/ide-helper": "4.6.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\DNS\\": "src/DNS" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + } + ], + "description": "Lite & fast micro PHP DNS server abstraction that is **easy to use**.", + "keywords": [ + "dns", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/dns/issues", + "source": "https://github.com/utopia-php/dns/tree/0.3.0" + }, + "time": "2025-08-04T11:05:53+00:00" + }, { "name": "utopia-php/domains", "version": "0.8.0", @@ -6091,16 +6147,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.24", + "version": "9.6.23", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "ea49afa29aeea25ea7bf9de9fdd7cab163cc0701" + "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ea49afa29aeea25ea7bf9de9fdd7cab163cc0701", - "reference": "ea49afa29aeea25ea7bf9de9fdd7cab163cc0701", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", + "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", "shasum": "" }, "require": { @@ -6111,7 +6167,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.4", + "myclabs/deep-copy": "^1.13.1", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", @@ -6122,11 +6178,11 @@ "phpunit/php-timer": "^5.0.3", "sebastian/cli-parser": "^1.0.2", "sebastian/code-unit": "^1.0.8", - "sebastian/comparator": "^4.0.9", + "sebastian/comparator": "^4.0.8", "sebastian/diff": "^4.0.6", "sebastian/environment": "^5.1.5", "sebastian/exporter": "^4.0.6", - "sebastian/global-state": "^5.0.8", + "sebastian/global-state": "^5.0.7", "sebastian/object-enumerator": "^4.0.4", "sebastian/resource-operations": "^3.0.4", "sebastian/type": "^3.2.1", @@ -6174,7 +6230,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.24" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.23" }, "funding": [ { @@ -6198,7 +6254,7 @@ "type": "tidelift" } ], - "time": "2025-08-10T08:32:42+00:00" + "time": "2025-05-02T06:40:34+00:00" }, { "name": "psr/cache", @@ -6418,16 +6474,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.9", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5" + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/67a2df3a62639eab2cc5906065e9805d4fd5dfc5", - "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", "shasum": "" }, "require": { @@ -6480,27 +6536,15 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.9" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", - "type": "tidelift" } ], - "time": "2025-08-10T06:51:50+00:00" + "time": "2022-09-14T12:41:17+00:00" }, { "name": "sebastian/complexity", @@ -6767,16 +6811,16 @@ }, { "name": "sebastian/global-state", - "version": "5.0.8", + "version": "5.0.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6" + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6", - "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", "shasum": "" }, "require": { @@ -6819,27 +6863,15 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.8" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", - "type": "tidelift" } ], - "time": "2025-08-10T07:10:35+00:00" + "time": "2024-03-02T06:35:11+00:00" }, { "name": "sebastian/lines-of-code", @@ -7012,16 +7044,16 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.6", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "539c6691e0623af6dc6f9c20384c120f963465a0" + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0", - "reference": "539c6691e0623af6dc6f9c20384c120f963465a0", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", "shasum": "" }, "require": { @@ -7063,27 +7095,15 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", - "type": "tidelift" } ], - "time": "2025-08-10T06:57:39+00:00" + "time": "2023-02-03T06:07:39+00:00" }, { "name": "sebastian/resource-operations", @@ -8345,18 +8365,9 @@ "time": "2024-03-07T20:33:40+00:00" } ], - "aliases": [ - { - "package": "utopia-php/database", - "version": "dev-get-sequence-default", - "alias": "0.77.3", - "alias_normalized": "0.77.3.0" - } - ], + "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "utopia-php/database": 20 - }, + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/docker-compose.yml b/docker-compose.yml index ad135c708b..331daea522 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -120,6 +120,8 @@ services: - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA - _APP_DOMAIN_TARGET_A + - _APP_DOMAIN_TARGET_CAA + - _APP_DNS - _APP_DOMAIN_FUNCTIONS - _APP_REDIS_HOST - _APP_REDIS_PORT @@ -535,6 +537,8 @@ services: - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA - _APP_DOMAIN_TARGET_A + - _APP_DOMAIN_TARGET_CAA + - _APP_DNS - _APP_DOMAIN_FUNCTIONS - _APP_EMAIL_CERTIFICATES - _APP_REDIS_HOST @@ -704,6 +708,8 @@ services: - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA - _APP_DOMAIN_TARGET_A + - _APP_DOMAIN_TARGET_CAA + - _APP_DNS - _APP_EMAIL_SECURITY - _APP_REDIS_HOST - _APP_REDIS_PORT @@ -738,6 +744,8 @@ services: - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA - _APP_DOMAIN_TARGET_A + - _APP_DOMAIN_TARGET_CAA + - _APP_DNS - _APP_DOMAIN_FUNCTIONS - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST diff --git a/docs/examples/1.7.x/client-android/java/functions/create-execution.md b/docs/examples/1.7.x/client-android/java/functions/create-execution.md index c138b0ef86..06c50278a5 100644 --- a/docs/examples/1.7.x/client-android/java/functions/create-execution.md +++ b/docs/examples/1.7.x/client-android/java/functions/create-execution.md @@ -15,7 +15,7 @@ functions.createExecution( "", // path (optional) ExecutionMethod.GET, // method (optional) mapOf( "a" to "b" ), // headers (optional) - "", // scheduledAt (optional) + "", // scheduledAt (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.7.x/client-android/kotlin/functions/create-execution.md b/docs/examples/1.7.x/client-android/kotlin/functions/create-execution.md index cb7c60bac1..5e1950b8d9 100644 --- a/docs/examples/1.7.x/client-android/kotlin/functions/create-execution.md +++ b/docs/examples/1.7.x/client-android/kotlin/functions/create-execution.md @@ -15,5 +15,5 @@ val result = functions.createExecution( path = "", // (optional) method = ExecutionMethod.GET, // (optional) headers = mapOf( "a" to "b" ), // (optional) - scheduledAt = "", // (optional) + scheduledAt = "", // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.7.x/server-kotlin/java/functions/create-execution.md b/docs/examples/1.7.x/server-kotlin/java/functions/create-execution.md index 82d48fa55b..93efa0adf8 100644 --- a/docs/examples/1.7.x/server-kotlin/java/functions/create-execution.md +++ b/docs/examples/1.7.x/server-kotlin/java/functions/create-execution.md @@ -16,7 +16,7 @@ functions.createExecution( "", // path (optional) ExecutionMethod.GET, // method (optional) mapOf( "a" to "b" ), // headers (optional) - "", // scheduledAt (optional) + "", // scheduledAt (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.7.x/server-kotlin/kotlin/functions/create-execution.md b/docs/examples/1.7.x/server-kotlin/kotlin/functions/create-execution.md index 94bfa2310c..2734412232 100644 --- a/docs/examples/1.7.x/server-kotlin/kotlin/functions/create-execution.md +++ b/docs/examples/1.7.x/server-kotlin/kotlin/functions/create-execution.md @@ -16,5 +16,5 @@ val response = functions.createExecution( path = "", // optional method = "GET", // optional headers = mapOf( "a" to "b" ), // optional - scheduledAt = "" // optional + scheduledAt = "" // optional ) diff --git a/src/Appwrite/Network/Validator/DNS.php b/src/Appwrite/Network/Validator/DNS.php index 73494ddc3e..f09bb42b02 100644 --- a/src/Appwrite/Network/Validator/DNS.php +++ b/src/Appwrite/Network/Validator/DNS.php @@ -2,24 +2,38 @@ namespace Appwrite\Network\Validator; +use Utopia\DNS\Client; +use Utopia\Domains\Domain; +use Utopia\System\System; use Utopia\Validator; class DNS extends Validator { - public const RECORD_A = 'a'; - public const RECORD_AAAA = 'aaaa'; - public const RECORD_CNAME = 'cname'; + public const RECORD_A = 'A'; + public const RECORD_AAAA = 'AAAA'; + public const RECORD_CNAME = 'CNAME'; + public const RECORD_CAA = 'CAA'; // You can provide domain only (as $target) for CAA validation /** * @var mixed */ protected mixed $logs; + /** + * @var string + */ + protected string $dnsServer; + /** * @param string $target */ - public function __construct(protected string $target, protected string $type = self::RECORD_CNAME) + public function __construct(protected string $target, protected string $type = self::RECORD_CNAME, string $dnsServer = '') { + if (empty($dnsServer)) { + $dnsServer = System::getEnv('_APP_DNS', '8.8.8.8'); + } + + $this->dnsServer = $dnsServer; } /** @@ -42,42 +56,65 @@ class DNS extends Validator * Check if DNS record value matches specific value * * @param mixed $domain - * * @return bool */ public function isValid($value): bool { - $typeNative = match ($this->type) { - self::RECORD_A => DNS_A, - self::RECORD_AAAA => DNS_AAAA, - self::RECORD_CNAME => DNS_CNAME, - default => throw new \Exception('Record type not supported.') - }; - - $dnsKey = match ($this->type) { - self::RECORD_A => 'ip', - self::RECORD_AAAA => 'ipv6', - self::RECORD_CNAME => 'target', - default => throw new \Exception('Record type not supported.') - }; - if (!is_string($value)) { return false; } + $dns = new Client($this->dnsServer); + try { - $records = \dns_get_record($value, $typeNative); - $this->logs = $records; - } catch (\Throwable $th) { + $rawQuery = $dns->query($value, $this->type); + + // Some DNS servers return all records, not only type that's asked for + // Likely occurs when no records of specific type are found + $query = array_filter($rawQuery, function ($record) { + return $record->getTypeName() === $this->type; + }); + + $this->logs = $query; + } catch (\Exception $e) { + $this->logs = ['error' => $e->getMessage()]; return false; } - if (!$records) { + if (empty($query)) { + // CAA records inherit from parent (custom CAA behaviour) + if ($this->type === self::RECORD_CAA) { + $domain = new Domain($value); + if ($domain->get() === $domain->getApex()) { + return true; // No CAA on apex domain means anyone can issue certificate + } + + // Recursive validation by parent domain + $parts = \explode('.', $value); + \array_shift($parts); + $parentDomain = \implode('.', $parts); + $validator = new DNS($this->target, DNS::RECORD_CAA, $this->dnsServer); + return $validator->isValid($parentDomain); + } + return false; } - foreach ($records as $record) { - if (isset($record[$dnsKey]) && $record[$dnsKey] === $this->target) { + foreach ($query as $record) { + // CAA validation only needs to ensure domain + if ($this->type === self::RECORD_CAA) { + // Extract domain; comments showcase extraction steps in most complex scenario + $rdata = $record->getRdata(); // 255 issuewild "certainly.com;validationmethods=tls-alpn-01;retrytimeout=3600" + $rdata = \explode(' ', $rdata, 3)[2] ?? ''; // "certainly.com;validationmethods=tls-alpn-01;retrytimeout=3600" + $rdata = \trim($rdata, '"'); // certainly.com;validationmethods=tls-alpn-01;retrytimeout=3600 + $rdata = \explode(';', $rdata, 2)[0] ?? ''; // certainly.com + + if ($rdata === $this->target) { + return true; + } + } + + if ($record->getRdata() === $this->target) { return true; } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php index f8ae29bd3e..0f8a8688c6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php @@ -106,8 +106,13 @@ class XList extends Action ]); $audit = new Audit($dbForProject); - // getContext() => `document` or `row`. - $resource = 'database/' . $databaseId . '/collection/' . $collectionId . '/' .$this->getContext(). '/' . $document->getId(); + $type = $this->getCollectionsEventsContext(); + $context = $this->getContext(); + $resource = match ($context) { + ROWS => "database/$databaseId/grid/$type/$collectionId/$context/{$document->getId()}", + default => "database/$databaseId/$type/$collectionId/$context/{$document->getId()}", + }; + $logs = $audit->getLogsByResource($resource, $queries); $output = []; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php index 0eca31e679..359fed118a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php @@ -103,7 +103,12 @@ class XList extends Action ]); $audit = new Audit($dbForProject); - $resource = 'database/' . $databaseId . '/' . $this->getContext() . '/' . $collectionId; + $context = $this->getContext(); + $resource = match ($context) { + TABLES => "database/$databaseId/grid/$context/$collectionId", + default => "database/$databaseId/$context/$collectionId", + }; + $logs = $audit->getLogsByResource($resource, $queries); $output = []; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Boolean/Create.php index baa4eaa146..a28b52aef0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Boolean/Create.php @@ -35,7 +35,7 @@ class Create extends BooleanCreate ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Boolean/Update.php index 2ea5314163..c2f40c96fb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Boolean/Update.php @@ -37,7 +37,7 @@ class Update extends BooleanUpdate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Datetime/Create.php index 501cc044d4..f1214f707b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Datetime/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Datetime/Create.php @@ -37,7 +37,7 @@ class Create extends DatetimeCreate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Datetime/Update.php index 34384e4d35..f2f5001242 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Datetime/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Datetime/Update.php @@ -39,7 +39,7 @@ class Update extends DatetimeUpdate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Delete.php index d0934b799f..9af55bee70 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Delete.php @@ -36,7 +36,7 @@ class Delete extends AttributesDelete ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.delete') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Email/Create.php index 841d18016f..3fd8d88fb5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Email/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Email/Create.php @@ -36,7 +36,7 @@ class Create extends EmailCreate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Email/Update.php index f162a18494..180ce40b6d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Email/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Email/Update.php @@ -38,7 +38,7 @@ class Update extends EmailUpdate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Enum/Create.php index 430d3f0220..c3ccffec1e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Enum/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Enum/Create.php @@ -38,7 +38,7 @@ class Create extends EnumCreate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Enum/Update.php index 6562742937..32f77a488a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Enum/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Enum/Update.php @@ -40,7 +40,7 @@ class Update extends EnumUpdate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Float/Create.php index 4dd9c53950..42699ca45e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Float/Create.php @@ -36,7 +36,7 @@ class Create extends FloatCreate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Float/Update.php index 3130e8ba2e..cd801450c7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Float/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Float/Update.php @@ -38,7 +38,7 @@ class Update extends FloatUpdate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/IP/Create.php index beae222ceb..541214897e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/IP/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/IP/Create.php @@ -36,7 +36,7 @@ class Create extends IPCreate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/IP/Update.php index c187c06801..c52105c812 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/IP/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/IP/Update.php @@ -38,7 +38,7 @@ class Update extends IPUpdate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Integer/Create.php index f97f4079ee..9873ad83b1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Integer/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Integer/Create.php @@ -36,7 +36,7 @@ class Create extends IntegerCreate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Integer/Update.php index ad4832ed10..e70f8bf7b0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Integer/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Integer/Update.php @@ -38,7 +38,7 @@ class Update extends IntegerUpdate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Relationship/Create.php index c340d1c574..d6942a4d15 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Relationship/Create.php @@ -37,7 +37,7 @@ class Create extends RelationshipCreate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Relationship/Update.php index 0811025ae3..729760bde0 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Relationship/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/Relationship/Update.php @@ -37,7 +37,7 @@ class Update extends RelationshipUpdate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/String/Create.php index 4ae844fd84..5829ce5361 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/String/Create.php @@ -38,7 +38,7 @@ class Create extends StringCreate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/String/Update.php index ed54ae9139..6333443686 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/String/Update.php @@ -40,7 +40,7 @@ class Update extends StringUpdate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/URL/Create.php index 7ae3214cb9..429ef06dbd 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/URL/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/URL/Create.php @@ -36,7 +36,7 @@ class Create extends URLCreate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create') ->label('audits.event', 'column.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/URL/Update.php index 17570a9842..de4b4ab79b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/URL/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Columns/URL/Update.php @@ -38,7 +38,7 @@ class Update extends URLUpdate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update') ->label('audits.event', 'column.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Create.php index 15aec531e7..f4274079bc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Create.php @@ -38,7 +38,7 @@ class Create extends CollectionCreate ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'table.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{response.$id}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{response.$id}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: null, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Delete.php index cb2923f572..13c3a7196b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Delete.php @@ -34,7 +34,7 @@ class Delete extends CollectionDelete ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].delete') ->label('audits.event', 'table.delete') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: null, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Indexes/Create.php index 3d9c8709a6..9bb3d6f123 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Indexes/Create.php @@ -40,7 +40,7 @@ class Create extends IndexCreate ->label('scope', 'tables.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'index.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Indexes/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Indexes/Delete.php index a2c23ea60b..679de9b77c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Indexes/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Indexes/Delete.php @@ -39,7 +39,7 @@ class Delete extends IndexDelete ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].indexes.[indexId].update') ->label('audits.event', 'index.delete') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: $this->getSdkGroup(), diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Delete.php index 9a594245b3..6816fc3b13 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Delete.php @@ -35,7 +35,7 @@ class Delete extends DocumentsDelete ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'rows.delete') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Update.php index d33e8b0fb6..8c640f00f8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Update.php @@ -36,7 +36,7 @@ class Update extends DocumentsUpdate ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'rows.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Upsert.php index b85ee87782..35f0a99bdc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Bulk/Upsert.php @@ -35,7 +35,7 @@ class Upsert extends DocumentsUpsert ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'row.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Decrement.php index fd642de09e..272510335f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Decrement.php @@ -36,7 +36,7 @@ class Decrement extends DecrementDocumentAttribute ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'rows.decrement') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Increment.php index b3af9bb1b9..2a28418a6e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Column/Increment.php @@ -36,7 +36,7 @@ class Increment extends IncrementDocumentAttribute ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'rows.increment') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Create.php index 6fadbe1412..d5a84bbb7c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Create.php @@ -44,7 +44,7 @@ class Create extends DocumentCreate ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'row.create') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Delete.php index b1f93164df..f821aabe6e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Delete.php @@ -40,7 +40,7 @@ class Delete extends DocumentDelete ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].delete') ->label('audits.event', 'row.delete') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}/row/{request.rowId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}/row/{request.rowId}') ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Update.php index f9b1ce693e..7731e10e9d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Update.php @@ -37,7 +37,7 @@ class Update extends DocumentUpdate ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'row.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}/row/{response.$id}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}/row/{response.$id}') ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Upsert.php index 145217e03b..d9756eab87 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Rows/Upsert.php @@ -37,7 +37,7 @@ class Upsert extends DocumentUpsert ->label('scope', 'rows.write') ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('audits.event', 'row.upsert') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}/row/{response.$id}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}/row/{response.$id}') ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Update.php index bbc82aa52d..b4e8df6e57 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Grids/Tables/Update.php @@ -37,7 +37,7 @@ class Update extends CollectionUpdate ->label('resourceType', RESOURCE_TYPE_DATABASES) ->label('event', 'databases.[databaseId].tables.[tableId].update') ->label('audits.event', 'table.update') - ->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}') + ->label('audits.resource', 'database/{request.databaseId}/grid/table/{request.tableId}') ->label('sdk', new Method( namespace: $this->getSdkNamespace(), group: null, diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php index 207f95ff7d..300383ef6d 100644 --- a/src/Appwrite/Platform/Workers/Certificates.php +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -337,6 +337,19 @@ class Certificates extends Action throw new Exception('Failed to verify domain DNS records.'); } + + // Ensure CAA won't block certificate issuance + if (!empty(System::getEnv('_APP_DOMAIN_TARGET_CAA', ''))) { + $validationStart = \microtime(true); + $validator = new DNS(System::getEnv('_APP_DOMAIN_TARGET_CAA', ''), DNS::RECORD_CAA); + if (!$validator->isValid($domain->get())) { + $log->addExtra('dnsTimingCaa', \strval(\microtime(true) - $validationStart)); + $log->addTag('dnsDomain', $domain->get()); + $error = $validator->getDescription(); + $log->addExtra('dnsResponse', \is_array($error) ? \json_encode($error) : \strval($error)); + throw new Exception('Failed to verify domain DNS records. CAA records do not allow Appwrite\'s certificate issuer.'); + } + } } else { // Main domain validation // TODO: Would be awesome to check A/AAAA record here. Maybe dry run? diff --git a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php index e0e75a2d40..20d7a7e5e8 100644 --- a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php @@ -715,6 +715,7 @@ class OpenAPI3 extends Format break; } + $readOnly = $rule['readOnly'] ?? false; if ($rule['array']) { $output['components']['schemas'][$model->getType()]['properties'][$name] = [ 'type' => 'array', @@ -728,6 +729,9 @@ class OpenAPI3 extends Format if ($format) { $output['components']['schemas'][$model->getType()]['properties'][$name]['items']['format'] = $format; } + if ($readOnly) { + $output['components']['schemas'][$model->getType()]['properties'][$name]['readOnly'] = true; + } } else { $output['components']['schemas'][$model->getType()]['properties'][$name] = [ 'type' => $type, @@ -738,6 +742,9 @@ class OpenAPI3 extends Format if ($format) { $output['components']['schemas'][$model->getType()]['properties'][$name]['format'] = $format; } + if ($readOnly) { + $output['components']['schemas'][$model->getType()]['properties'][$name]['readOnly'] = true; + } } if ($items) { $output['components']['schemas'][$model->getType()]['properties'][$name]['items'] = $items; diff --git a/src/Appwrite/SDK/Specification/Format/Swagger2.php b/src/Appwrite/SDK/Specification/Format/Swagger2.php index dbcb3f5ab5..db561650d8 100644 --- a/src/Appwrite/SDK/Specification/Format/Swagger2.php +++ b/src/Appwrite/SDK/Specification/Format/Swagger2.php @@ -724,6 +724,7 @@ class Swagger2 extends Format break; } + $readOnly = $rule['readOnly'] ?? false; if ($rule['type'] == 'json') { $output['definitions'][$model->getType()]['properties'][$name] = [ 'type' => $type, @@ -731,6 +732,10 @@ class Swagger2 extends Format 'description' => $rule['description'] ?? '', 'x-example' => $rule['example'] ?? null, ]; + + if ($readOnly) { + $output['definitions'][$model->getType()]['properties'][$name]['readOnly'] = true; + } continue; } @@ -747,6 +752,9 @@ class Swagger2 extends Format if ($format) { $output['definitions'][$model->getType()]['properties'][$name]['items']['format'] = $format; } + if ($readOnly) { + $output['definitions'][$model->getType()]['properties'][$name]['readOnly'] = true; + } } else { $output['definitions'][$model->getType()]['properties'][$name] = [ 'type' => $type, @@ -757,6 +765,9 @@ class Swagger2 extends Format if ($format) { $output['definitions'][$model->getType()]['properties'][$name]['format'] = $format; } + if ($readOnly) { + $output['definitions'][$model->getType()]['properties'][$name]['readOnly'] = true; + } } if ($items) { $output['definitions'][$model->getType()]['properties'][$name]['items'] = $items; diff --git a/src/Appwrite/Utopia/Response/Model.php b/src/Appwrite/Utopia/Response/Model.php index 32de9fa035..962da4834c 100644 --- a/src/Appwrite/Utopia/Response/Model.php +++ b/src/Appwrite/Utopia/Response/Model.php @@ -91,7 +91,8 @@ abstract class Model 'array' => false, 'description' => '', 'example' => '', - 'sensitive' => false + 'sensitive' => false, + 'readOnly' => false ], $options); return $this; @@ -129,6 +130,27 @@ abstract class Model return $list; } + /** + * Get Readonly Fields + * + * Returns list of field names that are marked as readOnly + * and should not be allowed in create/update payloads + * + * @return array + */ + public function getReadonlyFields(): array + { + $list = []; + + foreach ($this->rules as $key => $rule) { + if ($rule['readOnly'] ?? false) { + $list[] = $key; + } + } + + return $list; + } + /** * Is None * diff --git a/src/Appwrite/Utopia/Response/Model/ConsoleVariables.php b/src/Appwrite/Utopia/Response/Model/ConsoleVariables.php index 97dae2efcd..b81502f0a1 100644 --- a/src/Appwrite/Utopia/Response/Model/ConsoleVariables.php +++ b/src/Appwrite/Utopia/Response/Model/ConsoleVariables.php @@ -10,24 +10,30 @@ class ConsoleVariables extends Model public function __construct() { $this - ->addRule('_APP_DOMAIN_TARGET_CNAME', [ - 'type' => self::TYPE_STRING, - 'description' => 'CNAME target for your Appwrite custom domains.', - 'default' => '', - 'example' => 'appwrite.io', - ]) - ->addRule('_APP_DOMAIN_TARGET_A', [ - 'type' => self::TYPE_STRING, - 'description' => 'A target for your Appwrite custom domains.', - 'default' => '', - 'example' => '127.0.0.1', - ]) - ->addRule('_APP_DOMAIN_TARGET_AAAA', [ - 'type' => self::TYPE_STRING, - 'description' => 'AAAA target for your Appwrite custom domains.', - 'default' => '', - 'example' => '::1', - ]) + ->addRule('_APP_DOMAIN_TARGET_CNAME', [ + 'type' => self::TYPE_STRING, + 'description' => 'CNAME target for your Appwrite custom domains.', + 'default' => '', + 'example' => 'appwrite.io', + ]) + ->addRule('_APP_DOMAIN_TARGET_A', [ + 'type' => self::TYPE_STRING, + 'description' => 'A target for your Appwrite custom domains.', + 'default' => '', + 'example' => '127.0.0.1', + ]) + ->addRule('_APP_DOMAIN_TARGET_AAAA', [ + 'type' => self::TYPE_STRING, + 'description' => 'AAAA target for your Appwrite custom domains.', + 'default' => '', + 'example' => '::1', + ]) + ->addRule('_APP_DOMAIN_TARGET_CAA', [ + 'type' => self::TYPE_STRING, + 'description' => 'CAA target for your Appwrite custom domains.', + 'default' => '', + 'example' => 'digicert.com', + ]) ->addRule('_APP_STORAGE_LIMIT', [ 'type' => self::TYPE_INTEGER, 'description' => 'Maximum file size allowed for file upload in bytes.', diff --git a/src/Appwrite/Utopia/Response/Model/Document.php b/src/Appwrite/Utopia/Response/Model/Document.php index dc9faf6a8e..5bad504a63 100644 --- a/src/Appwrite/Utopia/Response/Model/Document.php +++ b/src/Appwrite/Utopia/Response/Model/Document.php @@ -41,18 +41,21 @@ class Document extends Any 'description' => 'Document automatically incrementing ID.', 'default' => 0, 'example' => 1, + 'readOnly' => true, ]) ->addRule('$collectionId', [ 'type' => self::TYPE_STRING, 'description' => 'Collection ID.', 'default' => '', 'example' => '5e5ea5c15117e', + 'readOnly' => true, ]) ->addRule('$databaseId', [ 'type' => self::TYPE_STRING, 'description' => 'Database ID.', 'default' => '', 'example' => '5e5ea5c15117e', + 'readOnly' => true, ]) ->addRule('$createdAt', [ 'type' => self::TYPE_DATETIME, diff --git a/tests/e2e/Scopes/Scope.php b/tests/e2e/Scopes/Scope.php index c072fdca35..2dbeae961e 100644 --- a/tests/e2e/Scopes/Scope.php +++ b/tests/e2e/Scopes/Scope.php @@ -97,6 +97,21 @@ abstract class Scope extends TestCase return $request; } + protected function assertSamePixels(string $expectedImagePath, string $actualImageBlob): void + { + $expected = new \Imagick($expectedImagePath); + $actual = new \Imagick(); + $actual->readImageBlob($actualImageBlob); + + foreach ([$expected, $actual] as $image) { + $image->setImageFormat('PNG'); + $image->stripImage(); + $image->setOption('png:exclude-chunks', 'date,time,iCCP,sRGB,gAMA,cHRM'); + } + + $this->assertSame($expected->getImageSignature(), $actual->getImageSignature()); + } + /** * @deprecated Use assertLastRequest instead. Used only historically in webhook tests */ diff --git a/tests/e2e/Services/Avatars/AvatarsBase.php b/tests/e2e/Services/Avatars/AvatarsBase.php index 6d34418438..83f70b8978 100644 --- a/tests/e2e/Services/Avatars/AvatarsBase.php +++ b/tests/e2e/Services/Avatars/AvatarsBase.php @@ -347,7 +347,7 @@ trait AvatarsBase $this->assertEquals(400, $image->getImageWidth()); $this->assertEquals(400, $image->getImageHeight()); $this->assertEquals('PNG', $image->getImageFormat()); - $this->assertEquals(strlen(\file_get_contents(__DIR__ . '/../../../resources/qr/qr-default.png')), strlen($response['body'])); + $this->assertSamePixels(__DIR__ . '/../../../resources/qr/qr-default.png', $response['body']); $response = $this->client->call(Client::METHOD_GET, '/avatars/qr', [ 'x-appwrite-project' => $this->getProject()['$id'], @@ -365,7 +365,7 @@ trait AvatarsBase $this->assertEquals(200, $image->getImageWidth()); $this->assertEquals(200, $image->getImageHeight()); $this->assertEquals('PNG', $image->getImageFormat()); - $this->assertEquals(strlen(\file_get_contents(__DIR__ . '/../../../resources/qr/qr-size-200.png')), strlen($response['body'])); + $this->assertSamePixels(__DIR__ . '/../../../resources/qr/qr-size-200.png', $response['body']); $response = $this->client->call(Client::METHOD_GET, '/avatars/qr', [ 'x-appwrite-project' => $this->getProject()['$id'], @@ -384,7 +384,7 @@ trait AvatarsBase $this->assertEquals(200, $image->getImageWidth()); $this->assertEquals(200, $image->getImageHeight()); $this->assertEquals('PNG', $image->getImageFormat()); - $this->assertEquals(strlen(\file_get_contents(__DIR__ . '/../../../resources/qr/qr-size-200-margin-10.png')), strlen($response['body'])); + $this->assertSamePixels(__DIR__ . '/../../../resources/qr/qr-size-200-margin-10.png', $response['body']); $response = $this->client->call(Client::METHOD_GET, '/avatars/qr', [ 'x-appwrite-project' => $this->getProject()['$id'], @@ -400,7 +400,7 @@ trait AvatarsBase $this->assertEquals(200, $image->getImageWidth()); $this->assertEquals(200, $image->getImageHeight()); $this->assertEquals('PNG', $image->getImageFormat()); - $this->assertEquals(strlen(\file_get_contents(__DIR__ . '/../../../resources/qr/qr-size-200-margin-10.png')), strlen($response['body'])); + $this->assertSamePixels(__DIR__ . '/../../../resources/qr/qr-size-200-margin-10.png', $response['body']); $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals('attachment; filename="qr.png"', $response['headers']['content-disposition']); diff --git a/tests/e2e/Services/Console/ConsoleConsoleClientTest.php b/tests/e2e/Services/Console/ConsoleConsoleClientTest.php index 6059cb2000..340cabc8c0 100644 --- a/tests/e2e/Services/Console/ConsoleConsoleClientTest.php +++ b/tests/e2e/Services/Console/ConsoleConsoleClientTest.php @@ -24,10 +24,11 @@ class ConsoleConsoleClientTest extends Scope ], $this->getHeaders())); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertCount(13, $response['body']); + $this->assertCount(14, $response['body']); $this->assertIsString($response['body']['_APP_DOMAIN_TARGET_CNAME']); $this->assertIsString($response['body']['_APP_DOMAIN_TARGET_A']); $this->assertIsString($response['body']['_APP_DOMAIN_TARGET_AAAA']); + $this->assertIsString($response['body']['_APP_DOMAIN_TARGET_CAA']); $this->assertIsInt($response['body']['_APP_STORAGE_LIMIT']); $this->assertIsInt($response['body']['_APP_COMPUTE_SIZE_LIMIT']); $this->assertIsBool($response['body']['_APP_DOMAIN_ENABLED']); diff --git a/tests/e2e/Services/GraphQL/AvatarsTest.php b/tests/e2e/Services/GraphQL/AvatarsTest.php index e3aaa2ce80..f805d965e2 100644 --- a/tests/e2e/Services/GraphQL/AvatarsTest.php +++ b/tests/e2e/Services/GraphQL/AvatarsTest.php @@ -29,7 +29,9 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(18546, \strlen($creditCardIcon['body'])); + $this->assertEquals(200, $creditCardIcon['headers']['status-code']); + $this->assertNotEmpty($creditCardIcon['body']); + $this->assertStringContainsString('image/', $creditCardIcon['headers']['content-type']); return $creditCardIcon['body']; } @@ -50,7 +52,9 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(13312, \strlen($browserIcon['body'])); + $this->assertEquals(200, $browserIcon['headers']['status-code']); + $this->assertNotEmpty($browserIcon['body']); + $this->assertStringContainsString('image/', $browserIcon['headers']['content-type']); return $browserIcon['body']; } @@ -71,7 +75,9 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(8814, \strlen($countryFlag['body'])); + $this->assertEquals(200, $countryFlag['headers']['status-code']); + $this->assertNotEmpty($countryFlag['body']); + $this->assertStringContainsString('image/', $countryFlag['headers']['content-type']); return $countryFlag['body']; } @@ -92,7 +98,9 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(52585, \strlen($image['body'])); + $this->assertEquals(200, $image['headers']['status-code']); + $this->assertNotEmpty($image['body']); + $this->assertStringContainsString('image/', $image['headers']['content-type']); return $image['body']; } @@ -113,7 +121,9 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(5430, \strlen($favicon['body'])); + $this->assertEquals(200, $favicon['headers']['status-code']); + $this->assertNotEmpty($favicon['body']); + $this->assertStringContainsString('image/', $favicon['headers']['content-type']); return $favicon['body']; } @@ -134,7 +144,9 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(9169, \strlen($qrCode['body'])); + $this->assertEquals(200, $qrCode['headers']['status-code']); + $this->assertNotEmpty($qrCode['body']); + $this->assertStringContainsString('image/', $qrCode['headers']['content-type']); return $qrCode['body']; } @@ -155,7 +167,9 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(5025, \strlen($initials['body'])); + $this->assertEquals(200, $initials['headers']['status-code']); + $this->assertNotEmpty($initials['body']); + $this->assertStringContainsString('image/', $initials['headers']['content-type']); return $initials['body']; } diff --git a/tests/e2e/Services/Migrations/MigrationsBase.php b/tests/e2e/Services/Migrations/MigrationsBase.php index 1de1137861..1fa17ffa95 100644 --- a/tests/e2e/Services/Migrations/MigrationsBase.php +++ b/tests/e2e/Services/Migrations/MigrationsBase.php @@ -1054,10 +1054,10 @@ trait MigrationsBase $errorData = json_decode($errorJson, true); $this->assertThat( - $errorData['message'], - $this->stringContains("CSV header mismatch. Missing column: 'age'") + implode("\n", $migration['body']['errors']), + $this->stringContains("CSV header validation failed: Missing required column: 'age'") ); - }, 60000, 500); + }, 60_000, 500); // missing row data, fail in worker. $missingColumn = $this->performCsvMigration( @@ -1087,12 +1087,12 @@ trait MigrationsBase $errorData = json_decode($errorJson, true); $this->assertThat( - $errorData['message'], + implode("\n", $migration['body']['errors']), $this->stringContains('CSV row does not match the number of header columns') ); - }, 60000, 500); + }, 60_000, 500); - // irrelevant column - email, fail in worker. + // irrelevant column - email, success. $irrelevantColumn = $this->performCsvMigration( [ 'fileId' => $fileIds['irrelevant-column'], @@ -1110,20 +1110,13 @@ trait MigrationsBase $this->assertEquals(200, $migration['headers']['status-code']); $this->assertEquals('finished', $migration['body']['stage']); - $this->assertEquals('failed', $migration['body']['status']); + $this->assertEquals('completed', $migration['body']['status']); $this->assertEquals('CSV', $migration['body']['source']); $this->assertEquals('Appwrite', $migration['body']['destination']); $this->assertContains(Resource::TYPE_ROW, $migration['body']['resources']); - $this->assertEmpty($migration['body']['statusCounters']); - - $errorJson = $migration['body']['errors'][0]; - $errorData = json_decode($errorJson, true); - - $this->assertThat( - $errorData['message'], - $this->stringContains("CSV header mismatch. Unexpected column: 'email'") - ); - }, 60000, 500); + $this->assertArrayHasKey(Resource::TYPE_ROW, $migration['body']['statusCounters']); + $this->assertEquals(100, $migration['body']['statusCounters'][Resource::TYPE_ROW]['success']); + }, 10_000, 500); // all data exists, pass. $migration = $this->performCsvMigration( @@ -1165,7 +1158,7 @@ trait MigrationsBase $this->assertEquals(200, $rows['headers']['status-code']); $this->assertIsArray($rows['body']['rows']); $this->assertIsNumeric($rows['body']['total']); - $this->assertEquals(100, $rows['body']['total']); + $this->assertEquals(200, $rows['body']['total']); // all data exists and includes internals, pass. $migration = $this->performCsvMigration( diff --git a/tests/resources/csv/documents-internals.csv b/tests/resources/csv/documents-internals.csv index 22144ab0b8..ab8e192a0e 100644 --- a/tests/resources/csv/documents-internals.csv +++ b/tests/resources/csv/documents-internals.csv @@ -1,26 +1,26 @@ $id,$createdAt,$updatedAt,$permissions,name,age -z1y2x3w4v5u6t7s8,2022-10-23T10:33:01+00:00,2023-03-15T12:00:41+00:00,"read(\"any\"),update(\"user:123\")",Diamond Mendez,56 -r9q0p1o2n3m4l5k6,2021-08-11T21:05:13+00:00,2024-01-02T08:45:22+00:00,"read(\"any\"),update(\"user:456\")",Michael Huff,20 -j7i8h9g0f1e2d3c4,2020-05-29T14:22:56+00:00,2022-11-30T18:19:33+00:00,"read(\"any\")",Alyssa Rodriguez,37 -b5a6z7y8x9w0v1u2,2023-01-18T03:44:09+00:00,2023-09-07T23:50:17+00:00,"read(\"any\")",Barbara Smith,26 -t3s4r5q6p7o8n9m0,2020-11-02T09:12:45+00:00,2021-07-21T15:30:55+00:00,"read(\"any\")",Evelyn Edwards,54 -l1k2j3i4h5g6f7e8,2022-03-19T19:55:27+00:00,2024-05-14T06:28:11+00:00,"read(\"any\")",Tina Richardson,41 -d9c0b1a2z3y4x5w6,2021-04-07T01:18:34+00:00,2023-06-25T11:47:04+00:00,"read(\"any\")",Joel Hernandez,49 -v7u8t9s0r1q2p3o4,2023-08-22T16:40:18+00:00,2024-02-19T04:09:58+00:00,"read(\"any\")",Zachary Cooper,59 -n5m6l7k8j9i0h1g2,2020-02-12T07:59:01+00:00,2022-09-08T13:21:49+00:00,"read(\"any\")",Brittany Spears,20 -f3e4d5c6b7a8z9y0,2021-12-05T22:33:12+00:00,2023-11-11T02:55:37+00:00,"read(\"any\")",Holly White,47 -x1w2v3u4t5s6r7q8,2022-07-14T05:01:50+00:00,2024-04-01T20:10:26+00:00,"read(\"any\")",Kimberly Barnes,27 -p9o0n1m2l3k4j5i6,2020-09-28T11:27:36+00:00,2021-10-17T09:38:08+00:00,"read(\"any\")",Stephen Miller,53 -h7g8f9e0d1c2b3a4,2023-04-04T08:15:59+00:00,2024-06-29T17:03:14+00:00,"read(\"any\")",Yvonne Newman,41 -y5x6w7v8u9t0s1r2,2021-01-25T18:09:21+00:00,2022-08-16T22:44:51+00:00,"read(\"any\")",Carol Kane,38 -q3p4o5n6m7l8k9j0,2022-06-09T12:53:47+00:00,2023-12-24T01:16:05+00:00,"read(\"any\")",Doris Foster,44 -i1h2g3f4e5d6c7b8,2020-07-03T23:37:02+00:00,2021-05-09T05:52:43+00:00,"read(\"any\")",Joseph Stokes,28 -a9z0y1x2w3v4u5t6,2023-10-10T02:20:15+00:00,2024-03-28T14:33:29+00:00,"read(\"any\")",Steve Williams,31 -s7r8q9p0o1n2m3l4,2021-06-16T13:48:53+00:00,2022-04-22T07:07:19+00:00,"read(\"any\")",James Carey,29 -k5j6i7h8g9f0e1d2,2022-12-27T20:06:38+00:00,2023-08-03T10:25:57+00:00,"read(\"any\")",Kathryn Henry,38 -c3b4a5z6y7x8w9v0,2020-04-20T04:41:24+00:00,2021-02-13T19:14:06+00:00,"read(\"any\")",Christopher Landry,23 -u1t2s3r4q5p6o7n8,2023-05-08T00:58:10+00:00,2024-07-05T03:36:48+00:00,"read(\"any\")",Jennifer Mcgee,62 -m9l0k1j2i3h4g5f6,2021-09-01T06:11:42+00:00,2022-01-26T16:59:23+00:00,"read(\"any\")",Cathy Church,35 -e7d8c9b0a1z2y3x4,2022-02-18T15:24:07+00:00,2023-04-12T00:40:31+00:00,"read(\"any\")",Jose Lopez,41 -w5v6u7t8s9r0q1p2,2020-12-13T09:03:55+00:00,2021-11-06T11:23:16+00:00,"read(\"any\")",William Rose,30 -o3n4m5l6k7j8i9h0,2021-12-13T09:03:55+00:00,2022-11-06T11:23:16+00:00,"read(\"any\")",Charles Hammer,61 +z1y2x3w4v5u6t7s8,2022-10-23T10:33:01+00:00,2023-03-15T12:00:41+00:00,"read(""any""),update(""user:123"")",Diamond Mendez,56 +r9q0p1o2n3m4l5k6,2021-08-11T21:05:13+00:00,2024-01-02T08:45:22+00:00,"read(""any""),update(""user:456"")",Michael Huff,20 +j7i8h9g0f1e2d3c4,2020-05-29T14:22:56+00:00,2022-11-30T18:19:33+00:00,"read(""any"")",Alyssa Rodriguez,37 +b5a6z7y8x9w0v1u2,2023-01-18T03:44:09+00:00,2023-09-07T23:50:17+00:00,"read(""any"")",Barbara Smith,26 +t3s4r5q6p7o8n9m0,2020-11-02T09:12:45+00:00,2021-07-21T15:30:55+00:00,"read(""any"")",Evelyn Edwards,54 +l1k2j3i4h5g6f7e8,2022-03-19T19:55:27+00:00,2024-05-14T06:28:11+00:00,"read(""any"")",Tina Richardson,41 +d9c0b1a2z3y4x5w6,2021-04-07T01:18:34+00:00,2023-06-25T11:47:04+00:00,"read(""any"")",Joel Hernandez,49 +v7u8t9s0r1q2p3o4,2023-08-22T16:40:18+00:00,2024-02-19T04:09:58+00:00,"read(""any"")",Zachary Cooper,59 +n5m6l7k8j9i0h1g2,2020-02-12T07:59:01+00:00,2022-09-08T13:21:49+00:00,"read(""any"")",Brittany Spears,20 +f3e4d5c6b7a8z9y0,2021-12-05T22:33:12+00:00,2023-11-11T02:55:37+00:00,"read(""any"")",Holly White,47 +x1w2v3u4t5s6r7q8,2022-07-14T05:01:50+00:00,2024-04-01T20:10:26+00:00,"read(""any"")",Kimberly Barnes,27 +p9o0n1m2l3k4j5i6,2020-09-28T11:27:36+00:00,2021-10-17T09:38:08+00:00,"read(""any"")",Stephen Miller,53 +h7g8f9e0d1c2b3a4,2023-04-04T08:15:59+00:00,2024-06-29T17:03:14+00:00,"read(""any"")",Yvonne Newman,41 +y5x6w7v8u9t0s1r2,2021-01-25T18:09:21+00:00,2022-08-16T22:44:51+00:00,"read(""any"")",Carol Kane,38 +q3p4o5n6m7l8k9j0,2022-06-09T12:53:47+00:00,2023-12-24T01:16:05+00:00,"read(""any"")",Doris Foster,44 +i1h2g3f4e5d6c7b8,2020-07-03T23:37:02+00:00,2021-05-09T05:52:43+00:00,"read(""any"")",Joseph Stokes,28 +a9z0y1x2w3v4u5t6,2023-10-10T02:20:15+00:00,2024-03-28T14:33:29+00:00,"read(""any"")",Steve Williams,31 +s7r8q9p0o1n2m3l4,2021-06-16T13:48:53+00:00,2022-04-22T07:07:19+00:00,"read(""any"")",James Carey,29 +k5j6i7h8g9f0e1d2,2022-12-27T20:06:38+00:00,2023-08-03T10:25:57+00:00,"read(""any"")",Kathryn Henry,38 +c3b4a5z6y7x8w9v0,2020-04-20T04:41:24+00:00,2021-02-13T19:14:06+00:00,"read(""any"")",Christopher Landry,23 +u1t2s3r4q5p6o7n8,2023-05-08T00:58:10+00:00,2024-07-05T03:36:48+00:00,"read(""any"")",Jennifer Mcgee,62 +m9l0k1j2i3h4g5f6,2021-09-01T06:11:42+00:00,2022-01-26T16:59:23+00:00,"read(""any"")",Cathy Church,35 +e7d8c9b0a1z2y3x4,2022-02-18T15:24:07+00:00,2023-04-12T00:40:31+00:00,"read(""any"")",Jose Lopez,41 +w5v6u7t8s9r0q1p2,2020-12-13T09:03:55+00:00,2021-11-06T11:23:16+00:00,"read(""any"")",William Rose,30 +o3n4m5l6k7j8i9h0,2021-12-13T09:03:55+00:00,2022-11-06T11:23:16+00:00,"read(""any"")",Charles Hammer,61 diff --git a/tests/resources/csv/irrelevant-column.csv b/tests/resources/csv/irrelevant-column.csv index 92105ceaa2..7b690ab71a 100644 --- a/tests/resources/csv/irrelevant-column.csv +++ b/tests/resources/csv/irrelevant-column.csv @@ -1,101 +1,101 @@ -$id,name,age,email -hxfcwpcas5xokpwe,Diamond Mendez,56,diamond.mendez@example.com -gw8nxwf6esn3tfwf,Michael Huff,20,michael.huff@example.com -xb6bxg56lral1qy9,Alyssa Rodriguez,37,alyssa.rodriguez@example.com -imerjq5j36y3agh2,Barbara Smith,26,barbara.smith@example.com -07yq9qdlhmbzmr35,Evelyn Edwards,54,evelyn.edwards@example.com -ksqo631sbhwj5ltg,Tina Richardson,41,tina.richardson@example.com -j7zlndgu0gbshp15,Joel Hernandez,49,joel.hernandez@example.com -mfntvnljrcmf7h6v,Zachary Cooper,59,zachary.cooper@example.com -5f9b01nziqu2h8ed,Brittany Spears,20,brittany.spears@example.com -4vxzbnzraqznk5u8,Holly White,47,holly.white@example.com -d4ywy3mtphaatbpf,Kimberly Barnes,27,kimberly.barnes@example.com -88odnk6nthyyvbal,Stephen Miller,53,stephen.miller@example.com -08oekee3fn7mzaa5,Yvonne Newman,41,yvonne.newman@example.com -quw55kn9895i5e4v,Carol Kane,38,carol.kane@example.com -nge6bm8ykripei6f,Doris Foster,44,doris.foster@example.com -4k16i33s0xl2ypx9,Joseph Stokes,28,joseph.stokes@example.com -q0j5rxbgid66snyf,Steve Williams,31,steve.williams@example.com -n1oxun7mqq3p103y,James Carey,29,james.carey@example.com -0dbvs840jkf8i0ye,Kathryn Henry,38,kathryn.henry@example.com -5sfaidgs1h87v15v,Christopher Landry,23,christopher.landry@example.com -vg3punvfu5khmf41,Jennifer Mcgee,62,jennifer.mcgee@example.com -f933qydr9u5b2r11,Cathy Church,35,cathy.church@example.com -wjv87y1inf8yk32s,Jose Lopez,41,jose.lopez@example.com -uljysdvdlcyrbrwk,William Rose,30,william.rose@example.com -ot8xtzh77j55wq0s,Sarah Ford,26,sarah.ford@example.com -9t76vnsv2u36s43t,Alisha Jones,61,alisha.jones@example.com -66y4tnty62hw8c02,Kristin Kelly,61,kristin.kelly@example.com -2punfblazi5v16ar,Brendan Stout,40,brendan.stout@example.com -sxhr4nf5w2gx4wbg,Kelly Cruz,18,kelly.cruz@example.com -68dvrqfwqnkq5el9,Samantha Martin,50,samantha.martin@example.com -20192l6dbeinhkh0,David Santos,46,david.santos@example.com -si0l4dgay09ebfmf,Elizabeth Carroll,22,elizabeth.carroll@example.com -lhse40vbldqb6ap1,Corey Owens,46,corey.owens@example.com -h5t3pslykyx3kxfm,Shelby Mueller,65,shelby.mueller@example.com -ldc0luydrw6jub0f,Dr. Sylvia Myers,29,sylvia.myers@example.com -voc9628xg4dsgw2y,Scott Freeman,48,scott.freeman@example.com -o4y0gk3gqv1ax2fz,Christopher Atkinson,21,christopher.atkinson@example.com -u1n3x4e4u7e0vzj6,Sean Diaz,31,sean.diaz@example.com -s36eskwtm0w7lwr7,Bobby Dyer,57,bobby.dyer@example.com -4hjnag1p5iwvtixd,Daniel Hall,62,daniel.hall@example.com -m91d80oxsa216zbh,Jennifer Ramirez,65,jennifer.ramirez@example.com -5hj6858zo2g85n6v,Angela Jackson,57,angela.jackson@example.com -8m8oihv9a1e7nn92,Kelly Lewis,36,kelly.lewis@example.com -7azy39la0no0mxi7,Jessica Munoz,55,jessica.munoz@example.com -47pmjkhnnqhyit8c,Kelly George,65,kelly.george@example.com -6j6cpy4kgneg1mmh,Anthony Johnson,65,anthony.johnson@example.com -tnlmtvap1zz89km9,Regina Fields,61,regina.fields@example.com -6cyuvnwwqdmrpfzh,Sharon Schaefer,30,sharon.schaefer@example.com -p1v4pyu2pqodc0ey,Jacob French,62,jacob.french@example.com -6npynnhjt2jd05xo,Jessica Costa,23,jessica.costa@example.com -wcxedf13n2e9qi4l,George Hardy,53,george.hardy@example.com -yf2xlcmszk2tqeig,Andrea Allison,20,andrea.allison@example.com -3bf2zzv7poststwa,Kevin Ferguson,32,kevin.ferguson@example.com -c2iataz0hhv39q63,Joseph Johnson,58,joseph.johnson@example.com -3e8npxhov4a39pvq,Ashley Martinez,18,ashley.martinez@example.com -t7dp41tysipytywq,Charles Nixon,23,charles.nixon@example.com -z8cztq7c47phyfhk,Carol Dudley,40,carol.dudley@example.com -2636f9d8r4ipm3h6,David Weber,51,david.weber@example.com -eh3f6wxtvkjq6ykq,Scott Robinson,32,scott.robinson@example.com -raskbwpsje69a59h,Anthony Hardy,38,anthony.hardy@example.com -90hn1p0b4cs9e2og,Mackenzie Owens,52,mackenzie.owens@example.com -am3swwfbo076x0v1,Brian Foster,27,brian.foster@example.com -5uw7utb9lq5cfncw,Hannah Forbes,56,hannah.forbes@example.com -cs6mbfzkzifefx6r,Lauren Reed,26,lauren.reed@example.com -ftw3uvztziiz9x00,Morgan Smith,28,morgan.smith@example.com -uhrqseeo43mozpaq,Samantha Alexander,65,samantha.alexander@example.com -pvvmzyfc1lxor11e,Tiffany Roberts,20,tiffany.roberts@example.com -jia7bdag4abz123s,Emily Hayes,34,emily.hayes@example.com -h6oozcngbz8o5x4y,Rebecca Villegas,52,rebecca.villegas@example.com -9v6z1pn2f9twcy12,Donald Shah,61,donald.shah@example.com -wzz3jduioso77o7f,Denise Cain,59,denise.cain@example.com -u51plhgvjodkswnr,Kristine Ramirez,53,kristine.ramirez@example.com -t1uhkmiytfyc13vc,Stacey Adkins,61,stacey.adkins@example.com -iqaqnf0ybg2ct507,Daniel Hunt,20,daniel.hunt@example.com -idwrwv2uu4hcpv2i,Roberta Johnson,48,roberta.johnson@example.com -2yd2hd6auetjacyo,Jason Williamson,39,jason.williamson@example.com -egrmdbibnjhi914x,Sandra Robinson,50,sandra.robinson@example.com -15m1pz2bb0ercgyk,Steve Rice,25,steve.rice@example.com -0i21bhkxdagjurb7,Kimberly Fritz,53,kimberly.fritz@example.com -726ofi7h5snreq67,Brianna Reynolds,33,brianna.reynolds@example.com -csqxse3wym56eim6,Alexander Williams,50,alexander.williams@example.com -qeaoylnrsf8p3byg,Andrew Thomas,25,andrew.thomas@example.com -edsswobumzyzbvhf,Austin Williams,57,austin.williams@example.com -hdzhzpt0ahy5hkib,Nicholas Williams,24,nicholas.williams@example.com -w1qmvmg4roa8xnwu,Mrs. Michelle Cisneros,48,michelle.cisneros@example.com -3z3o73x7adyuo6w0,Stacey Smith,39,stacey.smith@example.com -sse2u5zlgoqrgmcf,Laura Beck,20,laura.beck@example.com -rvovijmvch58r4yx,Molly Clark,51,molly.clark@example.com -doe06nrx8sg5mcuv,Carmen Morris,41,carmen.morris@example.com -jbjdwuvj5s4kw04y,Amanda Munoz,20,amanda.munoz@example.com -6k2ewkla7js0yw23,Rachel Collins,44,rachel.collins@example.com -fcxuyr4kkhrnigu1,John Alexander,18,john.alexander@example.com -d25fuwlos5mk07o0,Stacy Hunter,22,stacy.hunter@example.com -1vdai2rxmwd57oet,Eric Massey,40,eric.massey@example.com -pq4jnt9izu1wlrzd,Scott Garcia,20,scott.garcia@example.com -lz9kfc0lty5xcz14,Cassandra Nelson,35,cassandra.nelson@example.com -pu7w6tyab5jd4we9,Aaron Johnson,50,aaron.johnson@example.com -8dupswd2kqwdyn8v,Shannon Sherman,45,shannon.sherman@example.com -ye466l71jthiz2p6,April Garcia,60,april.garcia@example.com -xogsmfwb73l16qdt,Evan Lynn,20,evan.lynn@example.com +$id,name,age,email +r5ctmrqwqn1m3rc0,Diamond Mendez,56,diamond.mendez@example.com +wxwp7e7q7nx3ltfx,Michael Huff,20,michael.huff@example.com +4ct0b38fwaojawlv,Alyssa Rodriguez,37,alyssa.rodriguez@example.com +o0jjcuygbta2zvga,Barbara Smith,26,barbara.smith@example.com +bdy6l2ofl8klb4pb,Evelyn Edwards,54,evelyn.edwards@example.com +rkccl72v7zwtbila,Tina Richardson,41,tina.richardson@example.com +cilw7um0cd927esj,Joel Hernandez,49,joel.hernandez@example.com +60povvz0votkve1j,Zachary Cooper,59,zachary.cooper@example.com +ayow5dzwktvbbtp2,Brittany Spears,20,brittany.spears@example.com +cfru98od0lab0b2n,Holly White,47,holly.white@example.com +vjxjldvu3r6uylq6,Kimberly Barnes,27,kimberly.barnes@example.com +d1p47hl97pw6xowb,Stephen Miller,53,stephen.miller@example.com +yxk6qaa5ryb3gqrb,Yvonne Newman,41,yvonne.newman@example.com +ifkeo7j8t7hfd7z8,Carol Kane,38,carol.kane@example.com +e1q4lq8vvpxp9ysb,Doris Foster,44,doris.foster@example.com +obec6d52dc6swzsf,Joseph Stokes,28,joseph.stokes@example.com +26v06la6ug8wkvim,Steve Williams,31,steve.williams@example.com +m4glre4ch12vkxp6,James Carey,29,james.carey@example.com +jee8fyfffnjugsd5,Kathryn Henry,38,kathryn.henry@example.com +miquc6ljb9l3a31r,Christopher Landry,23,christopher.landry@example.com +ghf7da7seeuj1zdl,Jennifer Mcgee,62,jennifer.mcgee@example.com +x7h11phjrz77w0q8,Cathy Church,35,cathy.church@example.com +dn8u5lsux4708z6j,Jose Lopez,41,jose.lopez@example.com +zb7fdlyohuyy5i9k,William Rose,30,william.rose@example.com +qyrj8m9krp4dt4wt,Sarah Ford,26,sarah.ford@example.com +t6t673zpfyhhz8pg,Alisha Jones,61,alisha.jones@example.com +0hfbo0iy1q9bwc2n,Kristin Kelly,61,kristin.kelly@example.com +8alv4e4xrpcj443z,Brendan Stout,40,brendan.stout@example.com +qxm7a0z32xdkzxdj,Kelly Cruz,18,kelly.cruz@example.com +885mti7j7oiz5p5g,Samantha Martin,50,samantha.martin@example.com +v8i7dvhby6711m66,David Santos,46,david.santos@example.com +rggc0ow8ccd2jgvp,Elizabeth Carroll,22,elizabeth.carroll@example.com +012472s64rvzq1c4,Corey Owens,46,corey.owens@example.com +0k2xrwj4g33ut14y,Shelby Mueller,65,shelby.mueller@example.com +s3y9rl4uzf3difiq,Dr. Sylvia Myers,29,sylvia.myers@example.com +ntpc2td892t7f6an,Scott Freeman,48,scott.freeman@example.com +7f703gibyr5ijdmt,Christopher Atkinson,21,christopher.atkinson@example.com +r2jdf2pivkxmqd0l,Sean Diaz,31,sean.diaz@example.com +fj98fji1lrxeigs9,Bobby Dyer,57,bobby.dyer@example.com +mehqmzp9u7xv1z3j,Daniel Hall,62,daniel.hall@example.com +4cd5ln65qjfv3h4j,Jennifer Ramirez,65,jennifer.ramirez@example.com +wdi6ap0oa7m1ab1d,Angela Jackson,57,angela.jackson@example.com +l2foqjhxvjhjzijb,Kelly Lewis,36,kelly.lewis@example.com +d963t5yu35uagwm4,Jessica Munoz,55,jessica.munoz@example.com +99ez9uxsim8zp64m,Kelly George,65,kelly.george@example.com +v7wl221gycftl63d,Anthony Johnson,65,anthony.johnson@example.com +p2zzj0lnmjvqzfc3,Regina Fields,61,regina.fields@example.com +fk655e243z2ivvx6,Sharon Schaefer,30,sharon.schaefer@example.com +4ywsv6fw8g2d8ncw,Jacob French,62,jacob.french@example.com +y61q9k6g4h0fxxz4,Jessica Costa,23,jessica.costa@example.com +knj4hfzsthk7vx5n,George Hardy,53,george.hardy@example.com +a88u9w2pct2nn8l6,Andrea Allison,20,andrea.allison@example.com +hw960v1ybycrwr5o,Kevin Ferguson,32,kevin.ferguson@example.com +j9garslpgx6jgzgb,Joseph Johnson,58,joseph.johnson@example.com +gv101bz36elm84cd,Ashley Martinez,18,ashley.martinez@example.com +xrvzgt3gc0c7g4cl,Charles Nixon,23,charles.nixon@example.com +awjlu7uk0eutcfpb,Carol Dudley,40,carol.dudley@example.com +95oi26p2zdudpime,David Weber,51,david.weber@example.com +h8x7pkhdvu5bcp89,Scott Robinson,32,scott.robinson@example.com +oj6cu4jm1z2afe7s,Anthony Hardy,38,anthony.hardy@example.com +hgsdi1g30poqqmf0,Mackenzie Owens,52,mackenzie.owens@example.com +8fzdz914bqlqk2tc,Brian Foster,27,brian.foster@example.com +fwlqoeiunjhczpl0,Hannah Forbes,56,hannah.forbes@example.com +rsv8156goe8z4j6j,Lauren Reed,26,lauren.reed@example.com +1fjqv3w7uwbswe2p,Morgan Smith,28,morgan.smith@example.com +soqrzmhhg05hhzn4,Samantha Alexander,65,samantha.alexander@example.com +8quy52cto9kjjokp,Tiffany Roberts,20,tiffany.roberts@example.com +e3i1g1lw04v7jd89,Emily Hayes,34,emily.hayes@example.com +s7n8lzb0sw7h93z1,Rebecca Villegas,52,rebecca.villegas@example.com +e2lc7i81tpkqs1rp,Donald Shah,61,donald.shah@example.com +3oe2mysup1xluiw0,Denise Cain,59,denise.cain@example.com +1vqypc37f85nuqz4,Kristine Ramirez,53,kristine.ramirez@example.com +m0uh7r3dc6z8ucb4,Stacey Adkins,61,stacey.adkins@example.com +jdofz6x1ahganmqf,Daniel Hunt,20,daniel.hunt@example.com +vbe903c2q4m4q97g,Roberta Johnson,48,roberta.johnson@example.com +sndngrxuwpd93pdb,Jason Williamson,39,jason.williamson@example.com +66hvaw2p5xwf07p8,Sandra Robinson,50,sandra.robinson@example.com +9pvingfsl8cmag5c,Steve Rice,25,steve.rice@example.com +qe154m5hh00u4iiz,Kimberly Fritz,53,kimberly.fritz@example.com +avqnbrco2f0tfupk,Brianna Reynolds,33,brianna.reynolds@example.com +cqs10gi2qu1r3ugb,Alexander Williams,50,alexander.williams@example.com +jrpmfi6hmm7pmegp,Andrew Thomas,25,andrew.thomas@example.com +heeab2qqf0zm446f,Austin Williams,57,austin.williams@example.com +bkhugvnil7kjchm6,Nicholas Williams,24,nicholas.williams@example.com +b045j302pvv8l1p4,Mrs. Michelle Cisneros,48,michelle.cisneros@example.com +aikhii5q210lrfpr,Stacey Smith,39,stacey.smith@example.com +x0zajitea1z2dfo0,Laura Beck,20,laura.beck@example.com +abeecki7mdff1tv0,Molly Clark,51,molly.clark@example.com +yizama8r3i1to548,Carmen Morris,41,carmen.morris@example.com +8690yh971g4rgspj,Amanda Munoz,20,amanda.munoz@example.com +cd9vk5v97t359ul2,Rachel Collins,44,rachel.collins@example.com +wrkgmx1v0w9ja4l8,John Alexander,18,john.alexander@example.com +kxp3ucqo6ped4ss7,Stacy Hunter,22,stacy.hunter@example.com +dbvv8okae2qgo0gm,Eric Massey,40,eric.massey@example.com +9tn3nm6ppnayisje,Scott Garcia,20,scott.garcia@example.com +1xuc5t60xpcvd4qi,Cassandra Nelson,35,cassandra.nelson@example.com +qao1nulwn0kqyfkc,Aaron Johnson,50,aaron.johnson@example.com +kd2q6owvuwsy5knx,Shannon Sherman,45,shannon.sherman@example.com +wsl37kjo0bib4wrc,April Garcia,60,april.garcia@example.com +ujlz7k84xzfx4khs,Evan Lynn,20,evan.lynn@example.com diff --git a/tests/unit/Network/Validators/DNSTest.php b/tests/unit/Network/Validators/DNSTest.php index 5e8652381a..c3e819e7dc 100644 --- a/tests/unit/Network/Validators/DNSTest.php +++ b/tests/unit/Network/Validators/DNSTest.php @@ -3,8 +3,18 @@ namespace Tests\Unit\Network\Validators; use Appwrite\Network\Validator\DNS; +use Appwrite\Tests\Retry; use PHPUnit\Framework\TestCase; +/* +DNS Setup (on Appwrite Labs digital ocean team, network tab): + +certainly.caa.appwrite.org: CAA 0 issue "certainly.com" +certainly-full.caa.appwrite.org: CAA 128 issuewild "certainly.com;account=123456;validationmethods=dns-01" +letsencrypt.certainly.caa.appwrite.org: CAA 0 issue "letsencrypt.org" + +*/ + class DNSTest extends TestCase { public function setUp(): void @@ -47,4 +57,52 @@ class DNSTest extends TestCase $this->assertEquals($validator->isValid('aaaa-unit-test.appwrite.org'), true); $this->assertEquals($validator->isValid('test1.appwrite.org'), false); } + + #[Retry(count: 5)] + public function testCAA(): void + { + $certainly = new DNS('certainly.com', DNS::RECORD_CAA, 'ns1.digitalocean.com'); + $letsencrypt = new DNS('letsencrypt.org', DNS::RECORD_CAA, 'ns1.digitalocean.com'); + + // No CAA record succeeds on main domain & subdomains for any issuer + $this->assertEquals($certainly->isValid('caa.appwrite.org'), true); + $this->assertEquals($certainly->isValid('sub.caa.appwrite.org'), true); + $this->assertEquals($certainly->isValid('sub.sub.caa.appwrite.org'), true); + + $this->assertEquals($letsencrypt->isValid('caa.appwrite.org'), true); + $this->assertEquals($letsencrypt->isValid('sub.caa.appwrite.org'), true); + $this->assertEquals($letsencrypt->isValid('sub.sub.caa.appwrite.org'), true); + + // Custom flags and tag is allowed, but only for Certainly + $this->assertEquals($certainly->isValid('certainly-full.caa.appwrite.org'), true); + $this->assertEquals($letsencrypt->isValid('certainly-full.caa.appwrite.org'), false); + + // Custom flags&tag are not allowed if validator includes specific flags&tag + $certainlyFull = new DNS('0 issue "certainly.com"', DNS::RECORD_CAA); + $this->assertEquals($certainlyFull->isValid('certainly-full.caa.appwrite.org'), false); + + // Custom flags&tag still allows if they match exactly + $certainlyFull = new DNS('128 issuewild "certainly.com;account=123456;validationmethods=dns-01"', DNS::RECORD_CAA); + $this->assertEquals($certainlyFull->isValid('certainly-full.caa.appwrite.org'), true); + + // Certainly CAA allows Certainly, but not LetsEncrypt; Same for subdomains + $this->assertEquals($certainly->isValid('certainly.caa.appwrite.org'), true); + $this->assertEquals($letsencrypt->isValid('certainly.caa.appwrite.org'), false); + + $this->assertEquals($certainly->isValid('sub.certainly.caa.appwrite.org'), true); + $this->assertEquals($letsencrypt->isValid('sub.certainly.caa.appwrite.org'), false); + + $this->assertEquals($certainly->isValid('sub.sub.certainly.caa.appwrite.org'), true); + $this->assertEquals($letsencrypt->isValid('sub.sub.certainly.caa.appwrite.org'), false); + + // LetsEncrypt CAA on subdomain with parent allowing Certainly. Only LetsEncrypt is allowed; Same for subdomains + $this->assertEquals($certainly->isValid('letsencrypt.certainly.caa.appwrite.org'), false); + $this->assertEquals($letsencrypt->isValid('letsencrypt.certainly.caa.appwrite.org'), true); + + $this->assertEquals($certainly->isValid('sub.letsencrypt.certainly.caa.appwrite.org'), false); + $this->assertEquals($letsencrypt->isValid('sub.letsencrypt.certainly.caa.appwrite.org'), true); + + $this->assertEquals($certainly->isValid('sub.sub.letsencrypt.certainly.caa.appwrite.org'), false); + $this->assertEquals($letsencrypt->isValid('sub.sub.letsencrypt.certainly.caa.appwrite.org'), true); + } }