Bestehende Kunden-Integrationen der Altportale laufen nach dem Relaunch weiter, ohne dass Kunden etwas umstellen muessen: 1. legacy:import-api-keys uebernimmt die api_keys aus den Legacy-DBs (sf_guard_user_profile) als Sanctum-Tokens mit identischem Secret (Sanctum loest Tokens ohne id|-Praefix per sha256-Hash auf) — nur fuer berechtigte Nutzer (aktiver Zahlungsstatus/Bestandsschutz, ApiAccessEligibilityService); idempotent, mit Dry-Run. Probe gegen die lokalen Snapshots: 406 Keys wuerden uebernommen. 2. Die Auth-Middleware uebersetzt die alten Uebergabeformen (X-ApiKey- Header, api_key-Parameter) fuer gueltige Keys in Bearer; unbekannte Keys erhalten weiterhin 410 mit Migrationshinweis. 3. Die dokumentierten /service/...-Pfade der Alt-API sind als Aliasse auf die v1-Controller gelegt (pressreleases inkl. Bilder, companies lesend); nicht uebernommene Alt-Endpoints (Pressemappen/Kontakte anlegen) antworten 410 mit Hinweis auf Kundenkonto und neue Doku. API-Seite + OpenAPI entsprechend aktualisiert; Go-Live-Checkliste um den Import-Schritt (nach dem Grandfathering) ergaenzt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
90 lines
2.6 KiB
PHP
90 lines
2.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Api;
|
|
|
|
use App\Models\User;
|
|
use Laravel\Sanctum\PersonalAccessToken;
|
|
|
|
/**
|
|
* Übernahme bestehender Legacy-API-Integrationen (User-Entscheidung
|
|
* 06.07.2026): Die api_keys der Altportale (sf_guard_user_profile) werden
|
|
* als Sanctum-Tokens mit identischem Secret importiert — Sanctum löst
|
|
* Tokens ohne "id|"-Präfix über den sha256-Hash auf, dadurch bleiben die
|
|
* bestehenden Schlüssel der Kunden unverändert gültig. Berechtigt ist,
|
|
* wer auch selbst ein Token erstellen dürfte (ApiAccessEligibilityService).
|
|
*/
|
|
class LegacyApiKeyImporter
|
|
{
|
|
public const TOKEN_NAME = 'Legacy-API-Key (importiert)';
|
|
|
|
public const ABILITIES = [
|
|
'press-releases:read',
|
|
'press-releases:write',
|
|
'press-release-images:write',
|
|
'companies:read',
|
|
'newsletter:subscribe',
|
|
];
|
|
|
|
public function __construct(private readonly ApiAccessEligibilityService $eligibility) {}
|
|
|
|
/**
|
|
* @param iterable<int, array{legacy_id: int, api_key: string}> $rows
|
|
* @return array{imported: int, already_present: int, ineligible: int, unknown_user: int, ineligible_users: list<string>}
|
|
*/
|
|
public function import(string $portal, iterable $rows, bool $dryRun = false): array
|
|
{
|
|
$result = [
|
|
'imported' => 0,
|
|
'already_present' => 0,
|
|
'ineligible' => 0,
|
|
'unknown_user' => 0,
|
|
'ineligible_users' => [],
|
|
];
|
|
|
|
foreach ($rows as $row) {
|
|
$key = trim((string) $row['api_key']);
|
|
|
|
if ($key === '') {
|
|
continue;
|
|
}
|
|
|
|
$user = User::query()
|
|
->where('legacy_portal', $portal)
|
|
->where('legacy_id', (int) $row['legacy_id'])
|
|
->first();
|
|
|
|
if (! $user) {
|
|
$result['unknown_user']++;
|
|
|
|
continue;
|
|
}
|
|
|
|
$hash = hash('sha256', $key);
|
|
|
|
if (PersonalAccessToken::query()->where('token', $hash)->exists()) {
|
|
$result['already_present']++;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (! $this->eligibility->canCreateToken($user)) {
|
|
$result['ineligible']++;
|
|
$result['ineligible_users'][] = (string) $user->email;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (! $dryRun) {
|
|
$user->tokens()->create([
|
|
'name' => self::TOKEN_NAME,
|
|
'token' => $hash,
|
|
'abilities' => self::ABILITIES,
|
|
]);
|
|
}
|
|
|
|
$result['imported']++;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
}
|