diff --git a/.env b/.env index aa8e5254ec..1ca5d5fc4e 100644 --- a/.env +++ b/.env @@ -26,7 +26,7 @@ _APP_DNS=172.16.238.100 # CoreDNS _APP_DOMAIN=appwrite.test _APP_CONSOLE_DOMAIN=localhost _APP_DOMAIN_FUNCTIONS=functions.localhost -_APP_DOMAIN_SITES=sites.localhost +_APP_DOMAIN_SITES=sites.localhost,rebranded.localhost _APP_DOMAIN_TARGET_CNAME=cname.localhost _APP_DOMAIN_TARGET_A=203.0.0.1 _APP_DOMAIN_TARGET_AAAA=::1 diff --git a/app/config/platform.php b/app/config/platform.php index 913390ae5e..4030949ce6 100644 --- a/app/config/platform.php +++ b/app/config/platform.php @@ -2,6 +2,17 @@ use Utopia\System\System; +// For now, take first domain as primary (for previews) +// Later-on this can become platform-specific with new env var (appwrite=this,imagine=that) +$sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); +if (\str_contains($sitesDomain, ',')) { + $sitesDomain = explode(',', $sitesDomain)[0]; +} +$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); +if (\str_contains($functionsDomain, ',')) { + $functionsDomain = explode(',', $functionsDomain)[0]; +} + /** * Platform configuration */ @@ -23,5 +34,6 @@ return [ 'privacyUrl' => APP_EMAIL_PRIVACY_URL, 'websiteUrl' => 'https://' . APP_DOMAIN, 'emailSenderName' => APP_EMAIL_PLATFORM_NAME, - 'sitePreviewDomain' => System::getEnv('_APP_DOMAIN_SITES', ''), + 'sitesDomain' => $sitesDomain, + 'functionsDomain' => $functionsDomain, ]; diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 3a67068f1c..046f10f715 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -341,7 +341,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $projectId = $project->getId(); // Deployment preview - $sitesDomain = $platform['sitePreviewDomain']; + $sitesDomain = $platform['sitesDomain']; $domain = ID::unique() . "." . $sitesDomain; $ruleId = md5($domain); $previewRuleId = $ruleId; diff --git a/app/controllers/general.php b/app/controllers/general.php index 40bcdf4ea3..3ab59eb870 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -85,22 +85,32 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw $platformHostnames = $platform['hostnames'] ?? []; if ($rule->isEmpty()) { - $appDomainFunctionsFallback = System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', ''); - $appDomainFunctions = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - $appDomainSites = System::getEnv('_APP_DOMAIN_SITES', ''); - if (!empty($appDomainFunctionsFallback) && \str_ends_with($host, $appDomainFunctionsFallback)) { - $appDomainFunctions = $appDomainFunctionsFallback; + $denyDomains = []; + $denyEnvVars = [ + System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', ''), + System::getEnv('_APP_DOMAIN_FUNCTIONS', ''), + System::getEnv('_APP_DOMAIN_SITES', ''), + ]; + foreach ($denyEnvVars as $denyEnvVar) { + foreach (\explode(',', $denyEnvVar) as $denyDomain) { + if (empty($denyDomain)) { + continue; + } + $denyDomains[] = $denyDomain; + } } - if ($host === $appDomainFunctions || $host === $appDomainSites) { - throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.', view: $errorView); - } + foreach ($denyDomains as $denyDomain) { + if ($host === $denyDomain) { + throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.', view: $errorView); + } - if (\str_ends_with($host, $appDomainFunctions) || \str_ends_with($host, $appDomainSites)) { - $exception = new AppwriteException(AppwriteException::RULE_NOT_FOUND, 'This domain is not connected to any Appwrite resources. Visit domains tab under function/site settings to configure it.', view: $errorView); + if (\str_ends_with($host, $denyDomain)) { + $exception = new AppwriteException(AppwriteException::RULE_NOT_FOUND, 'This domain is not connected to any Appwrite resources. Visit domains tab under function/site settings to configure it.', view: $errorView); - $exception->addCTA('Start with this domain', $url . '/console'); - throw $exception; + $exception->addCTA('Start with this domain', $url . '/console'); + throw $exception; + } } if (!in_array($host, $platformHostnames)) { @@ -1094,19 +1104,28 @@ App::init() // 5. Create new rule $owner = ''; - $fallback = System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', ''); - $funcDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - $siteDomain = System::getEnv('_APP_DOMAIN_SITES', ''); - if (!empty($fallback) && \str_ends_with($domain->get(), $fallback)) { - $funcDomain = $fallback; + // Mark owner as Appwrite if its appwrite-owned domain + $appwriteDomains = []; + $appwriteDomainEnvs = [ + System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', ''), + System::getEnv('_APP_DOMAIN_FUNCTIONS', ''), + System::getEnv('_APP_DOMAIN_SITES', ''), + ]; + foreach ($appwriteDomainEnvs as $appwriteDomainEnv) { + foreach (\explode(',', $appwriteDomainEnv) as $appwriteDomain) { + if (empty($appwriteDomain)) { + continue; + } + $appwriteDomains[] = $appwriteDomain; + } } - if ( - (!empty($funcDomain) && \str_ends_with($domain->get(), $funcDomain)) || - (!empty($siteDomain) && \str_ends_with($domain->get(), $siteDomain)) - ) { - $owner = 'Appwrite'; + foreach ($appwriteDomains as $appwriteDomain) { + if (\str_ends_with($domain->get(), $appwriteDomain)) { + $owner = 'Appwrite'; + break; + } } $ruleId = $isMd5 ? md5($domain->get()) : ID::unique(); diff --git a/app/http.php b/app/http.php index 5d08c53eee..e23880e943 100644 --- a/app/http.php +++ b/app/http.php @@ -100,11 +100,19 @@ function dispatch(Server $server, int $fd, int $type, $data = null): int $risky = false; if (str_starts_with($request, 'POST') && str_contains($request, '/executions')) { $risky = true; - } elseif (str_ends_with($domain, System::getEnv('_APP_DOMAIN_FUNCTIONS'))) { - $risky = true; } elseif ($domains->get(md5($domain), 'value') === 1) { // executions request coming from custom domain $risky = true; + } else { + foreach (\explode(',', System::getEnv('_APP_DOMAIN_FUNCTIONS')) as $riskyDomain) { + if (empty($riskyDomain)) { + continue; + } + if (str_ends_with($domain, $riskyDomain)) { + $risky = true; + break; + } + } } if ($risky) { @@ -591,9 +599,33 @@ $http->on(Constant::EVENT_TASK, function () use ($register, $domains) { $sum = count($results); foreach ($results as $document) { $domain = $document->getAttribute('domain'); - if (str_ends_with($domain, System::getEnv('_APP_DOMAIN_FUNCTIONS')) || str_ends_with($domain, System::getEnv('_APP_DOMAIN_SITES'))) { + + $denyDomains = []; + $denyEnvVars = [ + System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', ''), + System::getEnv('_APP_DOMAIN_FUNCTIONS', ''), + System::getEnv('_APP_DOMAIN_SITES', ''), + ]; + foreach ($denyEnvVars as $denyEnvVar) { + foreach (\explode(',', $denyEnvVar) as $denyDomain) { + if (empty($denyDomain)) { + continue; + } + $denyDomains[] = $denyDomain; + } + } + + $isDenyDomain = false; + foreach ($denyDomains as $denyDomain) { + if (str_ends_with($domain, $denyDomain)) { + $isDenyDomain = true; + } + } + + if ($isDenyDomain) { continue; } + $domains->set(md5($domain), ['value' => 1]); } $latestDocument = !empty(array_key_last($results)) ? $results[array_key_last($results)] : null; diff --git a/src/Appwrite/Platform/Modules/Compute/Base.php b/src/Appwrite/Platform/Modules/Compute/Base.php index 7b844200cb..6f604bae1b 100644 --- a/src/Appwrite/Platform/Modules/Compute/Base.php +++ b/src/Appwrite/Platform/Modules/Compute/Base.php @@ -235,7 +235,7 @@ class Base extends Action ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('sites', $site->getId(), $site); - $sitesDomain = $platform['sitePreviewDomain']; + $sitesDomain = $platform['sitesDomain']; $domain = ID::unique() . "." . $sitesDomain; // TODO: (@Meldiron) Remove after 1.7.x migration diff --git a/src/Appwrite/Platform/Modules/Console/Http/Resources/Get.php b/src/Appwrite/Platform/Modules/Console/Http/Resources/Get.php index 1468bf71ac..717ef6c788 100644 --- a/src/Appwrite/Platform/Modules/Console/Http/Resources/Get.php +++ b/src/Appwrite/Platform/Modules/Console/Http/Resources/Get.php @@ -74,36 +74,41 @@ class Get extends Action ) { $domains = $platform['hostnames'] ?? []; if ($type === 'rules') { - $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); - $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - + $deniedDomains = [...$domains]; $restrictions = []; - if (!empty($sitesDomain)) { + + $sitesDomains = System::getEnv('_APP_DOMAIN_SITES', ''); + foreach (\explode(',', $sitesDomains) as $sitesDomain) { + if (empty($sitesDomain)) { + continue; + } + + $deniedDomains[] = $sitesDomain; + // Ensure site domains are exactly 1 subdomain, and dont start with reserved prefix $domainLevel = \count(\explode('.', $sitesDomain)); $restrictions[] = DomainValidator::createRestriction($sitesDomain, $domainLevel + 1, ['commit-', 'branch-']); } - if (!empty($functionsDomain)) { + + $functionsDomains = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); + foreach (\explode(',', $functionsDomains) as $functionsDomain) { + if (empty($functionsDomain)) { + continue; + } + + $deniedDomains[] = $functionsDomain; + // Ensure function domains are exactly 1 subdomain $domainLevel = \count(\explode('.', $functionsDomain)); $restrictions[] = DomainValidator::createRestriction($functionsDomain, $domainLevel + 1); } + $validator = new DomainValidator($restrictions); if (!$validator->isValid($value)) { throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'This domain name is not allowed. Please use a different domain.'); } - $deniedDomains = [...$domains]; - - if (!empty($sitesDomain)) { - $deniedDomains[] = $sitesDomain; - } - - if (!empty($functionsDomain)) { - $deniedDomains[] = $functionsDomain; - } - $denyListDomains = System::getEnv('_APP_CUSTOM_DOMAIN_DENY_LIST', ''); $denyListDomains = \array_map('trim', explode(',', $denyListDomains)); foreach ($denyListDomains as $denyListDomain) { diff --git a/src/Appwrite/Platform/Modules/Console/Http/Variables/Get.php b/src/Appwrite/Platform/Modules/Console/Http/Variables/Get.php index 03d692954f..af83a3efd3 100644 --- a/src/Appwrite/Platform/Modules/Console/Http/Variables/Get.php +++ b/src/Appwrite/Platform/Modules/Console/Http/Variables/Get.php @@ -46,10 +46,11 @@ class Get extends Action contentType: ContentType::JSON )) ->inject('response') + ->inject('platform') ->callback($this->action(...)); } - public function action(Response $response) + public function action(Response $response, array $platform) { $validator = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME')); $isCNAMEValid = !empty(System::getEnv('_APP_DOMAIN_TARGET_CNAME', '')) && $validator->isKnown() && !$validator->isTest(); @@ -82,8 +83,8 @@ class Get extends Action '_APP_VCS_ENABLED' => $isVcsEnabled, '_APP_DOMAIN_ENABLED' => $isDomainEnabled, '_APP_ASSISTANT_ENABLED' => $isAssistantEnabled, - '_APP_DOMAIN_SITES' => System::getEnv('_APP_DOMAIN_SITES'), - '_APP_DOMAIN_FUNCTIONS' => System::getEnv('_APP_DOMAIN_FUNCTIONS'), + '_APP_DOMAIN_SITES' => $platform['sitesDomain'], + '_APP_DOMAIN_FUNCTIONS' => $platform['functionsDomain'], '_APP_OPTIONS_FORCE_HTTPS' => System::getEnv('_APP_OPTIONS_FORCE_HTTPS'), '_APP_DOMAINS_NAMESERVERS' => System::getEnv('_APP_DOMAINS_NAMESERVERS'), ]); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php index 4bf072d115..581cfb5716 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php @@ -116,6 +116,7 @@ class Create extends Base ->inject('request') ->inject('gitHub') ->inject('authorization') + ->inject('platform') ->callback($this->action(...)); } @@ -154,7 +155,8 @@ class Create extends Base Database $dbForPlatform, Request $request, GitHub $github, - Authorization $authorization + Authorization $authorization, + array $platform ) { // Temporary abuse check @@ -321,7 +323,6 @@ class Create extends Base template: $template, github: $github, activate: true, - authorization: $authorization, reference: $providerBranch, referenceType: 'branch' ); @@ -366,7 +367,7 @@ class Create extends Base ->setTemplate($template); } - $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); + $functionsDomain = $platform['functionsDomain']; if (!empty($functionsDomain)) { $routeSubdomain = ID::unique(); $domain = "{$routeSubdomain}.{$functionsDomain}"; diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index 7473d0eb1f..1c70a1b4ef 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -1037,7 +1037,7 @@ class Builds extends Action // VCS branch $branchName = $deployment->getAttribute('providerBranch'); if (!empty($branchName)) { - $sitesDomain = $platform['sitePreviewDomain']; + $sitesDomain = $platform['sitesDomain']; $branchPrefix = substr($branchName, 0, 16); if (strlen($branchName) > 16) { $remainingChars = substr($branchName, 16); diff --git a/src/Appwrite/Platform/Modules/Proxy/Action.php b/src/Appwrite/Platform/Modules/Proxy/Action.php index c3fa535a5c..62d6efbfa3 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Action.php +++ b/src/Appwrite/Platform/Modules/Proxy/Action.php @@ -31,34 +31,42 @@ class Action extends PlatformAction protected function validateDomainRestrictions(string $domain, array $platform): void { $domains = $platform['hostnames'] ?? []; - $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); - $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); + $deniedDomains = [...$domains]; $restrictions = []; - if (!empty($sitesDomain)) { + + $sitesDomains = System::getEnv('_APP_DOMAIN_SITES', ''); + foreach (\explode(',', $sitesDomains) as $sitesDomain) { + if (empty($sitesDomain)) { + continue; + } + + $deniedDomains[] = $sitesDomain; + + // Ensure site domains are exactly 1 subdomain, and dont start with reserved prefix $domainLevel = \count(\explode('.', $sitesDomain)); $restrictions[] = ValidatorDomain::createRestriction($sitesDomain, $domainLevel + 1, ['commit-', 'branch-']); } - if (!empty($functionsDomain)) { + + $functionsDomains = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); + foreach (\explode(',', $functionsDomains) as $functionsDomain) { + if (empty($functionsDomains)) { + continue; + } + + $deniedDomains[] = $functionsDomain; + + // Ensure function domains are exactly 1 subdomain $domainLevel = \count(\explode('.', $functionsDomain)); $restrictions[] = ValidatorDomain::createRestriction($functionsDomain, $domainLevel + 1); } + $validator = new ValidatorDomain($restrictions); if (!$validator->isValid($domain)) { throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'This domain name is not allowed. Please use a different domain.'); } - $deniedDomains = [...$domains]; - - if (!empty($sitesDomain)) { - $deniedDomains[] = $sitesDomain; - } - - if (!empty($functionsDomain)) { - $deniedDomains[] = $functionsDomain; - } - $denyListDomains = System::getEnv('_APP_CUSTOM_DOMAIN_DENY_LIST', ''); $denyListDomains = \array_map('trim', explode(',', $denyListDomains)); foreach ($denyListDomains as $denyListDomain) { @@ -117,31 +125,51 @@ class Action extends PlatformAction } } - $targetCNAME = null; + $targetCNAMEs = []; $ruleType = $rule->getAttribute('type', ''); $resourceType = $rule->getAttribute('deploymentResourceType', ''); // Ensures different target based on rule's type, as configured by env variables if ($resourceType === 'function') { // For example: fra.appwrite.run - $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_FUNCTIONS', '')); + foreach (\explode(',', System::getEnv('_APP_DOMAIN_FUNCTIONS', '')) as $targetCNAME) { + if (empty($targetCNAME)) { + continue; + } + $targetCNAMEs[] = new Domain($targetCNAME); + } } elseif ($resourceType === 'site') { // For example: appwrite.network - $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_SITES', '')); + foreach (\explode(',', System::getEnv('_APP_DOMAIN_SITES', '')) as $targetCNAME) { + if (empty($targetCNAME)) { + continue; + } + $targetCNAMEs[] = new Domain($targetCNAME); + } } elseif ($ruleType === 'api') { // For example: fra.cloud.appwrite.io - $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME', '')); + $targetCNAMEs[] = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME', '')); } elseif ($ruleType === 'redirect') { // Shouldn't be needed, because redirect should always have resourceTyp too, but just in case we default to sites // For example: appwrite.network - $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_SITES', '')); + foreach (\explode(',', System::getEnv('_APP_DOMAIN_SITES', '')) as $targetCNAME) { + if (empty($targetCNAME)) { + continue; + } + $targetCNAMEs[] = new Domain($targetCNAME); + } } $validators = []; $mainValidator = null; // Validator to use for error description - if (!is_null($targetCNAME)) { - $validator = new $dnsValidatorClass($targetCNAME->get(), Record::TYPE_CNAME, $dnsServers); + if (\count($targetCNAMEs) > 0) { + $cnameValidators = []; + foreach ($targetCNAMEs as $targetCNAME) { + $cnameValidators[] = new $dnsValidatorClass($targetCNAME->get(), Record::TYPE_CNAME, $dnsServers); + } + + $validator = new AnyOf($cnameValidators); $validators[] = $validator; if (\is_null($mainValidator)) { @@ -185,4 +213,30 @@ class Action extends PlatformAction throw new Exception(Exception::RULE_VERIFICATION_FAILED, $mainValidator->getDescription()); } } + + protected function isAppwriteOwned(string $domain): bool + { + $appwriteDomains = []; + $appwriteDomainEnvs = [ + System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', ''), + System::getEnv('_APP_DOMAIN_FUNCTIONS', ''), + System::getEnv('_APP_DOMAIN_SITES', ''), + ]; + foreach ($appwriteDomainEnvs as $appwriteDomainEnv) { + foreach (\explode(',', $appwriteDomainEnv) as $appwriteDomain) { + if (empty($appwriteDomain)) { + continue; + } + $appwriteDomains[] = $appwriteDomain; + } + } + + foreach ($appwriteDomains as $appwriteDomain) { + if (\str_ends_with($domain, $appwriteDomain)) { + return true; + } + } + + return false; + } } diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php index 95ea8dd8cf..86b780bde0 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php @@ -74,18 +74,12 @@ class Create extends Action { $this->validateDomainRestrictions($domain, $platform); - $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); - $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - // TODO: (@Meldiron) Remove after 1.7.x migration $ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain) : ID::unique(); $status = RULE_STATUS_CREATED; $owner = ''; - if ( - ($functionsDomain != '' && \str_ends_with($domain, $functionsDomain)) || - ($sitesDomain != '' && \str_ends_with($domain, $sitesDomain)) - ) { + if ($this->isAppwriteOwned($domain)) { $status = RULE_STATUS_VERIFIED; $owner = 'Appwrite'; } diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php index ea0fb69050..5837d80630 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php @@ -79,9 +79,6 @@ class Create extends Action { $this->validateDomainRestrictions($domain, $platform); - $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); - $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { throw new Exception(Exception::RULE_RESOURCE_NOT_FOUND); @@ -94,10 +91,7 @@ class Create extends Action $status = RULE_STATUS_CREATED; $owner = ''; - if ( - ($functionsDomain != '' && \str_ends_with($domain, $functionsDomain)) || - ($sitesDomain != '' && \str_ends_with($domain, $sitesDomain)) - ) { + if ($this->isAppwriteOwned($domain)) { $status = RULE_STATUS_VERIFIED; $owner = 'Appwrite'; } diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php index f21374b49a..e1dd3de108 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php @@ -82,9 +82,6 @@ class Create extends Action { $this->validateDomainRestrictions($domain, $platform); - $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); - $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - $collection = match ($resourceType) { 'site' => 'sites', 'function' => 'functions' @@ -99,10 +96,7 @@ class Create extends Action $status = RULE_STATUS_CREATED; $owner = ''; - if ( - ($functionsDomain != '' && \str_ends_with($domain, $functionsDomain)) || - ($sitesDomain != '' && \str_ends_with($domain, $sitesDomain)) - ) { + if ($this->isAppwriteOwned($domain)) { $status = RULE_STATUS_VERIFIED; $owner = 'Appwrite'; } diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php index 26bd453eb3..20829c1b91 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php @@ -79,9 +79,6 @@ class Create extends Action { $this->validateDomainRestrictions($domain, $platform); - $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); - $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - $site = $dbForProject->getDocument('sites', $siteId); if ($site->isEmpty()) { throw new Exception(Exception::RULE_RESOURCE_NOT_FOUND); @@ -94,10 +91,7 @@ class Create extends Action $status = RULE_STATUS_CREATED; $owner = ''; - if ( - ($functionsDomain != '' && \str_ends_with($domain, $functionsDomain)) || - ($sitesDomain != '' && \str_ends_with($domain, $sitesDomain)) - ) { + if ($this->isAppwriteOwned($domain)) { $status = RULE_STATUS_VERIFIED; $owner = 'Appwrite'; } diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php index c2420aa223..6d6b599ed7 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php @@ -274,7 +274,7 @@ class Create extends Action ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('sites', $site->getId(), $site); - $sitesDomain = $platform['sitePreviewDomain']; + $sitesDomain = $platform['sitesDomain']; $domain = ID::unique() . "." . $sitesDomain; // TODO: (@Meldiron) Remove after 1.7.x migration @@ -344,7 +344,7 @@ class Create extends Action ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('sites', $site->getId(), $site); - $sitesDomain = $platform['sitePreviewDomain']; + $sitesDomain = $platform['sitesDomain']; $domain = ID::unique() . "." . $sitesDomain; $ruleId = md5($domain); $authorization->skip( diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php index bb0c007850..1d60c6776c 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php @@ -145,7 +145,7 @@ class Create extends Action $dbForProject->updateDocument('sites', $site->getId(), $site); // Preview deployments for sites - $sitesDomain = $platform['sitePreviewDomain']; + $sitesDomain = $platform['sitesDomain']; $domain = ID::unique() . "." . $sitesDomain; // TODO: (@Meldiron) Remove after 1.7.x migration diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php index 2e079a057b..e36bed94bc 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php @@ -189,7 +189,7 @@ class Create extends Base ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('sites', $site->getId(), $site); - $sitesDomain = $platform['sitePreviewDomain']; + $sitesDomain = $platform['sitesDomain']; $domain = ID::unique() . "." . $sitesDomain; // TODO: (@Meldiron) Remove after 1.7.x migration diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index dc49d27aea..e30197cf13 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -1283,6 +1283,7 @@ class UsageTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); + $functionsDomain = \explode(',', System::getEnv('_APP_DOMAIN_FUNCTIONS', ''))[0]; $rule = $this->client->call( Client::METHOD_POST, '/proxy/rules/function', @@ -1291,7 +1292,7 @@ class UsageTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'domain' => 'test-' . ID::unique() . '.' . System::getEnv('_APP_DOMAIN_FUNCTIONS'), + 'domain' => 'test-' . ID::unique() . '.' . $functionsDomain, 'functionId' => $functionId, ], ); diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index 7403b23a73..4994561c9a 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -316,12 +316,13 @@ trait FunctionsBase protected function setupFunctionDomain(string $functionId, string $subdomain = ''): string { + $functionsDomain = \explode(',', System::getEnv('_APP_DOMAIN_FUNCTIONS', ''))[0]; $subdomain = $subdomain ? $subdomain : ID::unique(); $rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/function', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'domain' => $subdomain . '.' . System::getEnv('_APP_DOMAIN_FUNCTIONS', ''), + 'domain' => $subdomain . '.' . $functionsDomain, 'functionId' => $functionId, ]); diff --git a/tests/e2e/Services/Projects/ProjectsCustomServerTest.php b/tests/e2e/Services/Projects/ProjectsCustomServerTest.php index b2cf57ddc4..313a4d53be 100644 --- a/tests/e2e/Services/Projects/ProjectsCustomServerTest.php +++ b/tests/e2e/Services/Projects/ProjectsCustomServerTest.php @@ -50,7 +50,7 @@ class ProjectsCustomServerTest extends Scope $this->assertEquals(204, $response['headers']['status-code']); - $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); + $functionsDomain = \explode(',', System::getEnv('_APP_DOMAIN_FUNCTIONS', ''))[0]; $response = $this->client->call(Client::METHOD_POST, '/proxy/rules/api', $headers, [ 'domain' => $functionsDomain, @@ -59,7 +59,7 @@ class ProjectsCustomServerTest extends Scope $this->assertEquals(400, $response['headers']['status-code']); - $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); + $sitesDomain = \explode(',', System::getEnv('_APP_DOMAIN_SITES', ''))[0]; $response = $this->client->call(Client::METHOD_POST, '/proxy/rules/api', $headers, [ 'domain' => $sitesDomain, diff --git a/tests/e2e/Services/Proxy/ProxyCustomServerTest.php b/tests/e2e/Services/Proxy/ProxyCustomServerTest.php index 1830adbae3..be0b89b404 100644 --- a/tests/e2e/Services/Proxy/ProxyCustomServerTest.php +++ b/tests/e2e/Services/Proxy/ProxyCustomServerTest.php @@ -112,7 +112,8 @@ class ProxyCustomServerTest extends Scope $this->assertEquals(201, $rule['headers']['status-code']); $this->cleanupRule($rule['body']['$id']); - $domain = \uniqid() . '-vcs.' . System::getEnv('_APP_DOMAIN_SITES', ''); + $sitesDomain = \explode(',', System::getEnv('_APP_DOMAIN_SITES', ''))[0]; + $domain = \uniqid() . '-vcs.' . $sitesDomain; $rule = $this->createSiteRule('commit-' . $domain, $siteId); $this->assertEquals(400, $rule['headers']['status-code']); @@ -292,11 +293,28 @@ class ProxyCustomServerTest extends Scope $ruleId = $this->setupSiteRule($domain, $siteId); $this->assertNotEmpty($ruleId); + $rule = $this->getRule($ruleId); + $this->assertSame(200, $rule['headers']['status-code']); + $this->assertSame('created', $rule['body']['status']); $response = $proxyClient->call(Client::METHOD_GET, '/contact'); $this->assertEquals(200, $response['headers']['status-code']); $this->assertStringContainsString('Contact page', $response['body']); + // Wildcard domains automatically get verified status + $domains = [ + \uniqid() . '.sites.localhost', + \uniqid() . '.rebranded.localhost', + ]; + foreach ($domains as $domain) { + $wildcardRuleId = $this->setupSiteRule($domain, $siteId); + $this->assertNotEmpty($wildcardRuleId); + $rule = $this->getRule($wildcardRuleId); + $this->assertSame(200, $rule['headers']['status-code']); + $this->assertSame('verified', $rule['body']['status']); + $this->cleanupRule($wildcardRuleId); + } + $rules = $this->listRules([ 'queries' => [ Query::limit(1)->toString(), @@ -384,7 +402,8 @@ class ProxyCustomServerTest extends Scope public function testUpdateRule(): void { // Create function appwrite-network domain - $domain = \uniqid() . '-cname-api.' . System::getEnv('_APP_DOMAIN_FUNCTIONS'); + $functionsDomain = \explode(',', System::getEnv('_APP_DOMAIN_FUNCTIONS', ''))[0]; + $domain = \uniqid() . '-cname-api.' . $functionsDomain; $rule = $this->createAPIRule($domain); $this->assertEquals(201, $rule['headers']['status-code']); @@ -393,7 +412,8 @@ class ProxyCustomServerTest extends Scope $this->cleanupRule($rule['body']['$id']); // Create site appwrite-network domain - $domain = \uniqid() . '-cname-api.' . System::getEnv('_APP_DOMAIN_SITES'); + $sitesDomain = \explode(',', System::getEnv('_APP_DOMAIN_SITES', ''))[0]; + $domain = \uniqid() . '-cname-api.' . $sitesDomain; $rule = $this->createAPIRule($domain); $this->assertEquals(201, $rule['headers']['status-code']); diff --git a/tests/e2e/Services/Sites/SitesBase.php b/tests/e2e/Services/Sites/SitesBase.php index 7eb5d9699c..e7af09d1d9 100644 --- a/tests/e2e/Services/Sites/SitesBase.php +++ b/tests/e2e/Services/Sites/SitesBase.php @@ -368,12 +368,13 @@ trait SitesBase protected function setupSiteDomain(string $siteId, string $subdomain = ''): string { + $sitesDomain = \explode(',', System::getEnv('_APP_DOMAIN_SITES', ''))[0]; $subdomain = $subdomain ? $subdomain : ID::unique(); $rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/site', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'domain' => $subdomain . '.' . System::getEnv('_APP_DOMAIN_SITES', ''), + 'domain' => $subdomain . '.' . $sitesDomain, 'siteId' => $siteId, ]); diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index ff4d8dd5e1..27294a9c3b 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -1810,11 +1810,12 @@ class SitesCustomServerTest extends Scope $siteId2 = $site2['body']['$id']; + $sitesDomain = \explode(',', System::getEnv('_APP_DOMAIN_SITES', ''))[0]; $rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/site', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'domain' => $subdomain . '.' . System::getEnv('_APP_DOMAIN_SITES', ''), + 'domain' => $subdomain . '.' . $sitesDomain, 'siteId' => $siteId2, ]);