139 lines
5.9 KiB
PHP
139 lines
5.9 KiB
PHP
<?php
|
|
|
|
namespace App\Provisioning\Steps\Customer;
|
|
|
|
use App\Models\ProvisioningRun;
|
|
use App\Provisioning\StepResult;
|
|
use App\Services\Mail\GuestMailConfig;
|
|
use App\Services\Proxmox\ProxmoxClient;
|
|
use App\Support\NextcloudOcc;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
/**
|
|
* Bringt der Nextcloud eines Kunden bei, Mail zu verschicken.
|
|
*
|
|
* Bis hierher konnte sie es NICHT — weder die Bereitstellung noch die
|
|
* Compose-Datei der Vorlage setzten je einen Versand. Damit verschickte jede
|
|
* Kundeninstanz gar nichts: keine Freigabe-Benachrichtigung, keine
|
|
* Aktivitaetsmeldung, kein "Passwort vergessen". Still, ohne Fehlermeldung.
|
|
*
|
|
* Das ist die Voraussetzung fuer die Mitarbeiterverwaltung: die Einladung an
|
|
* einen Mitarbeiter verschickt NEXTCLOUD, nicht CluPilot — nur so entsteht
|
|
* das Passwort dort, wo niemand sonst es zu sehen bekommt.
|
|
*
|
|
* Ohne Server oder Absenderpostfach wird NICHTS geschrieben. Eine halb
|
|
* eingetragene Mailkonfiguration waere schlimmer als keine.
|
|
*
|
|
* Das ist aber KEIN Grund, den Lauf scheitern zu lassen: dieser Schritt steht
|
|
* in der PFLICHT-Pipeline `customer`, und ein Kunde, der bezahlt hat, bekommt
|
|
* seine Cloud auch dann, wenn der Mailversand noch nicht eingerichtet ist —
|
|
* genau wie RegisterMonitoring die Bereitstellung nie an einer nicht
|
|
* erreichbaren Ueberwachung scheitern laesst (siehe deren Kopfkommentar). Der
|
|
* Schritt geht in diesem Fall mit `advance()` weiter; nachgeholt wird es ueber
|
|
* `clupilot:configure-instance-mail` (Aufgabe 4), sobald Server und Postfach
|
|
* stehen.
|
|
*
|
|
* Die Rueckmeldung dazu geht an ZWEI Stellen, nicht nur einer — genau wie bei
|
|
* RegisterMonitoring. Ein Lauf-Ereignis (`outcome: info`) haengt sich an den
|
|
* Lauf, den der Betreiber in der Konsole sieht; `Log::warning` bleibt daneben
|
|
* fuer die Nachschau ausserhalb der Konsole. Das Lauf-Ereignis ist hier nicht
|
|
* optional: solange der Mailserver noch nicht steht, tritt dieser Zweig bei
|
|
* JEDER einzelnen Bestellung ein, und die einzige Rueckmeldung darf dann
|
|
* nicht in einer Logdatei verschwinden, in die niemand schaut. Mailversand
|
|
* ist eine Nebenfunktion: fehlt er, funktioniert die Cloud vollstaendig, nur
|
|
* Einladungen und Freigabe-Benachrichtigungen gehen nicht.
|
|
*
|
|
* Nicht eingerichtet ist etwas anderes als kaputt: sobald Server und Postfach
|
|
* da sind, wird geschrieben, und ein Fehler beim Schreiben selbst (occ liefert
|
|
* einen Fehlercode, der Gast antwortet nicht) bleibt ein Fehlschlag wie jeder
|
|
* andere Gastbefehl in dieser Pipeline — CustomerStep::guest() wirft dafuer,
|
|
* und der Runner macht daraus einen Retry.
|
|
*
|
|
* WO DAS SMTP-PASSWORT UEBERALL SICHTBAR IST — ehrlich aufgeschrieben, weil
|
|
* ein erster Entwurf es zu verstecken versuchte und das Verstecken nichts
|
|
* brachte:
|
|
*
|
|
* - im Argv des `/bin/sh -c`, das der Gastagent fuer diesen Aufruf startet
|
|
* (guestExec baut die ganze Zeile so). Debians `dash` behaelt diesen
|
|
* Prozess fuer die volle Dauer des Aufrufs;
|
|
* - im Argv von `php occ` im Container;
|
|
* - und DAUERHAFT im Klartext in /opt/nextcloud … config/config.php, wo
|
|
* Nextcloud es selbst ablegt.
|
|
*
|
|
* Der dritte Punkt erledigt die ersten beiden: wer die Datei lesen kann, muss
|
|
* gar nicht erst in eine Prozessliste schauen. Ein Versteck, das dieselbe
|
|
* Person nicht aussperrt, ist keines — und ein Kommentar, der eines
|
|
* behauptet, haelt den Naechsten vom Nachsehen ab.
|
|
*
|
|
* Die Eingrenzung liegt deshalb in der Infrastruktur, nicht hier: das
|
|
* Versandkonto kann nur senden, der Versandport nimmt nur die eigenen
|
|
* Hostadressen an, und der Mailserver drosselt je Konto. Was hier gilt und
|
|
* geprueft wird, ist etwas anderes: der Wert wird maskiert, damit er keinen
|
|
* zweiten Befehl starten kann.
|
|
*/
|
|
class ConfigureInstanceMail extends CustomerStep
|
|
{
|
|
public function __construct(private ProxmoxClient $pve) {}
|
|
|
|
public function key(): string
|
|
{
|
|
return 'configure_instance_mail';
|
|
}
|
|
|
|
public function maxDuration(): int
|
|
{
|
|
return 120;
|
|
}
|
|
|
|
public function execute(ProvisioningRun $run): StepResult
|
|
{
|
|
$instance = $this->instance($run);
|
|
|
|
if ($instance === null) {
|
|
return StepResult::fail('instance_missing');
|
|
}
|
|
|
|
$config = GuestMailConfig::for($instance);
|
|
|
|
if (! $config->available()) {
|
|
// Nicht eingerichtet, nicht kaputt — siehe Kopfkommentar. Die Cloud
|
|
// wird trotzdem ausgeliefert. available() === false heisst hier
|
|
// immer, dass problem() einen Grund liefert (siehe GuestMailConfig)
|
|
// — kein Rueckfall auf einen Fall, der nicht eintreten kann.
|
|
$grund = $config->problem();
|
|
|
|
$run->events()->create([
|
|
'step' => $this->key(),
|
|
'attempt' => $run->attempt,
|
|
'outcome' => 'info',
|
|
'message' => 'Mailversand uebersprungen (nicht eingerichtet): '.$grund,
|
|
]);
|
|
Log::warning('Mailversand fuer Instanz uebersprungen (nicht eingerichtet)', [
|
|
'instance' => $instance->uuid,
|
|
'reason' => $grund,
|
|
]);
|
|
|
|
return StepResult::advance();
|
|
}
|
|
|
|
$pve = $this->pve->forHost($instance->host);
|
|
|
|
// Der Schluessel ist ein fester occ-Name aus GuestMailConfig::values(),
|
|
// keine Benutzereingabe — genauso unmaskiert wie 'files default_quota'
|
|
// in ApplyStorageQuota. Maskiert wird nur der WERT, siehe Kopfkommentar.
|
|
foreach ($config->values() as $schluessel => $wert) {
|
|
$this->guest($pve, $run, NextcloudOcc::command(
|
|
'config:system:set '.$schluessel.' --value='.escapeshellarg($wert)
|
|
));
|
|
}
|
|
|
|
// Das Passwort wie jeder andere Wert. Verstecken laesst es sich auf
|
|
// dieser Maschine nicht — siehe Kopfkommentar.
|
|
$this->guest($pve, $run, NextcloudOcc::command(
|
|
'config:system:set mail_smtppassword --value='.escapeshellarg($config->password())
|
|
));
|
|
|
|
return StepResult::advance();
|
|
}
|
|
}
|