From a14d5c584ebefbd3e9181c1df3965071b4226001 Mon Sep 17 00:00:00 2001 From: VijaykumarPujar-tech Date: Mon, 8 Dec 2025 20:22:18 +0530 Subject: [PATCH 01/15] Fix: robust SMTP validation and added regression test --- .../Projects/ProjectsConsoleClientTest.php | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index e297757225..99f7205d28 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -564,6 +564,10 @@ class ProjectsConsoleClientTest extends Scope public function testUpdateProjectSMTP($data): array { $id = $data['projectId']; + + /** + * Test for SUCCESS: Valid Credentials + */ $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/smtp', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -603,6 +607,35 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals('password', $response['body']['smtpPassword']); $this->assertEquals('', $response['body']['smtpSecure']); + /** * Test for FAILURE: Missing or Invalid Credentials + */ + $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/smtp', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'enabled' => true, + 'senderEmail' => 'fail@appwrite.io', + 'senderName' => 'Failing Mailer', + 'host' => 'maildev', + 'port' => 1025, + 'username' => 'invalid-user', + 'password' => 'bad-password', + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertEquals(Exception::PROJECT_SMTP_CONFIG_INVALID, $response['body']['type']); + $this->assertStringContainsStringIgnoringCase('SMTP authentication failed.', $response['body']['message']); + + /** * Test Reading Project to ensure settings were NOT saved after failure + */ + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('mailer@appwrite.io', $response['body']['smtpSenderEmail']); + return $data; } From 2fa95b52a047377445cb0ea8c28ef3cacb168e27 Mon Sep 17 00:00:00 2001 From: VijaykumarPujar-tech Date: Tue, 9 Dec 2025 22:40:51 +0530 Subject: [PATCH 02/15] Reverted some added changes --- .../Projects/ProjectsConsoleClientTest.php | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 99f7205d28..7e81ca9db0 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -565,9 +565,7 @@ class ProjectsConsoleClientTest extends Scope { $id = $data['projectId']; - /** - * Test for SUCCESS: Valid Credentials - */ + /**Test for SUCCESS: Valid Credentials*/ $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/smtp', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -607,8 +605,7 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals('password', $response['body']['smtpPassword']); $this->assertEquals('', $response['body']['smtpSecure']); - /** * Test for FAILURE: Missing or Invalid Credentials - */ + /** Test for Missing or Invalid Credentials*/ $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/smtp', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -626,16 +623,6 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals(Exception::PROJECT_SMTP_CONFIG_INVALID, $response['body']['type']); $this->assertStringContainsStringIgnoringCase('SMTP authentication failed.', $response['body']['message']); - /** * Test Reading Project to ensure settings were NOT saved after failure - */ - $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals('mailer@appwrite.io', $response['body']['smtpSenderEmail']); - return $data; } From 8951a8465c5d7a485604ef65a974da8dda1ed68c Mon Sep 17 00:00:00 2001 From: VijaykumarPujar-tech Date: Tue, 9 Dec 2025 23:30:34 +0530 Subject: [PATCH 03/15] Added the projects.php changes back --- app/controllers/api/projects.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 80d407322e..b8011828ce 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -2073,8 +2073,11 @@ App::patch('/v1/projects/:projectId/smtp') if ($enabled) { $mail = new PHPMailer(true); $mail->isSMTP(); - $mail->Username = $username; - $mail->Password = $password; + if (!empty($username) && !empty($password)) { + $mail->SMTPAuth = true; + $mail->Username = $username; + $mail->Password = $password; + } $mail->Host = $host; $mail->Port = $port; $mail->SMTPSecure = $secure; From e0a937912c6a9a2cc7c11636f843891e3496ecc3 Mon Sep 17 00:00:00 2001 From: VijaykumarPujar-tech Date: Tue, 9 Dec 2025 23:46:44 +0530 Subject: [PATCH 04/15] Added Validation check for username and password --- app/controllers/api/projects.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index b8011828ce..e0f8013467 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -2071,13 +2071,16 @@ App::patch('/v1/projects/:projectId/smtp') // validate SMTP settings if ($enabled) { + if (empty($username)) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'SMTP Username is required when enabling SMTP.'); + } elseif (empty($password)) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'SMTP Password is required when enabling SMTP.'); + } $mail = new PHPMailer(true); $mail->isSMTP(); - if (!empty($username) && !empty($password)) { - $mail->SMTPAuth = true; - $mail->Username = $username; - $mail->Password = $password; - } + $mail->SMTPAuth = true; + $mail->Username = $username; + $mail->Password = $password; $mail->Host = $host; $mail->Port = $port; $mail->SMTPSecure = $secure; From d174233cd64a7b407f918041f68106d9dda1bdbb Mon Sep 17 00:00:00 2001 From: Steven Nguyen <1477010+stnguyen90@users.noreply.github.com> Date: Fri, 12 Dec 2025 04:58:59 +0000 Subject: [PATCH 05/15] fix: update SMTP configuration and enhance validation checks --- .env | 4 +- app/controllers/api/projects.php | 13 +-- docker-compose.yml | 3 + .../Projects/ProjectsConsoleClientTest.php | 91 +++++++++++-------- 4 files changed, 61 insertions(+), 50 deletions(-) diff --git a/.env b/.env index 4d7c038a6b..256f2c984a 100644 --- a/.env +++ b/.env @@ -69,8 +69,8 @@ _APP_STORAGE_ANTIVIRUS_PORT=3310 _APP_SMTP_HOST=maildev _APP_SMTP_PORT=1025 _APP_SMTP_SECURE= -_APP_SMTP_USERNAME= -_APP_SMTP_PASSWORD= +_APP_SMTP_USERNAME=user +_APP_SMTP_PASSWORD=password _APP_SMS_PROVIDER=sms://username:password@mock _APP_SMS_FROM=+123456789 _APP_SMS_PROJECTS_DENY_LIST= diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index e0f8013467..fa48405e5e 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -294,7 +294,7 @@ App::post('/v1/projects') // Hook allowing instant project mirroring during migration // Outside of migration, hook is not registered and has no effect - $hooks->trigger('afterProjectCreation', [ $project, $pools, $cache ]); + $hooks->trigger('afterProjectCreation', [$project, $pools, $cache]); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -2071,14 +2071,9 @@ App::patch('/v1/projects/:projectId/smtp') // validate SMTP settings if ($enabled) { - if (empty($username)) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'SMTP Username is required when enabling SMTP.'); - } elseif (empty($password)) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'SMTP Password is required when enabling SMTP.'); - } $mail = new PHPMailer(true); $mail->isSMTP(); - $mail->SMTPAuth = true; + $mail->SMTPAuth = (!empty($username) && !empty($password)); $mail->Username = $username; $mail->Password = $password; $mail->Host = $host; @@ -2094,7 +2089,7 @@ App::patch('/v1/projects/:projectId/smtp') throw new Exception('Connection is not valid.'); } } catch (Throwable $error) { - throw new Exception(Exception::PROJECT_SMTP_CONFIG_INVALID, 'Could not connect to SMTP server: ' . $error->getMessage()); + throw new Exception(Exception::PROJECT_SMTP_CONFIG_INVALID, $error->getMessage()); } } @@ -2661,7 +2656,7 @@ App::patch('/v1/projects/:projectId/auth/session-invalidation') $auths = $project->getAttribute('auths', []); $auths['invalidateSessions'] = $enabled; $dbForPlatform->updateDocument('projects', $project->getId(), $project - ->setAttribute('auths', $auths)); + ->setAttribute('auths', $auths)); $response->dynamic($project, Response::MODEL_PROJECT); }); diff --git a/docker-compose.yml b/docker-compose.yml index 9575904616..b0811ee916 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1102,6 +1102,9 @@ services: - "traefik.http.routers.appwrite_maildev_https.rule=Host(`mail.localhost`)" - "traefik.http.routers.appwrite_maildev_https.service=appwrite_maildev" - "traefik.http.routers.appwrite_maildev_https.tls=true" + environment: + - MAILDEV_INCOMING_USER=${_APP_SMTP_USERNAME} + - MAILDEV_INCOMING_PASS=${_APP_SMTP_PASSWORD} request-catcher-webhook: # used mainly for dev tests (mock HTTP webhook) image: appwrite/requestcatcher:1.0.0 diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 7e81ca9db0..85f54e09cc 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -24,9 +24,9 @@ class ProjectsConsoleClientTest extends Scope use Async; /** - * @group devKeys * @group smtpAndTemplates - * @group projectsCRUD */ + * @group projectsCRUD + */ public function testCreateProject(): array { /** @@ -564,8 +564,14 @@ class ProjectsConsoleClientTest extends Scope public function testUpdateProjectSMTP($data): array { $id = $data['projectId']; - - /**Test for SUCCESS: Valid Credentials*/ + $smtpHost = System::getEnv('_APP_SMTP_HOST', "maildev"); + $smtpPort = intval(System::getEnv('_APP_SMTP_PORT', "1025")); + $smtpUsername = System::getEnv('_APP_SMTP_USERNAME', 'user'); + $smtpPassword = System::getEnv('_APP_SMTP_PASSWORD', 'password'); + + /** + * Test for SUCCESS: Valid Credentials + */ $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/smtp', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -573,23 +579,23 @@ class ProjectsConsoleClientTest extends Scope 'enabled' => true, 'senderEmail' => 'mailer@appwrite.io', 'senderName' => 'Mailer', - 'host' => 'maildev', - 'port' => 1025, - 'username' => 'user', - 'password' => 'password', + 'host' => $smtpHost, + 'port' => $smtpPort, + 'username' => $smtpUsername, + 'password' => $smtpPassword, ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertTrue($response['body']['smtpEnabled']); $this->assertEquals('mailer@appwrite.io', $response['body']['smtpSenderEmail']); $this->assertEquals('Mailer', $response['body']['smtpSenderName']); - $this->assertEquals('maildev', $response['body']['smtpHost']); - $this->assertEquals(1025, $response['body']['smtpPort']); - $this->assertEquals('user', $response['body']['smtpUsername']); - $this->assertEquals('password', $response['body']['smtpPassword']); + $this->assertEquals($smtpHost, $response['body']['smtpHost']); + $this->assertEquals($smtpPort, $response['body']['smtpPort']); + $this->assertEquals($smtpUsername, $response['body']['smtpUsername']); + $this->assertEquals($smtpPassword, $response['body']['smtpPassword']); $this->assertEquals('', $response['body']['smtpSecure']); - /** Test Reading Project */ + // Check the project $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -599,13 +605,15 @@ class ProjectsConsoleClientTest extends Scope $this->assertTrue($response['body']['smtpEnabled']); $this->assertEquals('mailer@appwrite.io', $response['body']['smtpSenderEmail']); $this->assertEquals('Mailer', $response['body']['smtpSenderName']); - $this->assertEquals('maildev', $response['body']['smtpHost']); - $this->assertEquals(1025, $response['body']['smtpPort']); - $this->assertEquals('user', $response['body']['smtpUsername']); - $this->assertEquals('password', $response['body']['smtpPassword']); + $this->assertEquals($smtpHost, $response['body']['smtpHost']); + $this->assertEquals($smtpPort, $response['body']['smtpPort']); + $this->assertEquals($smtpUsername, $response['body']['smtpUsername']); + $this->assertEquals($smtpPassword, $response['body']['smtpPassword']); $this->assertEquals('', $response['body']['smtpSecure']); - /** Test for Missing or Invalid Credentials*/ + /** + * Test for FAILURE: Invalid Credentials + */ $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/smtp', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -613,15 +621,15 @@ class ProjectsConsoleClientTest extends Scope 'enabled' => true, 'senderEmail' => 'fail@appwrite.io', 'senderName' => 'Failing Mailer', - 'host' => 'maildev', - 'port' => 1025, + 'host' => $smtpHost, + 'port' => $smtpPort, 'username' => 'invalid-user', 'password' => 'bad-password', ]); - + $this->assertEquals(400, $response['headers']['status-code']); $this->assertEquals(Exception::PROJECT_SMTP_CONFIG_INVALID, $response['body']['type']); - $this->assertStringContainsStringIgnoringCase('SMTP authentication failed.', $response['body']['message']); + $this->assertStringContainsStringIgnoringCase('Could not authenticate', $response['body']['message']); return $data; } @@ -633,6 +641,11 @@ class ProjectsConsoleClientTest extends Scope public function testCreateProjectSMTPTests($data): array { $id = $data['projectId']; + $smtpHost = System::getEnv('_APP_SMTP_HOST', "maildev"); + $smtpPort = intval(System::getEnv('_APP_SMTP_PORT', "1025")); + $smtpUsername = System::getEnv('_APP_SMTP_USERNAME', 'user'); + $smtpPassword = System::getEnv('_APP_SMTP_PASSWORD', 'password'); + $response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/smtp/tests', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -641,10 +654,10 @@ class ProjectsConsoleClientTest extends Scope 'senderEmail' => 'custommailer@appwrite.io', 'senderName' => 'Custom Mailer', 'replyTo' => 'reply@appwrite.io', - 'host' => 'maildev', - 'port' => 1025, - 'username' => '', - 'password' => '', + 'host' => $smtpHost, + 'port' => $smtpPort, + 'username' => $smtpUsername, + 'password' => $smtpPassword, ]); $this->assertEquals(204, $response['headers']['status-code']); @@ -678,10 +691,10 @@ class ProjectsConsoleClientTest extends Scope 'senderEmail' => 'custommailer@appwrite.io', 'senderName' => 'Custom Mailer', 'replyTo' => 'reply@appwrite.io', - 'host' => 'maildev', - 'port' => 1025, - 'username' => '', - 'password' => '', + 'host' => $smtpHost, + 'port' => $smtpPort, + 'username' => $smtpUsername, + 'password' => $smtpPassword, ]); $this->assertEquals(204, $response['headers']['status-code']); @@ -694,10 +707,10 @@ class ProjectsConsoleClientTest extends Scope 'senderEmail' => 'custommailer@appwrite.io', 'senderName' => 'Custom Mailer', 'replyTo' => 'reply@appwrite.io', - 'host' => 'maildev', - 'port' => 1025, - 'username' => '', - 'password' => '', + 'host' => $smtpHost, + 'port' => $smtpPort, + 'username' => $smtpUsername, + 'password' => $smtpPassword, ]); $this->assertEquals(400, $response['headers']['status-code']); @@ -720,7 +733,7 @@ class ProjectsConsoleClientTest extends Scope ], $this->getHeaders())); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals('Account Verification', $response['body']['subject']); + $this->assertEquals('Account Verification for {{project}}', $response['body']['subject']); $this->assertEquals('', $response['body']['senderEmail']); $this->assertEquals('verification', $response['body']['type']); $this->assertEquals('en-us', $response['body']['locale']); @@ -3023,7 +3036,7 @@ class ProjectsConsoleClientTest extends Scope $this->assertContains('users.write', $response['body']['scopes']); $this->assertContains('collections.read', $response['body']['scopes']); $this->assertContains('tables.read', $response['body']['scopes']); - $this->assertCount(3, $response['body']['scopes']); + $this->assertCount(4, $response['body']['scopes']); $this->assertArrayHasKey('sdks', $response['body']); $this->assertEmpty($response['body']['sdks']); $this->assertArrayHasKey('accessedAt', $response['body']); @@ -3042,7 +3055,7 @@ class ProjectsConsoleClientTest extends Scope $this->assertContains('users.write', $response['body']['scopes']); $this->assertContains('collections.read', $response['body']['scopes']); $this->assertContains('tables.read', $response['body']['scopes']); - $this->assertCount(3, $response['body']['scopes']); + $this->assertCount(4, $response['body']['scopes']); $this->assertArrayHasKey('sdks', $response['body']); $this->assertEmpty($response['body']['sdks']); $this->assertArrayHasKey('accessedAt', $response['body']); @@ -4976,8 +4989,8 @@ class ProjectsConsoleClientTest extends Scope $this->assertEmpty($response['body']); /** - * Get rate limit trying to use the deleted key - */ + * Get rate limit trying to use the deleted key + */ $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, From a94ccdb4f540d394855f2acc624c4d16d90d1b5f Mon Sep 17 00:00:00 2001 From: Steven Nguyen <1477010+stnguyen90@users.noreply.github.com> Date: Fri, 12 Dec 2025 06:02:17 +0000 Subject: [PATCH 06/15] fix: enhance SMTP authentication by using env vars --- tests/e2e/Scopes/ProjectCustom.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/e2e/Scopes/ProjectCustom.php b/tests/e2e/Scopes/ProjectCustom.php index c2b4896814..52c53016d6 100644 --- a/tests/e2e/Scopes/ProjectCustom.php +++ b/tests/e2e/Scopes/ProjectCustom.php @@ -161,9 +161,9 @@ trait ProjectCustom 'senderEmail' => 'mailer@appwrite.io', 'senderName' => 'Mailer', 'host' => 'maildev', - 'port' => 1025, - 'username' => '', - 'password' => '', + 'port' => intval(System::getEnv('_APP_SMTP_PORT', "1025")), + 'username' => System::getEnv('_APP_SMTP_USERNAME', 'user'), + 'password' => System::getEnv('_APP_SMTP_PASSWORD', 'password'), ]); $project = [ From d4517384dc3419ee3ad47a4e172b976a4fd10044 Mon Sep 17 00:00:00 2001 From: Steven Nguyen <1477010+stnguyen90@users.noreply.github.com> Date: Fri, 12 Dec 2025 07:16:49 +0000 Subject: [PATCH 07/15] fix: update project tests to reflect changes in project count and names --- .../Projects/ProjectsConsoleClientTest.php | 59 ++++--------------- 1 file changed, 13 insertions(+), 46 deletions(-) diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index bda213d121..f2122b4ba9 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -257,11 +257,11 @@ class ProjectsConsoleClientTest extends Scope 'search' => $id ])); - $this->assertEquals($response['headers']['status-code'], 200); - $this->assertEquals($response['body']['total'], 3); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(4, $response['body']['total']); $this->assertIsArray($response['body']['projects']); - $this->assertCount(3, $response['body']['projects']); - $this->assertEquals($response['body']['projects'][0]['name'], 'Project Test'); + $this->assertCount(4, $response['body']['projects']); + $this->assertEquals('Project Test', $response['body']['projects'][0]['name']); $response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([ 'content-type' => 'application/json', @@ -271,9 +271,9 @@ class ProjectsConsoleClientTest extends Scope ])); $this->assertEquals($response['headers']['status-code'], 200); - $this->assertEquals(3, $response['body']['total']); + $this->assertEquals(4, $response['body']['total']); $this->assertIsArray($response['body']['projects']); - $this->assertCount(3, $response['body']['projects']); + $this->assertCount(4, $response['body']['projects']); $this->assertEquals($response['body']['projects'][0]['$id'], $data['projectId']); /** @@ -348,8 +348,8 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertNotEmpty($response['body']); - $this->assertCount(1, $response['body']['projects']); - $this->assertEquals('Project Test 2', $response['body']['projects'][0]['name']); + $this->assertCount(2, $response['body']['projects']); + $this->assertEquals('Team 1 Project', $response['body']['projects'][0]['name']); $response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([ 'content-type' => 'application/json', @@ -376,7 +376,7 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertNotEmpty($response['body']); - $this->assertCount(4, $response['body']['projects']); + $this->assertCount(5, $response['body']['projects']); $this->assertEquals('Project Test 2', $response['body']['projects'][0]['name']); $this->assertEquals('Team 1 Project', $response['body']['projects'][1]['name']); @@ -387,9 +387,9 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertNotEmpty($response['body']); - $this->assertCount(4, $response['body']['projects']); + $this->assertCount(5, $response['body']['projects']); $this->assertEquals('Project Test', $response['body']['projects'][0]['name']); - $this->assertEquals('Team 1 Project', $response['body']['projects'][2]['name']); + $this->assertEquals('Original Project', $response['body']['projects'][2]['name']); $response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([ 'content-type' => 'application/json', @@ -402,8 +402,8 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertNotEmpty($response['body']); - $this->assertCount(3, $response['body']['projects']); - $this->assertEquals('Team 1 Project', $response['body']['projects'][1]['name']); + $this->assertCount(4, $response['body']['projects']); + $this->assertEquals('Original Project', $response['body']['projects'][1]['name']); $response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([ 'content-type' => 'application/json', @@ -992,39 +992,6 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(15, $response['body']['authDuration']); - // Create session - $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - ]), [ - 'email' => $userEmail, - 'password' => 'password', - ]); - - $this->assertEquals(201, $response['headers']['status-code']); - - $sessionCookie = $response['headers']['set-cookie']; - - // Wait 10 seconds, ensure valid session, extend session - \sleep(10); - - $response = $this->client->call(Client::METHOD_GET, '/account', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'Cookie' => $sessionCookie, - ])); - - $this->assertEquals(200, $response['headers']['status-code']); - - $response = $this->client->call(Client::METHOD_PATCH, '/account/sessions/current', array_merge([ - 'origin' => 'http://localhost', - 'content-type' => 'application/json', - 'x-appwrite-project' => $projectId, - 'cookie' => $sessionCookie, - ])); - - $this->assertEquals(200, $response['headers']['status-code']); - // Wait 20 seconds, ensure non-valid session \sleep(20); From 7dea3f19a97fc8b0d5c23c51e34588b41db6994d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 18 Dec 2025 12:31:00 +0100 Subject: [PATCH 08/15] Add scopes for devkeys --- app/config/roles.php | 2 ++ src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php | 2 +- src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php | 2 +- src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php | 2 +- src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php | 2 +- src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/config/roles.php b/app/config/roles.php index 3bf2297550..25a6bac4da 100644 --- a/app/config/roles.php +++ b/app/config/roles.php @@ -58,6 +58,8 @@ $admins = [ 'projects.write', 'keys.read', 'keys.write', + 'devKeys.read', + 'devKeys.write', 'webhooks.read', 'webhooks.write', 'locale.read', diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php index 9332453eea..92bc329b16 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php @@ -34,7 +34,7 @@ class Create extends Action ->setHttpPath('/v1/projects/:projectId/dev-keys') ->desc('Create dev key') ->groups(['api', 'projects']) - ->label('scope', 'projects.write') + ->label('scope', 'devKeys.write') ->label('sdk', new Method( namespace: 'projects', group: 'devKeys', diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php index 2bfea6c55b..58ca0759e6 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php @@ -28,7 +28,7 @@ class Delete extends Action ->setHttpPath('/v1/projects/:projectId/dev-keys/:keyId') ->desc('Delete dev key') ->groups(['api', 'projects']) - ->label('scope', 'projects.write') + ->label('scope', 'devKeys.write') ->label('sdk', new Method( namespace: 'projects', group: 'devKeys', diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php index 29cda90f66..6245b5b2dc 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php @@ -28,7 +28,7 @@ class Get extends Action ->setHttpPath('/v1/projects/:projectId/dev-keys/:keyId') ->desc('Get dev key') ->groups(['api', 'projects']) - ->label('scope', 'projects.read') + ->label('scope', 'devKeys.read') ->label('sdk', new Method( namespace: 'projects', group: 'devKeys', diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php index b13bc535dd..fbd35d0995 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php @@ -29,7 +29,7 @@ class Update extends Action ->setHttpPath('/v1/projects/:projectId/dev-keys/:keyId') ->desc('Update dev key') ->groups(['api', 'projects']) - ->label('scope', 'projects.write') + ->label('scope', 'devKeys.write') ->label('sdk', new Method( namespace: 'projects', group: 'devKeys', diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php index 209387018b..0229fd845d 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php @@ -32,7 +32,7 @@ class XList extends Action ->setHttpPath('/v1/projects/:projectId/dev-keys') ->desc('List dev keys') ->groups(['api', 'projects']) - ->label('scope', 'projects.read') + ->label('scope', 'devKeys.read') ->label('sdk', new Method( namespace: 'projects', group: 'devKeys', From 89980b1f0ef6bab907f8eb333f8b7ed5362d5036 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Thu, 18 Dec 2025 15:46:03 +0400 Subject: [PATCH 09/15] Enforce email verification when linking OAuth2 --- app/controllers/api/account.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 3e2512c073..29fc387d58 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1639,9 +1639,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $failureRedirect(Exception::USER_UNAUTHORIZED, 'OAuth provider failed to return email.'); } - /** - * Is verified is not used yet, since we don't know after an account is created anymore if it was verified or not. - */ $isVerified = $oauth2->isEmailVerified($accessToken); $identity = $dbForProject->findOne('identities', [ @@ -1653,12 +1650,16 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $user = $dbForProject->getDocument('users', $identity->getAttribute('userId')); } - // If user is not found, check if there is an identity with the same provider user ID + // If user is not found, check if there is a user with the same email + // Only allow connecting to existing account if OAuth provider verified the email if ($user === false || $user->isEmpty()) { $userWithEmail = $dbForProject->findOne('users', [ Query::equal('email', [$email]), ]); if (!$userWithEmail->isEmpty()) { + if (!$isVerified) { + $failureRedirect(Exception::USER_OAUTH2_BAD_REQUEST, 'OAuth provider did not verify the email address.'); + } $user->setAttributes($userWithEmail->getArrayCopy()); } } From 144e88452e63514dfbd0fa2f1cc9bc1f44101f8d Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Thu, 18 Dec 2025 15:49:59 +0400 Subject: [PATCH 10/15] Use general bad request for unverified OAuth email --- app/controllers/api/account.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 29fc387d58..65af15d1fe 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1658,7 +1658,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ]); if (!$userWithEmail->isEmpty()) { if (!$isVerified) { - $failureRedirect(Exception::USER_OAUTH2_BAD_REQUEST, 'OAuth provider did not verify the email address.'); + $failureRedirect(Exception::GENERAL_BAD_REQUEST); } $user->setAttributes($userWithEmail->getArrayCopy()); } From bae194e866f37db4bd3a0a178311bb39e6d250ac Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Thu, 18 Dec 2025 15:59:33 +0400 Subject: [PATCH 11/15] Link account by email during OAuth --- app/controllers/api/account.php | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 65af15d1fe..0b279b5534 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1664,6 +1664,20 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') } } + // If user is not found, check if there is an identity with the same email + // Only allow connecting to existing account if OAuth provider verified the email + if ($user === false || $user->isEmpty()) { + $identityWithMatchingEmail = $dbForProject->findOne('identities', [ + Query::equal('providerEmail', [$email]), + ]); + if (!$identityWithMatchingEmail->isEmpty()) { + if (!$isVerified) { + $failureRedirect(Exception::GENERAL_BAD_REQUEST); + } + $user->setAttributes($dbForProject->getDocument('users', $identityWithMatchingEmail->getAttribute('userId'))->getArrayCopy()); + } + } + if ($user === false || $user->isEmpty()) { // Last option -> create the user $limit = $project->getAttribute('auths', [])['limit'] ?? 0; @@ -1675,14 +1689,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') } } - // Makes sure this email is not already used in another identity - $identityWithMatchingEmail = $dbForProject->findOne('identities', [ - Query::equal('providerEmail', [$email]), - ]); - if (!$identityWithMatchingEmail->isEmpty()) { - $failureRedirect(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */ - } - try { $emailCanonical = new Email($email); } catch (Throwable) { @@ -1736,7 +1742,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'providerType' => MESSAGE_TYPE_EMAIL, 'identifier' => $email, ])); - } catch (Duplicate) { $failureRedirect(Exception::USER_ALREADY_EXISTS); } From d094d6a08140119973e624654522c909645ccbf1 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Thu, 18 Dec 2025 16:00:41 +0400 Subject: [PATCH 12/15] Remove OAuth email verification comments --- app/controllers/api/account.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 0b279b5534..b740cbe87d 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1651,7 +1651,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') } // If user is not found, check if there is a user with the same email - // Only allow connecting to existing account if OAuth provider verified the email if ($user === false || $user->isEmpty()) { $userWithEmail = $dbForProject->findOne('users', [ Query::equal('email', [$email]), @@ -1665,7 +1664,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') } // If user is not found, check if there is an identity with the same email - // Only allow connecting to existing account if OAuth provider verified the email if ($user === false || $user->isEmpty()) { $identityWithMatchingEmail = $dbForProject->findOne('identities', [ Query::equal('providerEmail', [$email]), From 81b40659219397cbde290a12119d5af362056c1f Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Thu, 18 Dec 2025 16:44:04 +0400 Subject: [PATCH 13/15] Fix identity connecting - Add MockUnverified OAuth2 provider config - Add /v1/mock/tests/general/oauth2/user-unverified endpoint - Add MockUnverified class for unverified OAuth2 flow - Update Mock::isEmailVerified to respect user['verified'] flag - Add end-to-end tests for linking unverified and verified OAuth2 users - Enable stopOnFailure in phpunit.xml --- app/config/oAuthProviders.php | 11 ++ app/controllers/mock.php | 22 +++ phpunit.xml | 2 +- src/Appwrite/Auth/OAuth2/Mock.php | 4 +- src/Appwrite/Auth/OAuth2/MockUnverified.php | 30 ++++ .../Account/AccountCustomClientTest.php | 151 ++++++++++++++++++ 6 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 src/Appwrite/Auth/OAuth2/MockUnverified.php diff --git a/app/config/oAuthProviders.php b/app/config/oAuthProviders.php index d8dfc807b1..e6acd08c54 100644 --- a/app/config/oAuthProviders.php +++ b/app/config/oAuthProviders.php @@ -462,4 +462,15 @@ return [ 'mock' => true, 'class' => 'Appwrite\\Auth\\OAuth2\\Mock', ], + 'mock-unverified' => [ + 'name' => 'MockUnverified', + 'developers' => 'https://appwrite.io', + 'icon' => 'icon-appwrite', + 'enabled' => true, + 'sandbox' => false, + 'form' => false, + 'beta' => false, + 'mock' => true, + 'class' => 'Appwrite\\Auth\\OAuth2\\MockUnverified', + ], ]; diff --git a/app/controllers/mock.php b/app/controllers/mock.php index d78bb61481..6f092a5d19 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -117,6 +117,28 @@ App::get('/v1/mock/tests/general/oauth2/user') 'id' => 1, 'name' => 'User Name', 'email' => 'useroauth@localhost.test', + 'verified' => true, + ]); + }); + +App::get('/v1/mock/tests/general/oauth2/user-unverified') + ->desc('OAuth2 User Unverified') + ->groups(['mock']) + ->label('scope', 'public') + ->label('docs', false) + ->param('token', '', new Text(100), 'OAuth2 Access Token.') + ->inject('response') + ->action(function (string $token, Response $response) { + + if ($token != '123456') { + throw new Exception(Exception::GENERAL_MOCK, 'Invalid token'); + } + + $response->json([ + 'id' => 2, + 'name' => 'User Name Unverified', + 'email' => 'useroauthunverified@localhost.test', + 'verified' => false, ]); }); diff --git a/phpunit.xml b/phpunit.xml index a8578995c1..215ee2d59d 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -6,7 +6,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="false" + stopOnFailure="true" > diff --git a/src/Appwrite/Auth/OAuth2/Mock.php b/src/Appwrite/Auth/OAuth2/Mock.php index 61ce41d1b7..8bbf3647a1 100644 --- a/src/Appwrite/Auth/OAuth2/Mock.php +++ b/src/Appwrite/Auth/OAuth2/Mock.php @@ -130,7 +130,9 @@ class Mock extends OAuth2 */ public function isEmailVerified(string $accessToken): bool { - return true; + $user = $this->getUser($accessToken); + + return $user['verified'] ?? true; } /** diff --git a/src/Appwrite/Auth/OAuth2/MockUnverified.php b/src/Appwrite/Auth/OAuth2/MockUnverified.php new file mode 100644 index 0000000000..93bea02416 --- /dev/null +++ b/src/Appwrite/Auth/OAuth2/MockUnverified.php @@ -0,0 +1,30 @@ +user)) { + $user = $this->request('GET', 'http://localhost/' . $this->version . '/mock/tests/general/oauth2/user-unverified?token=' . \urlencode($accessToken)); + + $this->user = \json_decode($user, true); + } + + return $this->user; + } +} diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index 457799f991..d3aa8a4845 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -2183,6 +2183,157 @@ class AccountCustomClientTest extends Scope $this->assertEquals('tuvwxyz', $response['body']['providerRefreshToken']); $this->assertNotEquals($initialExpiry, $response['body']['providerAccessTokenExpiry']); + // Clean up - delete the user + $response = $this->client->call(Client::METHOD_DELETE, '/users/' . $userId, array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + + $this->assertEquals(204, $response['headers']['status-code']); + + return []; + } + + public function testOAuthUnverifiedEmailCannotLinkToExistingAccount() + { + $provider = 'mock-unverified'; + $appId = '1'; + $secret = '123456'; + + // First, create a user with the same email that the unverified OAuth will try to use + $email = 'useroauthunverified@localhost.test'; + $password = 'password'; + + $response = $this->client->call(Client::METHOD_POST, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'userId' => ID::unique(), + 'email' => $email, + 'password' => $password, + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $existingUserId = $response['body']['$id']; + + // Enable the mock-unverified provider + $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $this->getProject()['$id'] . '/oauth2', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'provider' => $provider, + 'appId' => $appId, + 'secret' => $secret, + 'enabled' => true, + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Attempt OAuth login with unverified email - should fail because existing user has same email + $response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), [ + 'success' => 'http://localhost/v1/mock/tests/general/oauth2/success', + 'failure' => 'http://localhost/v1/mock/tests/general/oauth2/failure', + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertEquals('failure', $response['body']['result']); + + // Clean up - delete the user + $response = $this->client->call(Client::METHOD_DELETE, '/users/' . $existingUserId, array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + + $this->assertEquals(204, $response['headers']['status-code']); + + return []; + } + + public function testOAuthVerifiedEmailCanLinkToExistingAccount() + { + $provider = 'mock'; + $appId = '1'; + $secret = '123456'; + $email = 'useroauth@localhost.test'; + + // Create a user with the same email that the verified OAuth will try to use + $response = $this->client->call(Client::METHOD_POST, '/account', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'userId' => ID::unique(), + 'email' => $email, + 'password' => 'password', + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $existingUserId = $response['body']['$id']; + + // Enable the mock provider + $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $this->getProject()['$id'] . '/oauth2', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => 'console', + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'provider' => $provider, + 'appId' => $appId, + 'secret' => $secret, + 'enabled' => true, + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + // Attempt OAuth login with verified email - should succeed and link to existing account + $response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), [ + 'success' => 'http://localhost/v1/mock/tests/general/oauth2/success', + 'failure' => 'http://localhost/v1/mock/tests/general/oauth2/failure', + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('success', $response['body']['result']); + + // Verify the OAuth identity was linked to the existing user + $sessionCookieKey = 'a_session_' . $this->getProject()['$id']; + $session = $response['cookies'][$sessionCookieKey]; + + $response = $this->client->call(Client::METHOD_GET, '/account', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session, + ])); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($existingUserId, $response['body']['$id']); + $this->assertEquals($email, $response['body']['email']); + + // Clean up - delete the user + $response = $this->client->call(Client::METHOD_DELETE, '/users/' . $existingUserId, array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + + $this->assertEquals(204, $response['headers']['status-code']); + return []; } From 0058e88217efe7068eaf4fe01c13fff9a913045e Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 19 Dec 2025 01:53:05 +1300 Subject: [PATCH 14/15] Fix auth calls --- app/controllers/general.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 31647eb994..638df72419 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1034,7 +1034,8 @@ App::init() ->inject('dbForPlatform') ->inject('queueForCertificates') ->inject('platform') - ->action(function (Request $request, Document $console, Database $dbForPlatform, Certificate $queueForCertificates, array $platform) { + ->inject('authorization') + ->action(function (Request $request, Document $console, Database $dbForPlatform, Certificate $queueForCertificates, array $platform, Authorization $authorization) { $hostname = $request->getHostname(); $cache = Config::getParam('hostnames', []); $platformHostnames = $platform['hostnames'] ?? []; @@ -1065,7 +1066,7 @@ App::init() } // 4. Check/create rule (requires DB access) - Authorization::disable(); + $authorization->disable(); try { // TODO: (@Meldiron) Remove after 1.7.x migration $isMd5 = System::getEnv('_APP_RULES_FORMAT') === 'md5'; @@ -1121,7 +1122,7 @@ App::init() } finally { $cache[$domain->get()] = true; Config::setParam('hostnames', $cache); - Authorization::reset(); + $authorization->reset(); } }); From e61f3c7a42365132cfafb360c04d1474d0b82785 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Thu, 18 Dec 2025 18:03:02 +0400 Subject: [PATCH 15/15] Update phpunit.xml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- phpunit.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit.xml b/phpunit.xml index 215ee2d59d..a8578995c1 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -6,7 +6,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="true" + stopOnFailure="false" >