diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Create.php index 4c115d8d4e..b989310a3d 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Create.php @@ -13,7 +13,6 @@ use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; -use Utopia\Database\Query; use Utopia\Database\Validator\UID; use Utopia\Domains\Domain; use Utopia\Platform\Action; @@ -74,54 +73,28 @@ class Create extends Action public function action(string $domain, string $resourceType, string $resourceId, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject) { $mainDomain = System::getEnv('_APP_DOMAIN', ''); - if ($domain === $mainDomain) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your main domain to specific resource. Please use subdomain or a different domain.'); - } - $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - if ($domain === $functionsDomain || $domain === $sitesDomain) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your functions or sites domain to a specific resource. Please use a different domain.'); - } + $deniedDomains = [ + $mainDomain, + $sitesDomain, + $functionsDomain, + 'localhost', + APP_HOSTNAME_INTERNAL, + ]; - if ($domain === 'localhost' || $domain === APP_HOSTNAME_INTERNAL) { + if (in_array($domain, $deniedDomains, true)) { throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'This domain name is not allowed. Please pick another one.'); } - // TODO: @christyjacob remove once we migrate the rules in 1.7.x - if (System::getEnv('_APP_RULES_FORMAT') === 'md5') { - $document = $dbForPlatform->getDocument('rules', md5($domain)); - } else { - $document = $dbForPlatform->findOne('rules', [ - Query::equal('domain', [$domain]), - ]); - } - - if (!$document->isEmpty()) { - if ($document->getAttribute('projectId') === $project->getId()) { - $resourceType = $document->getAttribute('resourceType'); - $resourceId = $document->getAttribute('resourceId'); - $message = "Domain already assigned to another resource."; - if (!empty($resourceId)) { - $message .= " with ID '{$resourceId}'"; - } - - $message .= '.'; - } else { - $message = 'Domain already assigned to different project.'; - } - - throw new Exception(Exception::RULE_ALREADY_EXISTS, $message); - } - $resourceInternalId = ''; switch ($resourceType) { case 'function': case 'site': if (empty($resourceId)) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, '$resourceId cannot be empty for resourceType "' . $resourceType . '".'); + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'resourceId cannot be empty for resourceType "' . $resourceType . '".'); } $expectedDomain = ($resourceType === 'function') ? $functionsDomain : $sitesDomain; @@ -154,16 +127,24 @@ class Create extends Action // TODO: @christyjacob remove once we migrate the rules in 1.7.x $ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain->get()) : ID::unique(); - $rule = new Document([ - '$id' => $ruleId, - 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), - 'domain' => $domain->get(), - 'resourceType' => $resourceType, - 'resourceId' => $resourceId, - 'resourceInternalId' => $resourceInternalId, - 'certificateId' => '', - ]); + try { + $rule = new Document([ + '$id' => $ruleId, + 'projectId' => $project->getId(), + 'projectInternalId' => $project->getInternalId(), + 'domain' => $domain->get(), + 'resourceType' => $resourceType, + 'resourceId' => $resourceId, + 'resourceInternalId' => $resourceInternalId, + 'certificateId' => '', + ]); + } catch (\Throwable $e) { + if ($e->getCode() === Exception::DOCUMENT_ALREADY_EXISTS) { + throw new Exception(Exception::RULE_ALREADY_EXISTS); + } + + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'An unexpected error occurred: ' . $e->getMessage()); + } $status = 'created'; diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index 27fdd0b8fa..b053e4b9cd 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -1110,22 +1110,7 @@ class UsageTest extends Scope $this->assertNotEmpty($rule['body']['$id']); $this->assertNotEmpty($rule['body']['domain']); - $rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'queries' => [ - Query::equal('resourceId', [$functionId])->toString(), - Query::equal('resourceType', ['function'])->toString(), - ], - ]); - - $this->assertEquals(200, $rules['headers']['status-code']); - $this->assertEquals(1, $rules['body']['total']); - $this->assertCount(1, $rules['body']['rules']); - $this->assertNotEmpty($rules['body']['rules'][0]['domain']); - - $domain = $rules['body']['rules'][0]['domain']; + $domain = $rule['body']['domain']; $response = $this->client->call( Client::METHOD_GET, diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index bbdcbf3d67..ce5df8b746 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -259,7 +259,7 @@ trait FunctionsBase return $function; } - protected function createFunctionDomain(string $functionId, string $subdomain = ''): string + protected function setupFunctionDomain(string $functionId, string $subdomain = ''): string { $subdomain = $subdomain ? $subdomain : ID::unique(); $rule = $this->client->call(Client::METHOD_POST, '/proxy/rules', array_merge([ diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index d3d7c0dc18..f941c0658a 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -1654,9 +1654,7 @@ class FunctionsCustomServerTest extends Scope 'execute' => ['any'] ]); - $this->createFunctionDomain($functionId); - - $domain = $this->getFunctionDomain($functionId); + $domain = $this->setupFunctionDomain($functionId); $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', @@ -1717,7 +1715,7 @@ class FunctionsCustomServerTest extends Scope 'execute' => ['any'] ]); - $domain = $this->createFunctionDomain($functionId); + $domain = $this->setupFunctionDomain($functionId); $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', @@ -1752,7 +1750,7 @@ class FunctionsCustomServerTest extends Scope 'execute' => ['any'] ]); - $domain = $this->createFunctionDomain($functionId); + $domain = $this->setupFunctionDomain($functionId); $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', @@ -1872,7 +1870,7 @@ class FunctionsCustomServerTest extends Scope $functionId = $function['body']['$id'] ?? ''; - $domain = $this->createFunctionDomain($functionId); + $domain = $this->setupFunctionDomain($functionId); $this->setupDeployment($functionId, [ 'code' => $this->packageFunction('node'), diff --git a/tests/e2e/Services/Projects/ProjectsCustomServerTest.php b/tests/e2e/Services/Projects/ProjectsCustomServerTest.php index 25a5a93daa..60ae7e0bbb 100644 --- a/tests/e2e/Services/Projects/ProjectsCustomServerTest.php +++ b/tests/e2e/Services/Projects/ProjectsCustomServerTest.php @@ -31,6 +31,21 @@ class ProjectsCustomServerTest extends Scope $this->assertEquals(201, $response['headers']['status-code']); + $response = $this->client->call(Client::METHOD_POST, '/proxy/rules', $headers, [ + 'resourceType' => 'api', + 'domain' => 'abc.test.io', + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // duplicate rule + $response2 = $this->client->call(Client::METHOD_POST, '/proxy/rules', $headers, [ + 'resourceType' => 'api', + 'domain' => 'abc.test.io', + ]); + + $this->assertEquals(409, $response2['headers']['status-code']); + $response = $this->client->call(Client::METHOD_DELETE, '/proxy/rules/' . $response['body']['$id'], $headers); $this->assertEquals(204, $response['headers']['status-code']); @@ -69,5 +84,26 @@ class ProjectsCustomServerTest extends Scope ]); $this->assertEquals(400, $response['headers']['status-code']); + + $mainDomain = System::getEnv('_APP_DOMAIN', ''); + $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); + $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); + + $deniedDomains = [ + $mainDomain, + $sitesDomain, + $functionsDomain, + 'localhost', + APP_HOSTNAME_INTERNAL, + ]; + + foreach ($deniedDomains as $deniedDomain) { + $response = $this->client->call(Client::METHOD_POST, '/proxy/rules', $headers, [ + 'resourceType' => 'api', + 'domain' => $deniedDomain, + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + } } } diff --git a/tests/e2e/Services/Sites/SitesBase.php b/tests/e2e/Services/Sites/SitesBase.php index 72448898f5..4fcd34572d 100644 --- a/tests/e2e/Services/Sites/SitesBase.php +++ b/tests/e2e/Services/Sites/SitesBase.php @@ -269,7 +269,7 @@ trait SitesBase return $site; } - protected function createSiteDomain(string $siteId, string $subdomain = ''): string + protected function setupSiteDomain(string $siteId, string $subdomain = ''): string { $subdomain = $subdomain ? $subdomain : ID::unique(); $rule = $this->client->call(Client::METHOD_POST, '/proxy/rules', array_merge([ diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index 723b49d5a2..0ff6652ed5 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -79,7 +79,7 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($siteId); - $rule = $this->createSiteDomain($siteId, 'test-site'); + $rule = $this->setupSiteDomain($siteId); $response = $this->client->call(Client::METHOD_GET, '/console/resources', [ 'origin' => 'http://localhost', @@ -289,7 +289,7 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($siteId); - $domain = $this->createSiteDomain($siteId); + $domain = $this->setupSiteDomain($siteId); $secretVariable = $this->createVariable($siteId, [ 'key' => 'name', @@ -1245,7 +1245,7 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($site['body']['deploymentId']); }, 50000, 500); - $domain = $this->createSiteDomain($siteId); + $domain = $this->setupSiteDomain($siteId); $proxyClient = new Client(); $proxyClient->setEndpoint('http://' . $domain); @@ -1287,7 +1287,7 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($siteId); $subdomain = 'startup' . \uniqid(); - $domain = $this->createSiteDomain($siteId, $subdomain); + $domain = $this->setupSiteDomain($siteId, $subdomain); $deploymentId = $this->setupDeployment($siteId, [ 'code' => $this->packageSite('static'), @@ -1373,7 +1373,7 @@ class SitesCustomServerTest extends Scope $siteId = $site['body']['$id']; - $domain = $this->createSiteDomain($siteId, $subdomain); + $domain = $this->setupSiteDomain($siteId, $subdomain); $this->assertNotEmpty($domain); @@ -1396,7 +1396,7 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($siteId); - $domain = $this->createSiteDomain($siteId); + $domain = $this->setupSiteDomain($siteId); $deploymentId = $this->setupDeployment($siteId, [ 'code' => $this->packageSite('static'), @@ -1461,7 +1461,8 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($siteId); - $domain = $this->createSiteDomain($siteId, $subdomain); + $this->setupSiteDomain($siteId, $subdomain); + $domain = $this->getSiteDomain($siteId); $this->assertNotEmpty($domain);