174 lines
6.7 KiB
Markdown
174 lines
6.7 KiB
Markdown
# 10 – Legacy-API-Kunden & Zugriffsbereinigung
|
||
|
||
## Ausgangspunkt
|
||
|
||
Das Legacy-System speichert API-Zugriffe nicht applikationsseitig. In Produktion ist Symfony-Logging deaktiviert (`logging_enabled: false`, `sfNoLogger`). API-Nutzung ist daher nicht zuverlässig aus der Anwendung rekonstruierbar.
|
||
|
||
Stattdessen wird der API-Cutover aus den migrierten Daten abgeleitet:
|
||
|
||
- Legacy-API-User sind in `users.registration_type = apiuser` bzw. über die Rolle `api-only` erkennbar.
|
||
- Legacy-API-Keys wurden bewusst nicht übernommen.
|
||
- Legacy-API-Credentials wie `apiReadAccess` / `apiWriteAccess` wurden nicht 1:1 pro User persistiert, sondern in neue Sanctum-Abilities übersetzt.
|
||
- Zahlungs-/Freigabestatus kann nur über importierte `legacy_invoices` bzw. später über Grandfathering-Daten bewertet werden.
|
||
|
||
## Legacy-Regeln
|
||
|
||
Im alten Symfony-Frontend gilt:
|
||
|
||
- API-fähige Routen sind in `apps/frontend/config/routing.yml` mit `api_access: true` markiert.
|
||
- `ApiAccessFilter` weist `X-ApiKey` auf nicht-API-Routen mit HTTP 501 ab.
|
||
- `ApiKeyGuardAuthFilter` liest `X-ApiKey` und sucht `sfGuardUserProfile.api_key`.
|
||
- Wird ein Profil gefunden, wird dessen User für den Request eingeloggt.
|
||
- Wenn bereits eine gültige Web-Session existiert, kann die API technisch auch ohne gültigen API-Key laufen, sofern Credentials passen.
|
||
- `SecureApiAccessFilter` verlangt:
|
||
- `GET` / `HEAD` → `apiReadAccess`
|
||
- `POST` / `PUT` / `DELETE` → `apiWriteAccess`
|
||
- andere Methoden → HTTP 405
|
||
- Einzelne Actions verlangen zusätzlich Symfony-Credentials wie `editor`.
|
||
- `ApiKeyGuardAuthFilter` prüft nicht selbst `is_active`.
|
||
|
||
## Neue Bewertungsregel
|
||
|
||
Für den Cutover erhalten nur noch Kunden Zugriff, die im migrierten System fachlich freigabefähig sind:
|
||
|
||
1. Kandidat ist `registration_type = apiuser` oder hat Rolle `api-only`.
|
||
2. User ist aktiv (`is_active = true`).
|
||
3. Die letzte importierte Legacy-Rechnung des Users hat `status = paid`.
|
||
|
||
Alle anderen Kandidaten werden nicht automatisch freigeschaltet:
|
||
|
||
- `needs_review`: aktiver API-Kandidat ohne importierte Legacy-Rechnung.
|
||
- `blocked`: inaktiver API-Kandidat oder letzte Rechnung nicht bezahlt.
|
||
|
||
## Report-Command
|
||
|
||
```bash
|
||
php artisan api:legacy-customers-report
|
||
php artisan api:legacy-customers-report --classification=eligible
|
||
php artisan api:legacy-customers-report --portal=businessportal24
|
||
php artisan api:legacy-customers-report --no-report
|
||
```
|
||
|
||
Der Command schreibt standardmäßig:
|
||
|
||
```bash
|
||
storage/app/private/migration/legacy-api-customers-*.json
|
||
```
|
||
|
||
Der Report enthält:
|
||
|
||
- Kandidatenregel und bekannte Datenlücken
|
||
- Zusammenfassung nach `eligible`, `needs_review`, `blocked`
|
||
- User-ID, E-Mail, Portal, Legacy-ID
|
||
- Rollen und Registration-Type
|
||
- letzte Legacy-Rechnung inkl. Status, Datum, Betrag
|
||
- empfohlene Aktion:
|
||
- `invite_to_generate_sanctum_token`
|
||
- `manual_billing_review_required`
|
||
- `do_not_grant_api_access`
|
||
|
||
## Aktuelle Datenlage
|
||
|
||
In der migrierten Datenbank wurden bisher gefunden:
|
||
|
||
- 195 User mit `registration_type = apiuser`
|
||
- 1 API-User mit archivierter Legacy-Rechnung
|
||
- diese Rechnung ist bezahlt
|
||
|
||
Damit ist nach aktueller Datenlage nur ein API-User automatisch freigabefähig. Die übrigen API-User müssen entweder manuell geprüft werden oder benötigen zusätzliche Legacy-Billing-/Vertragsdaten.
|
||
|
||
## Noch fehlende Daten
|
||
|
||
Für eine harte finale Entscheidung fehlen ggf.:
|
||
|
||
- vollständige Legacy-Zuordnung von API-Usern zu zahlenden Vertrags-/Billing-Usern, falls API-User nicht selbst Rechnungsempfänger waren
|
||
- explizite Legacy-Credential-Zuordnung `apiReadAccess` / `apiWriteAccess` pro User, falls feiner als `apiuser`/`api-only` benötigt
|
||
- aktuelle fachliche Liste aktiver API-Kunden vom Auftraggeber, falls Rechnungsarchiv nicht ausreicht
|
||
|
||
Ohne diese Daten ist die sichere Standardeinstellung:
|
||
|
||
- nur `eligible` automatisch zur Token-Migration einladen
|
||
- `needs_review` nicht automatisch freischalten
|
||
- `blocked` nicht freischalten
|
||
|
||
## Token-Erstellung im neuen System
|
||
|
||
Die Customer-Seite für API-Tokens ist zusätzlich technisch abgesichert. Ein User kann nur dann einen Sanctum-Token erstellen, wenn:
|
||
|
||
1. `users.is_active = true`
|
||
2. mindestens eine der folgenden Bedingungen erfüllt ist:
|
||
- aktiver neuer Zahlungsstatus in `user_payment_options` (`status = active`, aktueller Zeitraum gültig)
|
||
- aktiver Bestandsschutz in `user_payment_options` (`status = grandfathered`, `grandfathered_until >= today`)
|
||
- Übergangsweise: letzte importierte Legacy-Rechnung ist `paid`
|
||
|
||
Damit bekommen reaktivierte oder neue Kunden künftig keinen API-Zugang über alte Rechnungen, sondern erst über die neue aktive Zahlungs-/Vertragslogik. Bereits erstellte Tokens inaktiver User werden zusätzlich bei API-Requests blockiert.
|
||
|
||
## API-Usage-Logging
|
||
|
||
Neue API-Requests werden persistent in `api_usage_logs` protokolliert:
|
||
|
||
- `user_id`
|
||
- `personal_access_token_id`
|
||
- HTTP-Methode
|
||
- Pfad
|
||
- Route-Name
|
||
- Statuscode
|
||
- IP-Adresse
|
||
- User-Agent
|
||
- Dauer in Millisekunden
|
||
- Zeitpunkt
|
||
|
||
Nicht gespeichert werden:
|
||
|
||
- Bearer-Token im Klartext
|
||
- Legacy-`api_key`
|
||
- Request-Payloads
|
||
|
||
Auch abgewiesene Legacy-Key-Requests (`410 Gone`) und Requests inaktiver User (`403`) werden geloggt.
|
||
|
||
## API-Rate-Limiting
|
||
|
||
Die `/api/v1`-Routen sind zusätzlich per Middleware `EnsureApiTokenRateLimit` limitiert:
|
||
|
||
- Limit: 60 Requests pro Minute
|
||
- bevorzugter Schlüssel: Sanctum-/Bearer-Token-ID
|
||
- Fallback: SHA-256-Fingerprint des Bearer-Tokens, danach User-ID/IP
|
||
- inaktive User werden vorher per `EnsureApiUserIsActive` blockiert
|
||
- überschrittene Limits liefern HTTP 429 mit `Retry-After`, `X-RateLimit-Limit` und `X-RateLimit-Remaining`
|
||
|
||
Damit wird ein einzelner stark nutzender Client begrenzt, ohne andere Tokens desselben Users sofort mitzusperren.
|
||
|
||
## API-Usage-Reporting
|
||
|
||
Die protokollierten API-Requests können per Artisan-Command ausgewertet werden:
|
||
|
||
```bash
|
||
php artisan api:usage-report
|
||
php artisan api:usage-report --from=2026-04-01 --to=2026-04-30
|
||
php artisan api:usage-report --user=123
|
||
php artisan api:usage-report --status=403
|
||
php artisan api:usage-report --no-report
|
||
```
|
||
|
||
Der Command schreibt standardmäßig:
|
||
|
||
```bash
|
||
storage/app/private/migration/api-usage-*.json
|
||
```
|
||
|
||
Der Report enthält:
|
||
|
||
- Gesamtzahl Requests
|
||
- eindeutige User und Sanctum-Tokens
|
||
- 2xx-/4xx-/5xx-Verteilung
|
||
- durchschnittliche Dauer
|
||
- Top-Pfade
|
||
- Statuscode-Verteilung
|
||
- Top-User und Top-Tokens
|
||
- letzte Requests im gefilterten Zeitraum
|
||
|
||
Damit ist die neue API-Nutzung ab Cutover nicht mehr von Webserver-Logs abhängig und kann regelmäßig exportiert oder später in eine Admin-UI übernommen werden.
|
||
|
||
## P7-Abgrenzung Newsletter
|
||
|
||
`newsletter/unsubscribe` wird bewusst nicht als Legacy-API-Endpoint umgesetzt. Es gibt dafür keinen relevanten Legacy-API-Client. Newsletter-Abmeldung und externer Newsletter-Dienst werden nach der Migration im neuen Frontend sauber neu implementiert.
|