# 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.