Merge pull request #11248 from appwrite/feat-trusted-console-projects

Feat: Support trusted console projects
This commit is contained in:
Matej Bačo 2026-02-04 16:10:36 +01:00 committed by GitHub
commit f7f6b5187c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 83 additions and 1 deletions

1
.env
View file

@ -25,6 +25,7 @@ _APP_OPENSSL_KEY_V1=your-secret-key
_APP_DNS=172.16.238.100 # CoreDNS
_APP_DOMAIN=appwrite.test
_APP_CONSOLE_DOMAIN=localhost
_APP_CONSOLE_TRUSTED_PROJECTS=trusted-project,another-trusted-project
_APP_DOMAIN_FUNCTIONS=functions.localhost
_APP_DOMAIN_SITES=sites.localhost,rebranded.localhost
_APP_DOMAIN_TARGET_CNAME=cname.localhost

View file

@ -253,7 +253,24 @@ Http::setResource('rule', function (Request $request, Database $dbForPlatform, D
]) ?? new Document();
});
if ($rule->getAttribute('projectInternalId') !== $project->getSequence()) {
$permitsCurrentProject = $rule->getAttribute('projectInternalId', '') === $project->getSequence();
// Temporary implementation until custom wildcard domains are an official feature
// Allow trusted projects; Used for Console (website) previews
if (!$permitsCurrentProject && !$rule->isEmpty() && !empty($rule->getAttribute('projectId', ''))) {
$trustedProjects = [];
foreach (\explode(',', System::getEnv('_APP_CONSOLE_TRUSTED_PROJECTS', '')) as $trustedProject) {
if (empty($trustedProject)) {
continue;
}
$trustedProjects[] = $trustedProject;
}
if (\in_array($rule->getAttribute('projectId', ''), $trustedProjects)) {
$permitsCurrentProject = true;
}
}
if (!$permitsCurrentProject) {
return new Document();
}

View file

@ -133,6 +133,7 @@ services:
- _APP_OPENSSL_KEY_V1
- _APP_DOMAIN
- _APP_CONSOLE_DOMAIN
- _APP_CONSOLE_TRUSTED_PROJECTS
- _APP_DOMAIN_TARGET_CNAME
- _APP_DOMAIN_TARGET_AAAA
- _APP_DOMAIN_TARGET_A
@ -510,6 +511,7 @@ services:
- _APP_OPTIONS_ROUTER_FORCE_HTTPS
- _APP_DOMAIN
- _APP_CONSOLE_DOMAIN
- _APP_CONSOLE_TRUSTED_PROJECTS
- _APP_STORAGE_DEVICE
- _APP_STORAGE_S3_ACCESS_KEY
- _APP_STORAGE_S3_SECRET

View file

@ -5173,6 +5173,68 @@ class ProjectsConsoleClientTest extends Scope
$this->assertEquals($origin, $response['headers']['access-control-allow-origin'] ?? null);
}
public function testConsoleCorsWithTrustedProject(): void
{
$trustedProjectIds = ['trusted-project', 'another-trusted-project']; // Set in env variable
$projectIds = \array_merge($trustedProjectIds, ['untrusted-project-id']);
foreach ($projectIds as $projectId) {
try {
// Create project
$this->setupProject([
'projectId' => $projectId,
'name' => 'Trusted project',
'region' => System::getEnv('_APP_REGION', 'default')
]);
// Add domain to trusted project; API for simplicity, in real work this will be site
$domain = \uniqid() . '.custom.localhost';
$rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/api', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-mode' => 'admin',
], $this->getHeaders()), [
'domain' => $domain
]);
$this->assertEquals(201, $rule['headers']['status-code']);
// Talk to Console APIs from trusted project domain
$currencies = $this->client->call(
Client::METHOD_GET,
'/locale/currencies',
array_merge(
$this->getHeaders(),
[
'content-type' => 'application/json',
'x-appwrite-project' => 'console',
'origin' => 'http://' . $domain
]
)
);
if (\in_array($projectId, $trustedProjectIds)) {
// Trusted projects can
$this->assertEquals(200, $currencies['headers']['status-code']);
$this->assertSame('http://' . $domain, $currencies['headers']['access-control-allow-origin']);
} else {
// Untrusted projects cannot
$this->assertEquals(403, $currencies['headers']['status-code']);
$this->assertArrayNotHasKey('access-control-allow-origin', $currencies['headers']);
}
} finally {
// Cleanup
$response = $this->client->call(Client::METHOD_DELETE, '/projects/' . $projectId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), []);
$this->assertEquals(204, $response['headers']['status-code']);
}
}
}
/**
* @group abuseEnabled
*/