fix(kerio): korrekte Admin-API gemäß Delivery.idl + Pop3Account-Doku

- Methoden: Delivery.getPop3AccountList / addPop3AccountList /
  setPop3Account (vorher geraten als Pop3Accounts.set/.create →
  Method not found).
- Pop3Account-Felder mit den richtigen Namen: isActive (statt enabled),
  mode (statt sslMode), authentication (statt authType), und
  leaveOnServer.removeAfterPeriod als OptionalLong-Wrapper.
  Falsche Namen wurden von Kerio still ignoriert → Sammler war inaktiv.
- User-Struct: allowPasswordChange=false (statt mayChangePassword,
  das es nicht gibt). emailAddresses weggelassen, Kerio leitet die
  primäre Adresse aus loginName+domain ab.
- Kerio-Step in 2 Sub-Steps aufgeteilt: User (skip wenn vorhanden) +
  POP3 (upsert). Damit wird bei einem zweiten Lauf der Sammler nicht
  übersprungen, nur weil der User schon existiert.
- POP3-Sammler ist jetzt UPSERT: existierende werden via setPop3Account
  überschrieben → Selbstreparatur kaputter Einträge + Passwort-
  Änderungen aus der CSV ziehen sich von selbst nach.

GUI: 👁/🙈-Toggle pro Passwort-Feld (Klartext temporär einsehbar).

Filenames der Sammel-PDFs + Admin-Report ohne Zeitstempel –
erneuter Lauf überschreibt statt anzuhäufen.

README: Ablauf-Sektion + Idempotenz-Tabelle aktualisiert; Kerio-
Caveat ersetzt durch konkrete Methoden-/Feld-Liste mit Doku-Link.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-12 14:09:06 +02:00
parent 4711c55d89
commit 06d7e00e49
4 changed files with 235 additions and 86 deletions
+30 -21
View File
@@ -121,7 +121,8 @@ def _deploy_one(account: Account, cfg: Config, log: Callable[[str], None]) -> Ac
log(f" ✗ FEHLER: {e}")
# Wir machen mit Kerio/Nextcloud trotzdem weiter die sind unabhängig.
# 2) Kerio
# 2) Kerio User und POP3-Sammler werden GETRENNT geprüft, damit ein
# bereits angelegter User nicht den Sammler-Schritt überspringt.
log(f" → Kerio User+POP3-Sammler: {account.emailadresse} @ {account.keriohost}")
try:
kerio = KerioClient(
@@ -130,25 +131,34 @@ def _deploy_one(account: Account, cfg: Config, log: Callable[[str], None]) -> Ac
port=cfg.KERIO_PORT, verify=cfg.VERIFY_TLS,
)
try:
if kerio.user_exists(account.emailadresse):
result.steps.append(StepResult("Kerio", "übersprungen", "User existiert bereits"))
log(" · existiert bereits, POP3-Sammler nicht neu angelegt")
# 2a) User
uid = kerio.find_user_id(account.emailadresse)
if uid:
result.steps.append(StepResult(
"Kerio User", "übersprungen", "existiert bereits"))
log(" · User existiert bereits")
else:
uid = kerio.create_user(
account.emailadresse,
account.kerioemailkennwort,
account.vollname,
)
kerio.add_pop3_collection(
kerio_user_id=uid,
server=account.pleskhost,
login_name=account.emailadresse,
password=account.pleskemailkennwort,
port=cfg.POP3_PORT, ssl=cfg.POP3_SSL,
leave_days=cfg.POP3_KEEP_DAYS,
)
result.steps.append(StepResult("Kerio", "angelegt", "inkl. POP3-Sammler"))
log(" ✓ angelegt + POP3-Sammler")
result.steps.append(StepResult("Kerio User", "angelegt"))
log(" ✓ User angelegt")
# 2b) POP3-Sammler upsert (anlegen wenn neu, sonst aktualisieren).
# So werden auch alte / kaputt konfigurierte Sammler beim
# nächsten Lauf automatisch repariert.
pop_status = kerio.upsert_pop3_collection(
deliver_to_email=account.emailadresse,
server=account.pleskhost,
login_name=account.emailadresse,
password=account.pleskemailkennwort,
port=cfg.POP3_PORT, ssl=cfg.POP3_SSL,
leave_days=cfg.POP3_KEEP_DAYS,
)
result.steps.append(StepResult("Kerio POP3", pop_status))
log(f" ✓ POP3-Sammler {pop_status}")
finally:
kerio.logout()
except Exception as e:
@@ -233,9 +243,8 @@ def pdf_only(accounts: List[Account], cfg: Config,
write_user_minimal_pdf(per_min, acc, cfg.SMTP_PORT, cfg.IMAP_PORT)
log(f" ⤷ PDF: {per_pdf}")
log(f" ⤷ PDF (minimal): {per_min}")
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
combined = output_dir / f"zugangsdaten_gesamt_{timestamp}.pdf"
combined_min = output_dir / f"zugangsdaten_gesamt_minimal_{timestamp}.pdf"
combined = output_dir / "zugangsdaten_gesamt.pdf"
combined_min = output_dir / "zugangsdaten_gesamt_minimal.pdf"
write_combined_pdf(combined, accounts, cfg.SMTP_PORT, cfg.POP3_PORT, cfg.POP3_SSL)
write_combined_minimal_pdf(combined_min, accounts, cfg.SMTP_PORT, cfg.IMAP_PORT)
log(f"✓ Gesamt-PDF: {combined}")
@@ -261,15 +270,15 @@ def run_deploy(accounts: List[Account], cfg: Config,
log(f" ⤷ PDF: {per_pdf}")
log(f" ⤷ PDF (minimal): {per_min}")
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
combined = output_dir / f"zugangsdaten_gesamt_{timestamp}.pdf"
combined_min = output_dir / f"zugangsdaten_gesamt_minimal_{timestamp}.pdf"
combined = output_dir / "zugangsdaten_gesamt.pdf"
combined_min = output_dir / "zugangsdaten_gesamt_minimal.pdf"
write_combined_pdf(combined, accounts, cfg.SMTP_PORT, cfg.POP3_PORT, cfg.POP3_SSL)
write_combined_minimal_pdf(combined_min, accounts, cfg.SMTP_PORT, cfg.IMAP_PORT)
log(f"✓ Gesamt-PDF: {combined}")
log(f"✓ Gesamt-PDF (minimal): {combined_min}")
report = output_dir / f"_admin_report_{timestamp}.txt"
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
report = output_dir / "_admin_report.txt"
_write_admin_report(report, results, cfg, timestamp)
log(f"✓ Admin-Report (nicht für Kunden!): {report}")
return results, combined