diff --git a/.env b/.env index 64fc7ef10f..e849e83801 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 37f7fdbc8b..c4d703d744 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) @@ -2079,6 +2079,7 @@ App::patch('/v1/projects/:projectId/smtp') if ($enabled) { $mail = new PHPMailer(true); $mail->isSMTP(); + $mail->SMTPAuth = (!empty($username) && !empty($password)); $mail->Username = $username; $mail->Password = $password; $mail->Host = $host; @@ -2094,7 +2095,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()); } } @@ -2665,7 +2666,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 57007c4efc..3b935b84fb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1131,6 +1131,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/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 = [ diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 9526c5a4da..f2122b4ba9 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -23,9 +23,9 @@ class ProjectsConsoleClientTest extends Scope use Async; /** - * @group devKeys * @group smtpAndTemplates - * @group projectsCRUD */ + * @group projectsCRUD + */ public function testCreateProject(): array { /** @@ -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', @@ -618,6 +618,14 @@ class ProjectsConsoleClientTest extends Scope public function testUpdateProjectSMTP($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'); + + /** + * 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'], @@ -625,23 +633,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'], @@ -651,12 +659,32 @@ 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 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'], + ], $this->getHeaders()), [ + 'enabled' => true, + 'senderEmail' => 'fail@appwrite.io', + 'senderName' => 'Failing Mailer', + '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('Could not authenticate', $response['body']['message']); + return $data; } @@ -665,6 +693,11 @@ class ProjectsConsoleClientTest extends Scope */ public function testCreateProjectSMTPTests(): void { + $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'); + // Create a team $team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([ 'content-type' => 'application/json', @@ -699,10 +732,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']); @@ -736,10 +769,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']); @@ -752,10 +785,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']); @@ -776,7 +809,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']); @@ -879,7 +912,7 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertNotEmpty($response['body']['$id']); - $this->assertEquals('Project Test 2', $response['body']['name']); + $this->assertEquals('Project Test', $response['body']['name']); $this->assertArrayHasKey('platforms', $response['body']); $this->assertArrayHasKey('webhooks', $response['body']); $this->assertArrayHasKey('keys', $response['body']); @@ -959,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); @@ -3132,7 +3132,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']); @@ -3151,7 +3151,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']); @@ -5086,8 +5086,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,