diff --git a/app/controllers/general.php b/app/controllers/general.php index 0447b305e8..c9efc90426 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -125,7 +125,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw $type = $rule->getAttribute('resourceType'); if ($type === 'function' || $type === 'site' || $type === 'deployment') { - $resourceCollection = match($type) { + $resourceCollection = match ($type) { 'function' => 'functions', 'site' => 'sites', 'deployment' => 'deployments', @@ -200,7 +200,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw throw new AppwriteException(AppwriteException::GENERAL_RESOURCE_BLOCKED); } - $version = match($type) { + $version = match ($type) { 'function' => $resource->getAttribute('version', 'v2'), 'site' => 'v4', 'deployment' => 'v4' @@ -209,7 +209,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw $runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []); $spec = Config::getParam('runtime-specifications')[$resource->getAttribute('specification', APP_COMPUTE_SPECIFICATION_DEFAULT)]; - $runtime = match($type) { + $runtime = match ($type) { 'function' => $runtimes[$resource->getAttribute('runtime')] ?? null, 'site' => $runtimes[$resource->getAttribute('buildRuntime')] ?? null, 'deployment' => $runtimes[$resource->getAttribute('buildRuntime')] ?? null, @@ -224,7 +224,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw throw new AppwriteException(AppwriteException::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $resource->getAttribute('runtime', '') . '" is not supported'); } - $deploymentId = match($type) { + $deploymentId = match ($type) { 'function' => $resource->getAttribute('deployment', ''), 'site' => $resource->getAttribute('deploymentId', ''), 'deployment' => $subResource->getId() @@ -390,12 +390,12 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw /** Execute function */ $executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST')); try { - $version = match($type) { + $version = match ($type) { 'function' => $resource->getAttribute('version', 'v2'), 'site' => 'v4', 'deployment' => 'v4' }; - $entrypoint = match($type) { + $entrypoint = match ($type) { 'function' => $deployment->getAttribute('entrypoint', ''), 'site' => '', 'deployment' => '' @@ -422,7 +422,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw $runtimeEntrypoint = 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $startCommand . '"'; } - $entrypoint = match($type) { + $entrypoint = match ($type) { 'function' => $deployment->getAttribute('entrypoint', ''), 'site' => '', 'deployment' => '' @@ -490,8 +490,8 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw if ($type === 'function') { $execution - ->setAttribute('status', 'failed') - ->setAttribute('errors', $th->getMessage() . '\nError Code: ' . $th->getCode()); + ->setAttribute('status', 'failed') + ->setAttribute('errors', $th->getMessage() . '\nError Code: ' . $th->getCode()); } Console::error($th->getMessage()); @@ -558,8 +558,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw ->addMetric(METRIC_EXECUTIONS_MB_SECONDS, (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT))) ->addMetric(str_replace('{functionInternalId}', $resource->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT))) ->setProject($project) - ->trigger() - ; + ->trigger(); return true; } elseif ($type === 'api') { @@ -738,6 +737,12 @@ App::init() $validator = new Hostname($clients); if ($validator->isValid($origin)) { $refDomainOrigin = $origin; + } else { + // Auto-allow domains with linked rule + $rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($origin))); + if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getInternalId()) { + $refDomainOrigin = $origin; + } } $refDomain = (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $refDomainOrigin . (!empty($port) ? ':' . $port : ''); @@ -787,7 +792,7 @@ App::init() $response->addFilter(new ResponseV18()); } if (version_compare($responseFormat, APP_VERSION_STABLE, '>')) { - $response->addHeader('X-Appwrite-Warning', "The current SDK is built for Appwrite " . $responseFormat . ". However, the current Appwrite server version is ". APP_VERSION_STABLE . ". Please downgrade your SDK to match the Appwrite version: https://appwrite.io/docs/sdks"); + $response->addHeader('X-Appwrite-Warning', "The current SDK is built for Appwrite " . $responseFormat . ". However, the current Appwrite server version is " . APP_VERSION_STABLE . ". Please downgrade your SDK to match the Appwrite version: https://appwrite.io/docs/sdks"); } } diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index ecba7a8b16..2a0b11f16c 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -1405,5 +1405,61 @@ class SitesCustomServerTest extends Scope $this->cleanupSite($siteId); } + public function testSiteCors(): void + { + // Create rule together with site + $subdomain = 'startup' . \uniqid(); + + $siteId = $this->setupSite([ + 'siteId' => ID::unique(), + 'name' => 'Startup site', + 'framework' => 'other', + 'adapter' => 'static', + 'buildRuntime' => 'static-1', + 'outputDirectory' => './', + 'buildCommand' => '', + 'installCommand' => '', + 'fallbackFile' => '', + 'subdomain' => $subdomain + ]); + + $this->assertNotEmpty($siteId); + + $domain = $this->getSiteDomain($siteId); + + $this->assertNotEmpty($domain); + + $url = 'http://' . $domain; + + $response = $this->client->call(Client::METHOD_GET, '/account', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'referer' => $url, + 'origin' => $url + ])); + + $this->assertEquals($url, $response['headers']['access-control-allow-origin']); + + $response = $this->client->call(Client::METHOD_GET, '/account', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => 'unknown', + 'referer' => $url, + 'origin' => $url + ])); + + $this->assertNotEquals($url, $response['headers']['access-control-allow-origin']); + $this->assertEquals('http://localhost', $response['headers']['access-control-allow-origin']); + + $response = $this->client->call(Client::METHOD_GET, '/account', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'referer' => 'http://unknown.com', + 'origin' => 'http://unknown.com' + ])); + + $this->assertNotEquals($url, $response['headers']['access-control-allow-origin']); + $this->assertEquals('http://localhost', $response['headers']['access-control-allow-origin']); + } + // TODO: Add tests for deletion of resources when site is deleted }