mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge branch '1.8.x' of https://github.com/appwrite/appwrite into get-sequence-null
# Conflicts: # composer.json # composer.lock
This commit is contained in:
@@ -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
|
||||
|
||||
+8
-8
@@ -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 && \
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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.',
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.*",
|
||||
|
||||
Generated
+92
-81
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -15,7 +15,7 @@ functions.createExecution(
|
||||
"<PATH>", // path (optional)
|
||||
ExecutionMethod.GET, // method (optional)
|
||||
mapOf( "a" to "b" ), // headers (optional)
|
||||
"", // scheduledAt (optional)
|
||||
"<SCHEDULED_AT>", // scheduledAt (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
|
||||
@@ -15,5 +15,5 @@ val result = functions.createExecution(
|
||||
path = "<PATH>", // (optional)
|
||||
method = ExecutionMethod.GET, // (optional)
|
||||
headers = mapOf( "a" to "b" ), // (optional)
|
||||
scheduledAt = "", // (optional)
|
||||
scheduledAt = "<SCHEDULED_AT>", // (optional)
|
||||
)
|
||||
@@ -16,7 +16,7 @@ functions.createExecution(
|
||||
"<PATH>", // path (optional)
|
||||
ExecutionMethod.GET, // method (optional)
|
||||
mapOf( "a" to "b" ), // headers (optional)
|
||||
"", // scheduledAt (optional)
|
||||
"<SCHEDULED_AT>", // scheduledAt (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
|
||||
@@ -16,5 +16,5 @@ val response = functions.createExecution(
|
||||
path = "<PATH>", // optional
|
||||
method = "GET", // optional
|
||||
headers = mapOf( "a" to "b" ), // optional
|
||||
scheduledAt = "" // optional
|
||||
scheduledAt = "<SCHEDULED_AT>" // optional
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-2
@@ -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 = [];
|
||||
|
||||
@@ -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 = [];
|
||||
|
||||
+1
-1
@@ -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(),
|
||||
|
||||
+1
-1
@@ -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(),
|
||||
|
||||
+1
-1
@@ -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(),
|
||||
|
||||
+1
-1
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
+1
-1
@@ -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(),
|
||||
|
||||
+1
-1
@@ -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(),
|
||||
|
||||
+1
-1
@@ -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(),
|
||||
|
||||
+1
-1
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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.',
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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']);
|
||||
|
||||
@@ -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']);
|
||||
|
||||
@@ -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'];
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 77.
|
@@ -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
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user