Plesk-Sync: del/add-Diff statt nicht-existierendem set:

Follow-up zu a83358b/24e152b. plesk bin mail --help auf Prod zeigt:
- -forwarding-addresses akzeptiert NUR add: und del:, kein set:
  → unser set:-Befehl wurde silent verworfen, Sync hatte nie
  Wirkung.
- -mailgroup als Option existiert gar nicht. Plesk nutzt -forwarding
  als Mailgroup-Schalter (im --info als "Mailgroup:" ausgegeben, im
  CLI als "-forwarding" gesetzt). Mein vorheriges -mailgroup false
  triggerte "Unrecognized option".

updateForwardTargets jetzt:
1. Aktuelle Members aus emailExists holen
2. Diff: toRemove = current \ targets, toAdd = targets \ current
   (case-insensitive)
3. Wenn toRemove: --update -forwarding-addresses del:<liste>
4. Wenn toAdd:    --update -forwarding true -forwarding-addresses add:<liste>

Idempotent, weil add/del Duplikate bzw. nicht-existente ignorieren.

Smoke-Test mit Prod-Stand (3 Bestands-Members + 1 neuer Eintrag):
nichts entfernt, nur bzirks@gmx.de hinzugefügt.
This commit is contained in:
2026-06-18 18:16:57 +02:00
parent 2becf6cb6a
commit 194c86409f
2 changed files with 71 additions and 27 deletions
@@ -497,36 +497,63 @@ export class PleskEmailProvider implements IEmailProvider {
};
}
// Plesk CLI API: Weiterleitungsziele aktualisieren.
// Format für -forwarding-addresses: "set:email1,email2" ersetzt alle Adressen.
// Plesk-CLI-Eigenheit: `-forwarding-addresses` akzeptiert NUR
// `add:` und `del:`, KEIN `set:`. Und `-forwarding` ist der
// Mailgroup-Schalter (Plesk nennt das im `--info` "Mailgroup",
// im CLI "forwarding" derselbe Mechanismus, doppelt benannt).
// Es gibt KEINE separaten Mailgroup-Optionen wie `-mailgroup`.
//
// WICHTIG: Mailgroup parallel deaktivieren. Plesk hat zwei
// unabhängige Verteil-Mechanismen (Mailgroup vs. Forwarding).
// Alt-Anlagen liefen oft via Mailgroup unser `set:`-Befehl auf
// forwarding-addresses ändert dann eine ungenutzte Tabelle und
// Mails landen weiterhin bei den Mailgroup-Members.
// Der Service-Layer importiert vorher die Mailgroup-Members in die
// `targets`-Liste, damit beim Umschalten nichts verloren geht.
const cliParams = [
'--update', email,
'-mailgroup', 'false',
'-forwarding', 'true',
'-forwarding-addresses', `set:${targets.join(',')}`,
];
console.log('[Plesk updateForwardTargets] CLI params:', cliParams);
const result = await this.request<{ code: number; stdout: string; stderr: string }>(
'POST',
'/api/v2/cli/mail/call',
{ params: cliParams },
);
console.log('[Plesk updateForwardTargets] response:', JSON.stringify(result, null, 2));
// Wir bauen daher den Diff: alte Member abrufen, dann
// del:<entfernt> + add:<neu> in zwei separaten Calls. Idempotent,
// weil add: Duplikate ignoriert und del: nicht-vorhandene auch.
const currentMembers = exists.mailgroupMembers ?? [];
const targetsLower = new Set(targets.map((t) => t.toLowerCase()));
const currentLower = new Set(currentMembers.map((m) => m.toLowerCase()));
const toRemove = currentMembers.filter((m) => !targetsLower.has(m.toLowerCase()));
const toAdd = targets.filter((t) => !currentLower.has(t.toLowerCase()));
if (result.code !== 0 || /error|failed/i.test(result.stderr || '')) {
console.log(
`[Plesk updateForwardTargets] ${email} aktuell: [${currentMembers.join(', ')}], ` +
`soll: [${targets.join(', ')}], entfernen: [${toRemove.join(', ')}], hinzufügen: [${toAdd.join(', ')}]`,
);
// Entfernen-Schritt
if (toRemove.length > 0) {
const delParams = [
'--update', email,
'-forwarding-addresses', `del:${toRemove.join(',')}`,
];
const delResult = await this.request<{ code: number; stdout: string; stderr: string }>(
'POST', '/api/v2/cli/mail/call', { params: delParams },
);
console.log('[Plesk updateForwardTargets] del response:', JSON.stringify(delResult, null, 2));
if (delResult.code !== 0 || /error|failed/i.test(delResult.stderr || '')) {
return {
success: false,
error: result.stderr?.trim() || result.stdout?.trim() || `Plesk returned code ${result.code}`,
error: delResult.stderr?.trim() || delResult.stdout?.trim() || `Plesk del returned code ${delResult.code}`,
};
}
}
// Hinzufügen-Schritt (impliziert -forwarding true, damit Mailgroup
// aktiviert bleibt bzw. wird).
if (toAdd.length > 0) {
const addParams = [
'--update', email,
'-forwarding', 'true',
'-forwarding-addresses', `add:${toAdd.join(',')}`,
];
const addResult = await this.request<{ code: number; stdout: string; stderr: string }>(
'POST', '/api/v2/cli/mail/call', { params: addParams },
);
console.log('[Plesk updateForwardTargets] add response:', JSON.stringify(addResult, null, 2));
if (addResult.code !== 0 || /error|failed/i.test(addResult.stderr || '')) {
return {
success: false,
error: addResult.stderr?.trim() || addResult.stdout?.trim() || `Plesk add returned code ${addResult.code}`,
};
}
}
return {
success: true,
+17
View File
@@ -97,6 +97,23 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
## ✅ Erledigt
- [x] **🐞 Plesk-Sync: `-forwarding-addresses set:` existiert gar nicht**
- Folge-Bug nach `a83358b`/`24e152b`: Sync verändert Plesk weiterhin
nicht. `plesk bin mail --help` zeigt: `-forwarding-addresses`
akzeptiert ausschließlich `add:` und `del:` unser `set:` wurde
von Plesk silent verworfen. Außerdem gibt es keine separate
`-mailgroup`-Option; was Plesk im `--info` als `Mailgroup: true`
zeigt, ist genau das, was `-forwarding true` in der CLI setzt
(doppelt benannt). Mein vorheriges `-mailgroup false` lief auf
den Phantom-Parameter und triggerte `Unrecognized option`.
- `updateForwardTargets` baut jetzt den Diff: aktuelle Mailgroup-
Members (aus `emailExists`) gegen Soll-Liste; `del:<entfernt>` +
`add:<neu>` in zwei separaten CLI-Calls. Idempotent.
Case-insensitive `Bruns.Gerhard``bruns.gerhard`.
- Phantom-`-mailgroup`-Parameter entfernt.
- Smoke-Test gegen Prod-Stand (3 Bestands-Members + 1 neuer Eintrag):
nichts entfernt, nur `bzirks@gmx.de` hinzugefügt.
- [x] **🔒 Pentest 83.1-83.3: Auto-Import-Pfad härten**
- **83.1 MEDIUM:** Auto-Import in `syncForwardingForEmail` umging
`assertValidForwardingEmail`. Plesk-Member wie `attacker@plesk.internal`