diff --git a/src/ApiResource/MailSettings.php b/src/ApiResource/MailSettings.php new file mode 100644 index 0000000..615b9d1 --- /dev/null +++ b/src/ApiResource/MailSettings.php @@ -0,0 +1,69 @@ + ['mail_settings:read']], + provider: MailSettingsProvider::class, + security: "is_granted('ROLE_ADMIN')", + ), + new Patch( + uriTemplate: '/mail/configuration', + denormalizationContext: ['groups' => ['mail_settings:write']], + normalizationContext: ['groups' => ['mail_settings:read']], + provider: MailSettingsProvider::class, + processor: MailSettingsProcessor::class, + security: "is_granted('ROLE_ADMIN')", + ), + ], +)] +final class MailSettings +{ + #[Groups(['mail_settings:read', 'mail_settings:write'])] + public ?string $protocol = null; + + #[Groups(['mail_settings:read', 'mail_settings:write'])] + public ?string $imapHost = null; + + #[Groups(['mail_settings:read', 'mail_settings:write'])] + public ?int $imapPort = null; + + #[Groups(['mail_settings:read', 'mail_settings:write'])] + public ?string $imapEncryption = null; + + #[Groups(['mail_settings:read', 'mail_settings:write'])] + public ?string $smtpHost = null; + + #[Groups(['mail_settings:read', 'mail_settings:write'])] + public ?int $smtpPort = null; + + #[Groups(['mail_settings:read', 'mail_settings:write'])] + public ?string $smtpEncryption = null; + + #[Groups(['mail_settings:read', 'mail_settings:write'])] + public ?string $username = null; + + #[Groups(['mail_settings:write'])] + public ?string $password = null; + + #[Groups(['mail_settings:read', 'mail_settings:write'])] + public ?string $sentFolderPath = null; + + #[Groups(['mail_settings:read', 'mail_settings:write'])] + public bool $enabled = false; + + #[Groups(['mail_settings:read'])] + public bool $hasPassword = false; +} diff --git a/src/State/Mail/MailSettingsProcessor.php b/src/State/Mail/MailSettingsProcessor.php new file mode 100644 index 0000000..9b7a7df --- /dev/null +++ b/src/State/Mail/MailSettingsProcessor.php @@ -0,0 +1,83 @@ +configRepository->findSingleton(); + if (null === $config) { + $config = new MailConfiguration(); + } + + if (null !== $data->protocol) { + $config->setProtocol($data->protocol); + } + if (null !== $data->imapHost) { + $config->setImapHost($data->imapHost); + } + if (null !== $data->imapPort) { + $config->setImapPort($data->imapPort); + } + if (null !== $data->imapEncryption) { + $config->setImapEncryption($data->imapEncryption); + } + if (null !== $data->smtpHost) { + $config->setSmtpHost($data->smtpHost); + } + if (null !== $data->smtpPort) { + $config->setSmtpPort($data->smtpPort); + } + if (null !== $data->smtpEncryption) { + $config->setSmtpEncryption($data->smtpEncryption); + } + if (null !== $data->username) { + $config->setUsername($data->username); + } + if (null !== $data->sentFolderPath) { + $config->setSentFolderPath($data->sentFolderPath); + } + $config->setEnabled($data->enabled); + + if (null !== $data->password && '' !== $data->password) { + $config->setEncryptedPassword($this->tokenEncryptor->encrypt($data->password)); + } + + $this->em->persist($config); + $this->em->flush(); + + $result = new MailSettings(); + $result->protocol = $config->getProtocol(); + $result->imapHost = $config->getImapHost(); + $result->imapPort = $config->getImapPort(); + $result->imapEncryption = $config->getImapEncryption(); + $result->smtpHost = $config->getSmtpHost(); + $result->smtpPort = $config->getSmtpPort(); + $result->smtpEncryption = $config->getSmtpEncryption(); + $result->username = $config->getUsername(); + $result->sentFolderPath = $config->getSentFolderPath(); + $result->enabled = $config->isEnabled(); + $result->hasPassword = $config->hasPassword(); + + return $result; + } +} diff --git a/src/State/Mail/MailSettingsProvider.php b/src/State/Mail/MailSettingsProvider.php new file mode 100644 index 0000000..9a60292 --- /dev/null +++ b/src/State/Mail/MailSettingsProvider.php @@ -0,0 +1,39 @@ +configRepository->findSingleton(); + $dto = new MailSettings(); + + if (null !== $config) { + $dto->protocol = $config->getProtocol(); + $dto->imapHost = $config->getImapHost(); + $dto->imapPort = $config->getImapPort(); + $dto->imapEncryption = $config->getImapEncryption(); + $dto->smtpHost = $config->getSmtpHost(); + $dto->smtpPort = $config->getSmtpPort(); + $dto->smtpEncryption = $config->getSmtpEncryption(); + $dto->username = $config->getUsername(); + $dto->sentFolderPath = $config->getSentFolderPath(); + $dto->enabled = $config->isEnabled(); + $dto->hasPassword = $config->hasPassword(); + } + + return $dto; + } +} diff --git a/tests/Functional/Controller/Mail/MailSettingsControllerTest.php b/tests/Functional/Controller/Mail/MailSettingsControllerTest.php new file mode 100644 index 0000000..63c1d0c --- /dev/null +++ b/tests/Functional/Controller/Mail/MailSettingsControllerTest.php @@ -0,0 +1,121 @@ +request('GET', '/api/mail/configuration'); + + self::assertResponseStatusCodeSame(401); + } + + public function testGetConfigurationReturns403ForRoleUser(): void + { + $client = static::createClient(); + $container = static::getContainer(); + $em = $container->get('doctrine.orm.entity_manager'); + + $user = $em->getRepository(User::class)->findOneBy(['username' => 'alice']); + $client->loginUser($user); + $client->request('GET', '/api/mail/configuration'); + + self::assertResponseStatusCodeSame(403); + } + + public function testGetConfigurationReturns200ForAdmin(): void + { + $client = static::createClient(); + $container = static::getContainer(); + $em = $container->get('doctrine.orm.entity_manager'); + + $admin = $em->getRepository(User::class)->findOneBy(['username' => 'admin']); + $client->loginUser($admin); + $client->request('GET', '/api/mail/configuration'); + + self::assertResponseIsSuccessful(); + $data = json_decode($client->getResponse()->getContent(), true); + + self::assertArrayNotHasKey('password', $data); + self::assertArrayNotHasKey('encryptedPassword', $data); + self::assertArrayHasKey('hasPassword', $data); + self::assertArrayHasKey('imapHost', $data); + self::assertArrayHasKey('enabled', $data); + } + + public function testPatchConfigurationReturns403ForRoleUser(): void + { + $client = static::createClient(); + $container = static::getContainer(); + $em = $container->get('doctrine.orm.entity_manager'); + + $user = $em->getRepository(User::class)->findOneBy(['username' => 'alice']); + $client->loginUser($user); + $client->request( + 'PATCH', + '/api/mail/configuration', + [], + [], + ['CONTENT_TYPE' => 'application/merge-patch+json'], + json_encode(['enabled' => false]) + ); + + self::assertResponseStatusCodeSame(403); + } + + public function testPatchConfigurationUpdatesFieldsForAdmin(): void + { + $client = static::createClient(); + $container = static::getContainer(); + $em = $container->get('doctrine.orm.entity_manager'); + + $admin = $em->getRepository(User::class)->findOneBy(['username' => 'admin']); + $client->loginUser($admin); + $client->request( + 'PATCH', + '/api/mail/configuration', + [], + [], + ['CONTENT_TYPE' => 'application/merge-patch+json'], + json_encode(['imapHost' => 'imap.example.com', 'enabled' => false]) + ); + + self::assertResponseIsSuccessful(); + $data = json_decode($client->getResponse()->getContent(), true); + self::assertSame('imap.example.com', $data['imapHost']); + self::assertArrayNotHasKey('password', $data); + } + + public function testPatchConfigurationWithPasswordEncryptsIt(): void + { + $client = static::createClient(); + $container = static::getContainer(); + $em = $container->get('doctrine.orm.entity_manager'); + + $admin = $em->getRepository(User::class)->findOneBy(['username' => 'admin']); + $client->loginUser($admin); + $client->request( + 'PATCH', + '/api/mail/configuration', + [], + [], + ['CONTENT_TYPE' => 'application/merge-patch+json'], + json_encode(['password' => 'secret123']) + ); + + self::assertResponseIsSuccessful(); + $data = json_decode($client->getResponse()->getContent(), true); + self::assertTrue($data['hasPassword']); + self::assertArrayNotHasKey('password', $data); + } +}