diff --git a/apps/settings/lib/Controller/MailSettingsController.php b/apps/settings/lib/Controller/MailSettingsController.php index 95e60c72496..c44c0fc45a8 100644 --- a/apps/settings/lib/Controller/MailSettingsController.php +++ b/apps/settings/lib/Controller/MailSettingsController.php @@ -59,6 +59,7 @@ class MailSettingsController extends Controller { ?bool $mail_smtpauth, string $mail_smtpport, string $mail_sendmailmode, + ?bool $mail_noverify = null, ): DataResponse { $mail_smtpauth = $mail_smtpauth == '1'; @@ -76,6 +77,15 @@ class MailSettingsController extends Controller { $configs[$key] = empty($value) ? null : $value; } + if ($mail_noverify !== null) { + $options = $this->config->getSystemValue('mail_smtpstreamoptions', []); + $options['ssl'] ??= []; + $options['ssl']['allow_self_signed'] = $mail_noverify; + $options['ssl']['verify_peer'] = !$mail_noverify; + $options['ssl']['verify_peer_name'] = !$mail_noverify; + $configs['mail_smtpstreamoptions'] = $options; + } + // Delete passwords from config in case no auth is specified if (!$mail_smtpauth) { $configs['mail_smtpname'] = null; diff --git a/apps/settings/lib/Settings/Admin/Mail.php b/apps/settings/lib/Settings/Admin/Mail.php index 92dc9a340e4..e76ca9b8214 100644 --- a/apps/settings/lib/Settings/Admin/Mail.php +++ b/apps/settings/lib/Settings/Admin/Mail.php @@ -17,7 +17,7 @@ use OCP\Settings\IDelegatedSettings; use OCP\Util; class Mail implements IDelegatedSettings { - + public function __construct( private IConfig $config, private IL10N $l, @@ -67,6 +67,7 @@ class Mail implements IDelegatedSettings { $smtpMode = 'smtp'; } + $smtpOptions = $this->config->getSystemValue('mail_smtpstreamoptions', []); $this->initialState->provideInitialState('settingsAdminMailConfig', [ 'mail_domain' => $this->config->getSystemValue('mail_domain', ''), 'mail_from_address' => $this->config->getSystemValue('mail_from_address', ''), @@ -78,6 +79,8 @@ class Mail implements IDelegatedSettings { 'mail_smtpname' => $this->config->getSystemValue('mail_smtpname', ''), 'mail_smtppassword' => $smtpPassword, 'mail_sendmailmode' => $this->config->getSystemValue('mail_sendmailmode', 'smtp'), + + 'mail_noverify' => $smtpOptions['ssl']['allow_self_signed'] ?? false, ]); Util::addScript('settings', 'vue-settings-admin-mail'); diff --git a/apps/settings/src/views/AdminSettingsMailServer.vue b/apps/settings/src/views/AdminSettingsMailServer.vue index 713832b88f7..74b7e794c49 100644 --- a/apps/settings/src/views/AdminSettingsMailServer.vue +++ b/apps/settings/src/views/AdminSettingsMailServer.vue @@ -39,6 +39,8 @@ const initialConfig = loadState<{ mail_smtpname: string mail_smtppassword: string mail_sendmailmode: string + + mail_noverify: boolean }>('settings', 'settingsAdminMailConfig') const mailConfig = ref({ ...initialConfig }) @@ -159,6 +161,10 @@ async function onSubmit() { :input-label="t('settings', 'Sendmail mode')" :options="settingsAdminMail.smtpSendmailModeOptions" required /> + + + {{ t('settings', 'Disable certificate verification (insecure)') }} + diff --git a/apps/settings/tests/Controller/MailSettingsControllerTest.php b/apps/settings/tests/Controller/MailSettingsControllerTest.php index f162b4da017..6064e76058a 100644 --- a/apps/settings/tests/Controller/MailSettingsControllerTest.php +++ b/apps/settings/tests/Controller/MailSettingsControllerTest.php @@ -95,7 +95,7 @@ class MailSettingsControllerTest extends \Test\TestCase { 'smtp', 'ssl', 'mx.nextcloud.org', - '1', + true, '25', 'smtp' ); @@ -108,7 +108,7 @@ class MailSettingsControllerTest extends \Test\TestCase { 'smtp', 'ssl', 'mx.nextcloud.org', - '0', + false, '25', 'smtp' ); @@ -116,16 +116,30 @@ class MailSettingsControllerTest extends \Test\TestCase { } public function testStoreCredentials(): void { + $calls = []; $this->config - ->expects($this->once()) - ->method('setSystemValues') - ->with([ - 'mail_smtpname' => 'UsernameToStore', - 'mail_smtppassword' => 'PasswordToStore', - ]); + ->expects($this->exactly(2)) + ->method('setSystemValue') + ->willReturnCallback(function (string $key, ?string $value) use (&$calls): void { + $calls[] = [$key, $value]; + }); $response = $this->mailController->storeCredentials('UsernameToStore', 'PasswordToStore'); $this->assertSame(Http::STATUS_OK, $response->getStatus()); + self::assertEqualsCanonicalizing([ + ['mail_smtpname', 'UsernameToStore'], + ['mail_smtppassword', 'PasswordToStore'], + ], $calls); + } + + public function testStoreCredentialsWithoutPassword(): void { + $this->config + ->expects($this->exactly(1)) + ->method('setSystemValue') + ->with('mail_smtpname', 'UsernameToStore'); + + $response = $this->mailController->storeCredentials('UsernameToStore', null); + $this->assertSame(Http::STATUS_OK, $response->getStatus()); } public function testSendTestMail(): void { diff --git a/apps/settings/tests/Settings/Admin/MailTest.php b/apps/settings/tests/Settings/Admin/MailTest.php index 992c7d43dba..216a42c340b 100644 --- a/apps/settings/tests/Settings/Admin/MailTest.php +++ b/apps/settings/tests/Settings/Admin/MailTest.php @@ -8,9 +8,11 @@ namespace OCA\Settings\Tests\Settings\Admin; use OCA\Settings\Settings\Admin\Mail; use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Services\IInitialState; use OCP\IBinaryFinder; use OCP\IConfig; use OCP\IL10N; +use OCP\IURLGenerator; use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; @@ -19,15 +21,22 @@ class MailTest extends TestCase { private Mail $admin; private IConfig&MockObject $config; private IL10N&MockObject $l10n; + private IURLGenerator&MockObject $urlGenerator; + private IInitialState&MockObject $initialState; protected function setUp(): void { parent::setUp(); $this->config = $this->createMock(IConfig::class); $this->l10n = $this->createMock(IL10N::class); + $this->l10n->method('t')->willReturnArgument(0); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->initialState = $this->createMock(IInitialState::class); $this->admin = new Mail( $this->config, - $this->l10n + $this->l10n, + $this->initialState, + $this->urlGenerator, ); } @@ -41,10 +50,12 @@ class MailTest extends TestCase { #[\PHPUnit\Framework\Attributes\DataProvider('dataGetForm')] public function testGetForm(bool $sendmail) { $finder = $this->createMock(IBinaryFinder::class); - $finder->expects(self::once()) + $finder->expects(self::atLeastOnce()) ->method('findBinaryPath') - ->with('sendmail') - ->willReturn($sendmail ? '/usr/bin/sendmail': false); + ->willReturnMap([ + ['qmail', false], + ['sendmail', $sendmail ? '/usr/bin/sendmail' : false], + ]); $this->overwriteService(IBinaryFinder::class, $finder); $this->config @@ -63,11 +74,37 @@ class MailTest extends TestCase { ['mail_sendmailmode', 'smtp', 'smtp'], ]); + $initialState = []; + $this->initialState->method('provideInitialState') + ->willReturnCallback(function (string $key, array $data) use (&$initialState): void { + $initialState[$key] = $data; + }); + $expected = new TemplateResponse( 'settings', 'settings/admin/additional-mail', - [ - 'sendmail_is_available' => $sendmail, + renderAs: '' + ); + + $this->assertEquals($expected, $this->admin->getForm()); + self::assertEquals([ + 'settingsAdminMail' => [ + 'configIsReadonly' => false, + 'docUrl' => '', + 'smtpModeOptions' => [ + ['label' => 'SMTP', 'id' => 'smtp'], + ...($sendmail ? [['label' => 'Sendmail', 'id' => 'sendmail']] : []) + ], + 'smtpEncryptionOptions' => [ + ['label' => 'None / STARTTLS', 'id' => ''], + ['label' => 'SSL/TLS', 'id' => 'ssl'], + ], + 'smtpSendmailModeOptions' => [ + ['label' => 'smtp (-bs)', 'id' => 'smtp'], + ['label' => 'pipe (-t -i)', 'id' => 'pipe'], + ], + ], + 'settingsAdminMailConfig' => [ 'mail_domain' => 'mx.nextcloud.com', 'mail_from_address' => 'no-reply@nextcloud.com', 'mail_smtpmode' => 'smtp', @@ -78,11 +115,9 @@ class MailTest extends TestCase { 'mail_smtpname' => 'smtp.sender.com', 'mail_smtppassword' => '********', 'mail_sendmailmode' => 'smtp', + 'mail_noverify' => false, ], - '' - ); - - $this->assertEquals($expected, $this->admin->getForm()); + ], $initialState); } public function testGetSection(): void {