diff --git a/README.md b/README.md index 814157f..925d1c0 100644 --- a/README.md +++ b/README.md @@ -49,9 +49,12 @@ Add-on-Einstellungen öffnen und ausfüllen: - **Verbindung testen** → lädt die Aktenschränke (Bestätigung, dass Login & URL passen) - optional **Standard-Aktenschrank** und Default-Optionen (eml/pdf/Anhänge) -> ⚠️ Das Passwort wird unverschlüsselt in `storage.local` gespeichert (v1). Für den -> Produktiveinsatz sollte auf den DocuWare Identity Service / OAuth umgestellt werden -> (`lib/auth.js` ist dafür gekapselt). +> 🔒 **Das Passwort wird nicht gespeichert.** Es wird beim ersten Ablegen pro +> Thunderbird-Sitzung einmal abgefragt, daraus ein OAuth-Token geholt und sofort +> verworfen. Nur der Token bleibt im Speicher des Hintergrundskripts (überlebt keinen +> Neustart). In `storage.local` liegen ausschließlich Server, Organisation, Benutzer +> und Standardwerte – keine Zugangsdaten. Auch der Einstellungs-Export enthält **kein** +> Passwort. ## Benutzung @@ -78,7 +81,7 @@ Das Add-on wird **nicht** öffentlich auf addons.thunderbird.net gelistet, sonde Gitea verteilt + aktualisiert. > Im XPI und in `updates.json` stehen **keine** Zugangsdaten. Das DocuWare-Passwort -> liegt ausschließlich lokal in `storage.local` und wird nie mitverteilt. +> wird ohnehin nirgends gespeichert (nur Sitzungs-Token im Speicher) und nie mitverteilt. **Einmalige Einrichtung:** @@ -102,7 +105,9 @@ neueste dort gelistete Version. ## Bekannte Grenzen / nächste Schritte - **PDF** ist v1 ein einfaches Text-PDF (Kopf + Klartext-Body) ohne HTML-Layout/Bilder. -- **Auth** nur Cookie-Logon; OAuth/Identity Service noch nicht implementiert. +- **Auth**: primär DocuWare Identity Service (OAuth, ROPC mit öffentlichem Client), + Fallback Cookie-Logon für alte On-Prem-Server. Passwort wird nicht gespeichert + (Token nur im RAM, 1× Abfrage pro Sitzung). - **Upload-Multipart-Format** ggf. je nach DocuWare-Version anpassen (`docuware.js → uploadDocument`). - Mehrfachauswahl von Mails: aktuell wird die erste markierte Mail abgelegt. diff --git a/background/background.js b/background/background.js index c1cbeaa..8a6a6e3 100644 --- a/background/background.js +++ b/background/background.js @@ -1,7 +1,43 @@ // Hintergrund-Skript: Button + Kontextmenü, öffnet den Ablage-Dialog. +// Zusätzlich Auth-Broker: hält den OAuth-Token nur im Speicher dieser TB-Sitzung +// (siehe auth.js). Das Passwort wird einmalig zum Login durchgereicht, daraus ein +// Token geholt und sofort verworfen – es wird NIE gespeichert. const DIALOG_URL = "dialog/dialog.html"; +// Auth-Broker: Dialog-/Optionsfenster fragen hier den Sitzungs-Token an bzw. +// melden sich mit dem Passwort an. So muss das Passwort nur 1x pro Sitzung getippt +// werden und liegt nirgends auf der Platte. +browser.runtime.onMessage.addListener((msg) => { + if (!msg || !msg.type) return undefined; + + if (msg.type === "auth:status") { + return Promise.resolve({ hasToken: Auth.hasValidToken(), token: Auth.currentToken() }); + } + + if (msg.type === "auth:logon") { + return (async () => { + try { + const settings = await Settings.get(); + if (!settings.serverUrl) throw new Error("Keine DocuWare-Server-URL konfiguriert."); + Auth.reset(); + const r = await Auth.logon({ ...settings, password: msg.password || "" }); + // Passwort wird hier verworfen – nur der Token bleibt im Speicher. + return { ok: true, mode: r.mode, token: Auth.currentToken() }; + } catch (e) { + return { ok: false, error: String((e && e.message) || e) }; + } + })(); + } + + if (msg.type === "auth:logout") { + Auth.reset(); + return Promise.resolve({ ok: true }); + } + + return undefined; +}); + function openDialog(messageIds) { const ids = Array.isArray(messageIds) ? messageIds : [messageIds]; // Pro Nachricht ein eigenes Ablagefenster (leicht versetzt gestapelt). diff --git a/dialog/dialog.css b/dialog/dialog.css index d569aa7..59840d4 100644 --- a/dialog/dialog.css +++ b/dialog/dialog.css @@ -62,3 +62,23 @@ footer { border-top: 1px solid #e5e7eb; padding: 10px 18px; background: #fafafa; .muted { color: #6b7280; font-size: 12px; } .err { color: #b91c1c; } .ok { color: #15803d; } .field.invalid input, .field.invalid textarea, .field.invalid select { border-color: #b91c1c; } + +/* Passwort-Abfrage (Sitzungs-Login, nicht gespeichert) */ +.overlay { + position: fixed; inset: 0; background: rgba(0, 0, 0, .35); + display: flex; align-items: center; justify-content: center; z-index: 50; +} +.overlay[hidden] { display: none; } +.overlay-box { + background: #fff; border-radius: 6px; padding: 18px 20px; width: 340px; + box-shadow: 0 8px 30px rgba(0, 0, 0, .25); +} +.overlay-box h2 { font-size: 15px; font-weight: 600; margin: 0 0 8px; } +.overlay-box input[type="password"] { + width: 100%; padding: 7px 9px; border: 1px solid #b9c0c8; border-radius: 3px; + font: inherit; margin-top: 8px; +} +.overlay-box input[type="password"]:focus { outline: none; border-color: #2563eb; box-shadow: 0 0 0 2px #2563eb33; } +.overlay-box .err { min-height: 1em; margin-top: 6px; } +.overlay-actions { display: flex; justify-content: flex-end; align-items: center; gap: 12px; margin-top: 10px; } +.overlay-box .pw-note { margin-top: 10px; } diff --git a/dialog/dialog.html b/dialog/dialog.html index 1763d5f..b8a29de 100644 --- a/dialog/dialog.html +++ b/dialog/dialog.html @@ -33,6 +33,21 @@
Ziel wählen …
+ + +