diff --git a/app/Services/Mail/GuestMailConfig.php b/app/Services/Mail/GuestMailConfig.php new file mode 100644 index 0000000..e4af9a5 --- /dev/null +++ b/app/Services/Mail/GuestMailConfig.php @@ -0,0 +1,105 @@ + */ + private readonly array $values, + private readonly string $password, + ) {} + + public static function for(Instance $instance): self + { + $host = trim((string) Settings::get('mail.host', '')); + $port = (int) Settings::get('mail.port', 0); + + if ($host === '' || $port < 1) { + return new self('no_server', [], ''); + } + + $box = self::mailboxFor($instance); + + if ($box === null || ! $box->isConfigured()) { + return new self('no_mailbox', [], ''); + } + + // Die Adresse zerfaellt in die beiden Werte, die Nextcloud getrennt + // fuehrt: den linken Teil als Absender, den rechten als Maildomain. + [$local, $domain] = array_pad(explode('@', $box->address, 2), 2, ''); + + return new self(null, [ + 'mail_smtpmode' => 'smtp', + 'mail_smtphost' => $host, + 'mail_smtpport' => (string) $port, + 'mail_smtpsecure' => (string) Settings::get('mail.encryption', 'tls'), + 'mail_smtpauth' => 'true', + 'mail_smtpname' => $box->smtpUsername(), + 'mail_from_address' => $local, + 'mail_domain' => $domain, + ], (string) $box->password); + } + + /** + * Heute fuer jede Instanz dasselbe Konto. Siehe Klassenkopf — hier haengt + * die spaetere Fassung mit einem Konto je Kunde. + */ + private static function mailboxFor(Instance $instance): ?Mailbox + { + return Mailbox::findByKey(self::RELAY_KEY); + } + + public function available(): bool + { + return $this->problem === null; + } + + /** `no_server`, `no_mailbox` oder null. */ + public function problem(): ?string + { + return $this->problem; + } + + /** + * Alles ausser dem Passwort. Das geht einen eigenen Weg, damit es beim + * Bauen der Befehle nicht versehentlich mitwandert. + * + * @return array + */ + public function values(): array + { + return $this->values; + } + + public function password(): string + { + return $this->password; + } +} diff --git a/tests/Feature/Mail/GuestMailConfigTest.php b/tests/Feature/Mail/GuestMailConfigTest.php new file mode 100644 index 0000000..630904e --- /dev/null +++ b/tests/Feature/Mail/GuestMailConfigTest.php @@ -0,0 +1,81 @@ +create([ + 'key' => 'instance-relay', + 'address' => 'noreply@clupilot.cloud', + 'username' => 'noreply@clupilot.cloud', + 'password' => 'geheim', + 'active' => true, + 'authenticates' => true, + ]); +} + +it('baut die Werte aus Server UND Postfach zusammen', function () { + versandbereit(); + // Instance kennt kein 'name'-Feld (siehe Migration) — GuestMailConfig + // liest ohnehin nur das Postfach, nicht die Instanz selbst. + $instance = Instance::factory()->create(); + + $config = GuestMailConfig::for($instance); + + expect($config->available())->toBeTrue() + ->and($config->values())->toMatchArray([ + 'mail_smtpmode' => 'smtp', + 'mail_smtphost' => 'mail.clupilot.cloud', + 'mail_smtpport' => '587', + 'mail_smtpsecure' => 'tls', + 'mail_smtpauth' => 'true', + 'mail_smtpname' => 'noreply@clupilot.cloud', + 'mail_from_address' => 'noreply', + 'mail_domain' => 'clupilot.cloud', + ]) + ->and($config->password())->toBe('geheim'); +}); + +it('traegt das Passwort NICHT unter values()', function () { + // values() wandert in Befehle. Das Passwort geht einen eigenen Weg, damit + // niemand es versehentlich mit den uebrigen Werten mitschleift. + versandbereit(); + + expect(GuestMailConfig::for(Instance::factory()->create())->values()) + ->not->toHaveKey('mail_smtppassword'); +}); + +it('sagt ohne Mailserver, dass nichts geschrieben werden darf', function () { + Mailbox::factory()->create(['key' => 'instance-relay', 'address' => 'noreply@clupilot.cloud', 'password' => 'geheim', 'active' => true]); + Settings::set('mail.host', ''); + + $config = GuestMailConfig::for(Instance::factory()->create()); + + expect($config->available())->toBeFalse() + ->and($config->problem())->toBe('no_server'); +}); + +it('sagt ohne Absenderpostfach, dass nichts geschrieben werden darf', function () { + Settings::set('mail.host', 'mail.clupilot.cloud'); + Settings::set('mail.port', 587); + + $config = GuestMailConfig::for(Instance::factory()->create()); + + expect($config->available())->toBeFalse() + ->and($config->problem())->toBe('no_mailbox'); +}); + +it('weist ein Postfach ohne Zugangsdaten ab', function () { + Settings::set('mail.host', 'mail.clupilot.cloud'); + Settings::set('mail.port', 587); + Mailbox::factory()->create(['key' => 'instance-relay', 'address' => 'noreply@clupilot.cloud', 'password' => null, 'active' => true, 'authenticates' => true]); + + expect(GuestMailConfig::for(Instance::factory()->create())->problem())->toBe('no_mailbox'); +});