# PROJ-13: REST API für externe CRM-Anbindung ## Status: In Progress **Created:** 2026-03-13 **Last Updated:** 2026-03-13 ## Dependencies - Requires: PROJ-1 (Authentifizierung) – API-Keys sind an Nutzer/Rollen gebunden - Requires: PROJ-5 (Speicherung & Indexierung) – Daten kommen aus dem Archiv - Requires: PROJ-6 (Volltext-Suche) – Suche über API nutzbar ## Hinweis Externe Systeme (CRM, ERP, Helpdesk etc.) sollen über eine dokumentierte REST API auf das Archiv zugreifen können – **ausschließlich lesend**. Schreiboperationen (Importieren, Löschen, Labeln etc.) sind über die API nicht möglich und werden nicht implementiert. Authentifizierung über API-Keys, nicht über Session-Cookies. ## User Stories - Als CRM-Administrator möchte ich einen API-Key generieren, damit mein CRM-System auf das Archiv zugreifen kann. - Als CRM-System möchte ich E-Mails eines bestimmten Kontakts (E-Mail-Adresse) abrufen, damit ich die Kommunikationshistorie im CRM anzeigen kann. - Als CRM-System möchte ich E-Mails nach Datum, Absender oder Betreff durchsuchen, damit ich gezielt relevante Mails finden kann. - Als Admin möchte ich API-Keys verwalten (anlegen, deaktivieren, löschen), damit ich den Zugriff kontrollieren kann. - Als Admin möchte ich sehen, welcher API-Key wann welche Anfragen gestellt hat. ## Acceptance Criteria - [ ] API-Key-Verwaltung im Admin-Bereich: anlegen, benennen, deaktivieren, löschen - [ ] API-Keys haben eine konfigurierbare Rolle (`user` oder `auditor`) – bestimmt Zugriffsumfang (Leserechte) - [ ] Nur `GET`-Methoden erlaubt – `POST`, `PUT`, `PATCH`, `DELETE` geben generisch 405 Method Not Allowed zurück - [ ] Authentifizierung via HTTP-Header: `Authorization: Bearer ` - [ ] Endpunkt: `GET /api/v1/mails?from=&to=&subject=&date_from=&date_to=` – Suche/Filterung - [ ] Endpunkt: `GET /api/v1/mails/{message_id}` – einzelne E-Mail abrufen (Metadaten) - [ ] Endpunkt: `GET /api/v1/mails/{message_id}/raw` – Original-EML herunterladen - [ ] Endpunkt: `GET /api/v1/mails?contact=email@firma.de` – alle Mails eines Kontakts (From oder To) - [ ] Antwortformat: JSON für Metadaten, `application/octet-stream` für Raw-EML - [ ] Paginierung: `?page=1&limit=25` (max. 100 pro Anfrage) - [ ] API-Zugriffe werden im Audit-Log erfasst (API-Key-Name, Endpunkt, Zeitstempel) - [ ] OpenAPI/Swagger-Dokumentation unter `/api/v1/docs` ## Edge Cases - Ungültiger oder deaktivierter API-Key → 401 Unauthorized - API-Key mit `user`-Rolle fragt Mails ab, auf die er keinen Zugriff hat → 403 - Rate-Limiting: zu viele Anfragen pro API-Key → 429 Too Many Requests - Sehr große Ergebnismengen (>10.000 Treffer) → Paginierung erzwingen, kein Full-Dump - CRM fragt nicht existierende Message-ID ab → 404 ## Technical Requirements - **Reine Lese-API** – ausschließlich `GET`-Endpunkte, keine Schreiboperationen - Eigener Route-Prefix `/api/v1/` für externe API (getrennt von interner `/api/`) - API-Keys: zufällig generiert (32 Byte, Base64), bcrypt-gehasht in der DB (nie im Klartext) - Rate-Limiting pro API-Key konfigurierbar (Standard: 60 Anfragen/Minute) - OpenAPI 3.0 Spec wird aus Code generiert oder manuell gepflegt - Versionierung: `/api/v1/` – spätere Versionen brechen bestehende Clients nicht --- ## Tech Design (Solution Architect) ### Systemübersicht ``` CRM / ERP / Helpdesk │ │ GET /api/v1/mails?contact=kunde@example.com │ Authorization: Bearer ▼ Go Backend – Externer API-Router (/api/v1/*) │ ├── API-Key Middleware ← statt Session-Cookie ├── Rate Limiter ├── Shared Search Service ←──── dieselbe Logik wie interne Suche (PROJ-6) └── Shared Mail Service ←──── dieselbe Logik wie Mail-Abruf (PROJ-7) ``` ### Komponentenstruktur **Go Backend:** ``` /api/v1/* (externer Prefix, getrennt von internem /api/*) │ ├── API-Key Middleware ← ersetzt Session-Middleware │ ├── Bearer-Token aus Header lesen │ ├── SHA-256-Hash → DB-Lookup ← schneller Lookup ohne bcrypt-Overhead │ ├── Key deaktiviert? → 401 │ ├── Rolle laden (user/auditor) │ └── Rate-Limit-Konfiguration laden │ ├── Rate Limiter ← Token-Bucket pro API-Key │ └── Limit überschritten → 429 + Retry-After Header │ ├── Method Guard ← alles außer GET → 405 │ ├── Shared Search Service ← identische Logik wie /api/search (PROJ-6) │ ├── Xapian QueryParser │ ├── Rollen-Filter (user/auditor) │ └── PostgreSQL Metadaten-Lookup │ ├── Shared Mail Service ← identische Logik wie /api/mails (PROJ-7) │ ├── .m-Datei lesen + entschlüsseln │ ├── MIME-Parser │ └── Anhang-Streaming │ ├── Audit Logger ← API-Key-Name + Endpunkt + Zeitstempel │ └── API-Key-Verwaltung (Admin) ├── POST /api/admin/apikeys ← Key generieren (einmalige Anzeige) ├── GET /api/admin/apikeys ← Liste (Name + Rolle + letzter Zugriff) └── DELETE /api/admin/apikeys/{id} ``` ### API-Key Authentifizierungsfluss ``` CRM-System │ │ Authorization: Bearer am_a1b2c3d4e5f6... ▼ API-Key Middleware ├─ Präfix "am_" prüfen ├─ SHA-256(token) → DB-Lookup (indiziert) ├─ Key gefunden + aktiv? Nein → 401 └─ Ja → Rolle + Rate-Limit laden │ ▼ Rate Limiter (Token-Bucket) ├─ Limit erreicht? → 429 + Retry-After: 30 └─ OK → weiter │ ▼ Method Guard ├─ POST/PUT/DELETE? → 405 └─ GET → weiter │ ▼ Shared Service Layer │ ▼ Audit Logger → API-Key-Name + Endpunkt + Zeitstempel ``` ### Datenmodell **Tabelle `api_keys`:** | Feld | Beschreibung | |------|-------------| | `id` | Interne ID | | `name` | Bezeichnung (z.B. "CRM Salesforce") | | `token_hash` | SHA-256 des Tokens (für schnellen Lookup, indiziert) | | `role` | `user` oder `auditor` | | `active` | `true` / `false` | | `rate_limit` | Anfragen pro Minute (Standard: 60) | | `created_at` | Erstellungszeitpunkt | | `last_used_at` | Letzter erfolgreicher Zugriff | **Key-Format:** ``` Generiert: am_<32-Byte-random-Base64> Gespeichert: SHA-256(token) in DB Angezeigt: einmalig im Admin-UI – danach nicht mehr abrufbar ``` ### Technische Entscheidungen | Entscheidung | Begründung | |---|---| | **Shared Service Layer** | Suche und Mail-Abruf teilen dieselbe Go-Logik mit der internen API – kein doppelter Code | | **SHA-256 statt bcrypt** | API-Keys sind kryptografisch zufällig (32 Byte) – SHA-256 reicht, bcrypt wäre bei jeder Anfrage zu langsam | | **`am_`-Präfix** | Erkennungsmerkmal für archivmail-Keys – einfach filterbar in Logs | | **Token einmalig anzeigen** | Nur Hash gespeichert – kein späteres Auslesen möglich (wie GitHub PAT) | | **Token-Bucket Rate Limiter** | Gleichmäßige Anfragen erlaubt, kurze Bursts toleriert | | **`/api/v1/` Prefix** | Klare Versionierung – zukünftige `/api/v2/` bricht bestehende Clients nicht | | **Audit-Log bei API-Zugriffen** | Externe Zugriffe werden geloggt (anders als interne Lesezugriffe) | ### Abhängigkeiten Kein zusätzliches Paket – Rate-Limiter und SHA-256 aus der Go-Stdlib (`crypto/sha256`, `sync`). ## QA Test Results _To be added by /qa_ ## Deployment _To be added by /deploy_