Uebernahme bestehender Legacy-API-Integrationen (Keys, Auth, Pfade)
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>
This commit is contained in:
parent
3cd7395319
commit
91992ac4b0
11 changed files with 425 additions and 13 deletions
82
app/Console/Commands/ImportLegacyApiKeys.php
Normal file
82
app/Console/Commands/ImportLegacyApiKeys.php
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\Api\LegacyApiKeyImporter;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* Importiert die Legacy-api_keys der Altportale als Sanctum-Tokens
|
||||
* (Übernahme bestehender API-Integrationen, 06.07.2026). Quelle sind die
|
||||
* read-only Legacy-Verbindungen; berechtigt ist, wer auch selbst ein Token
|
||||
* erstellen dürfte (aktiver Zahlungsstatus/Bestandsschutz). Wiederholbar:
|
||||
* bereits importierte Schlüssel werden übersprungen.
|
||||
*
|
||||
* Verwendung:
|
||||
* php artisan legacy:import-api-keys --dry-run
|
||||
* php artisan legacy:import-api-keys --portal=businessportal24
|
||||
*/
|
||||
class ImportLegacyApiKeys extends Command
|
||||
{
|
||||
protected $signature = 'legacy:import-api-keys
|
||||
{--portal=all : presseecho | businessportal24 | all}
|
||||
{--dry-run : Nur anzeigen, nichts schreiben}';
|
||||
|
||||
protected $description = 'Übernimmt die api_keys der Legacy-Portale als Sanctum-Tokens (bestehende Integrationen bleiben gültig).';
|
||||
|
||||
private const CONNECTIONS = [
|
||||
'presseecho' => 'mysql_presseecho',
|
||||
'businessportal24' => 'mysql_businessportal',
|
||||
];
|
||||
|
||||
public function handle(LegacyApiKeyImporter $importer): int
|
||||
{
|
||||
$portalOption = (string) $this->option('portal');
|
||||
$dryRun = (bool) $this->option('dry-run');
|
||||
|
||||
$portals = $portalOption === 'all' ? array_keys(self::CONNECTIONS) : [$portalOption];
|
||||
|
||||
if (array_diff($portals, array_keys(self::CONNECTIONS)) !== []) {
|
||||
$this->error('Ungültiges Portal. Erlaubt: presseecho, businessportal24, all.');
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
if ($dryRun) {
|
||||
$this->warn('[DRY-RUN] Kein tatsächlicher Schreibvorgang.');
|
||||
}
|
||||
|
||||
foreach ($portals as $portal) {
|
||||
$rows = DB::connection(self::CONNECTIONS[$portal])
|
||||
->table('sf_guard_user_profile')
|
||||
->whereNotNull('api_key')
|
||||
->where('api_key', '!=', '')
|
||||
->get(['user_id', 'api_key'])
|
||||
->map(fn (object $row): array => [
|
||||
'legacy_id' => (int) $row->user_id,
|
||||
'api_key' => (string) $row->api_key,
|
||||
]);
|
||||
|
||||
$result = $importer->import($portal, $rows, $dryRun);
|
||||
|
||||
$this->table(
|
||||
['Portal', 'Keys in Legacy-DB', 'Importiert', 'Schon vorhanden', 'Nicht berechtigt', 'Kein User-Mapping'],
|
||||
[[
|
||||
$portal,
|
||||
$rows->count(),
|
||||
$result['imported'],
|
||||
$result['already_present'],
|
||||
$result['ineligible'],
|
||||
$result['unknown_user'],
|
||||
]],
|
||||
);
|
||||
|
||||
foreach ($result['ineligible_users'] as $email) {
|
||||
$this->warn("Nicht berechtigt (kein aktiver Zahlungsstatus): {$email}");
|
||||
}
|
||||
}
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
|
@ -4,8 +4,17 @@ namespace App\Http\Middleware;
|
|||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Laravel\Sanctum\PersonalAccessToken;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Legacy-API-Kompatibilität (Übernahme bestehender Integrationen,
|
||||
* 06.07.2026): Die Altportale authentifizierten per `X-ApiKey`-Header bzw.
|
||||
* `api_key`-Parameter. Importierte Legacy-Keys (legacy:import-api-keys)
|
||||
* sind gültige Sanctum-Tokens — hier wird die alte Übergabeform in einen
|
||||
* Bearer-Header übersetzt, damit `auth:sanctum` sie versteht. Unbekannte
|
||||
* Keys erhalten weiterhin 410 mit Migrationshinweis.
|
||||
*/
|
||||
class RejectLegacyApiKeys
|
||||
{
|
||||
/**
|
||||
|
|
@ -15,14 +24,44 @@ class RejectLegacyApiKeys
|
|||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if ($request->has('api_key') || filled($request->header('X-Api-Key'))) {
|
||||
return response()->json([
|
||||
'message' => 'Legacy API keys are no longer supported.',
|
||||
'migration_url' => url('/customer/tokens'),
|
||||
'docs_url' => url('/docs/api/v1'),
|
||||
], 410);
|
||||
$legacyKey = $this->legacyKey($request);
|
||||
|
||||
if ($legacyKey === null) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
// Ein explizit gesetzter Bearer-Token hat Vorrang.
|
||||
if (filled($request->bearerToken())) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
if (PersonalAccessToken::findToken($legacyKey) !== null) {
|
||||
$request->headers->set('Authorization', 'Bearer '.$legacyKey);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => 'This API key is not valid (anymore). Please generate a token in your customer account.',
|
||||
'migration_url' => url('/customer/tokens'),
|
||||
'docs_url' => url('/docs/api/v1'),
|
||||
], 410);
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy-Schlüssel aus den historischen Übergabeformen: `X-ApiKey`
|
||||
* (dokumentierter Header der Altportale), `X-Api-Key` (verbreitete
|
||||
* Schreibvariante) oder `api_key` als Query-/Body-Parameter.
|
||||
*/
|
||||
private function legacyKey(Request $request): ?string
|
||||
{
|
||||
$key = trim((string) (
|
||||
$request->header('X-ApiKey')
|
||||
?: $request->header('X-Api-Key')
|
||||
?: $request->query('api_key')
|
||||
?: $request->input('api_key')
|
||||
));
|
||||
|
||||
return $key === '' ? null : $key;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
90
app/Services/Api/LegacyApiKeyImporter.php
Normal file
90
app/Services/Api/LegacyApiKeyImporter.php
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ use Illuminate\Foundation\Application;
|
|||
use Illuminate\Foundation\Configuration\Exceptions;
|
||||
use Illuminate\Foundation\Configuration\Middleware;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Laravel\Sanctum\Http\Middleware\CheckAbilities;
|
||||
use Laravel\Sanctum\Http\Middleware\CheckForAnyAbility;
|
||||
|
||||
|
|
@ -19,6 +20,11 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||
api: __DIR__.'/../routes/api.php',
|
||||
commands: __DIR__.'/../routes/console.php',
|
||||
health: '/up',
|
||||
then: function (): void {
|
||||
// Legacy-API-Aliasse (/service/… ohne /api-Präfix) in der
|
||||
// api-Middleware-Gruppe — Übernahme bestehender Integrationen.
|
||||
Route::middleware('api')->group(__DIR__.'/../routes/legacy-api.php');
|
||||
},
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware) {
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,11 @@ info:
|
|||
Rate-Limit: 60 Anfragen pro Minute je Token (Header X-RateLimit-Limit /
|
||||
X-RateLimit-Remaining, bei 429 zusätzlich Retry-After).
|
||||
|
||||
Migration: Der X-ApiKey-Header der Altportale wird nicht mehr
|
||||
unterstützt — legacy API keys are no longer supported.
|
||||
Migration: Bestehende API-Schlüssel der Altportale wurden übernommen
|
||||
und bleiben gültig — auch in der alten Übergabeform (X-ApiKey-Header)
|
||||
und auf den dokumentierten /service/…-Pfaden. Unbekannte Alt-Schlüssel
|
||||
erhalten 410 mit Migrationshinweis. Neue Integrationen nutzen bitte
|
||||
Bearer-Tokens auf /api/v1.
|
||||
servers:
|
||||
- url: https://pressekonto.de/api/v1
|
||||
security:
|
||||
|
|
|
|||
|
|
@ -130,6 +130,12 @@ php artisan domains:generate-favicons
|
|||
# fehlen AGB-Versionsangabe/Protokoll-Referenz und die /richtlinien-Kategorien.
|
||||
php artisan db:seed --class=LegalDocumentSeeder --force
|
||||
php artisan db:seed --class=RejectionCategorySeeder --force
|
||||
|
||||
# Legacy-API-Schlüssel als Sanctum-Tokens übernehmen (bestehende
|
||||
# Kunden-Integrationen bleiben gültig; erst Dry-Run prüfen). Läuft NACH dem
|
||||
# Grandfathering, da die Berechtigung am aktiven Zahlungsstatus hängt.
|
||||
php artisan legacy:import-api-keys --dry-run
|
||||
php artisan legacy:import-api-keys
|
||||
```
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -104,7 +104,11 @@
|
|||
(z. B. <code>press-releases:write</code>), die den Zugriff auf die jeweiligen Endpunkte steuern.
|
||||
Tokens erstellen und widerrufen Sie selbst in Ihrem Publisher-Konto unter „API-Tokens" —
|
||||
Voraussetzung ist eine aktive Buchung (Tarif oder Einzel-Pressemitteilung).
|
||||
Der <code>X-ApiKey</code>-Header der Altportale wird nicht mehr unterstützt.
|
||||
<strong>Bestandskunden der Altportale:</strong> Ihre bisherigen API-Schlüssel wurden
|
||||
übernommen und funktionieren weiter — sowohl als Bearer-Token als auch in der alten
|
||||
Form (<code>X-ApiKey</code>-Header) und auf den dokumentierten
|
||||
<code>/service/…</code>-Pfaden. Für neue Integrationen empfehlen wir die aktuellen
|
||||
<code>/api/v1/…</code>-Endpunkte mit Bearer-Token.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
50
routes/legacy-api.php
Normal file
50
routes/legacy-api.php
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
use App\Http\Controllers\Api\V1\CompanyController;
|
||||
use App\Http\Controllers\Api\V1\PressReleaseController;
|
||||
use App\Http\Controllers\Api\V1\PressReleaseImageController;
|
||||
use App\Http\Middleware\EnsureApiTokenRateLimit;
|
||||
use App\Http\Middleware\EnsureApiUserIsActive;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Legacy-API-Aliasse (Übernahme bestehender Integrationen, 06.07.2026)
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Die Altportale dokumentierten ihre API unter /service/... (JSON,
|
||||
| X-ApiKey-Header). Diese Aliasse bilden die dokumentierte Alt-Oberfläche
|
||||
| auf die v1-Controller ab, damit bestehende Kundenintegrationen nach dem
|
||||
| Relaunch weiterlaufen (Auth: importierte Legacy-Keys, s. Middleware
|
||||
| RejectLegacyApiKeys + legacy:import-api-keys). Läuft in der
|
||||
| api-Middleware-Gruppe (bootstrap/app.php, then:) — ohne /api-Präfix.
|
||||
|
|
||||
| Nicht abgebildet (waren dokumentiert, existieren in v1 nicht):
|
||||
| Pressemappen/Kontakte ANLEGEN sowie Einzelbild-GET — dafür antwortet
|
||||
| der Fallback mit einem Hinweis auf Kundenkonto und neue Doku.
|
||||
|
|
||||
*/
|
||||
|
||||
Route::prefix('service')
|
||||
->name('legacy-service.')
|
||||
->middleware(['auth:sanctum', EnsureApiUserIsActive::class, EnsureApiTokenRateLimit::class])
|
||||
->group(function (): void {
|
||||
Route::get('pressreleases', [PressReleaseController::class, 'index'])->name('pressreleases.index');
|
||||
Route::post('pressreleases', [PressReleaseController::class, 'store'])->name('pressreleases.store');
|
||||
Route::get('pressreleases/{pressRelease}', [PressReleaseController::class, 'show'])->name('pressreleases.show');
|
||||
Route::get('pressreleases/{pressRelease}/images', [PressReleaseImageController::class, 'index'])->name('pressreleases.images.index');
|
||||
Route::post('pressreleases/{pressRelease}/images', [PressReleaseImageController::class, 'store'])->name('pressreleases.images.store');
|
||||
|
||||
Route::get('users/me/companies', [CompanyController::class, 'index'])->name('companies.index');
|
||||
Route::get('users/me/companies/{company}', [CompanyController::class, 'show'])->name('companies.show');
|
||||
});
|
||||
|
||||
// Alles Übrige unter /service/… (inkl. der nicht übernommenen
|
||||
// Schreib-Endpoints für Pressemappen/Kontakte): klarer Hinweis statt 404.
|
||||
Route::any('service/{path?}', function () {
|
||||
return response()->json([
|
||||
'message' => 'This legacy endpoint is not available anymore. Companies and press contacts are managed in your customer account; see the current API reference for everything else.',
|
||||
'account_url' => url('/customer'),
|
||||
'docs_url' => url('/docs/api/v1'),
|
||||
], 410);
|
||||
})->where('path', '.*')->name('legacy-service.gone');
|
||||
128
tests/Feature/Api/LegacyApiCompatibilityTest.php
Normal file
128
tests/Feature/Api/LegacyApiCompatibilityTest.php
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\Portal;
|
||||
use App\Enums\UserPaymentOptionStatus;
|
||||
use App\Models\PaymentOption;
|
||||
use App\Models\User;
|
||||
use App\Models\UserPaymentOption;
|
||||
use App\Services\Api\LegacyApiKeyImporter;
|
||||
use Laravel\Sanctum\PersonalAccessToken;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Übernahme bestehender Legacy-API-Integrationen (06.07.2026): importierte
|
||||
* api_keys bleiben gültig, alte Übergabeformen (X-ApiKey/api_key) und die
|
||||
* dokumentierten /service/…-Pfade funktionieren weiter.
|
||||
*/
|
||||
function eligibleLegacyApiUser(int $legacyId = 4711): User
|
||||
{
|
||||
$user = User::factory()->create([
|
||||
'is_active' => true,
|
||||
'portal' => Portal::Businessportal24,
|
||||
'legacy_portal' => 'businessportal24',
|
||||
'legacy_id' => $legacyId,
|
||||
]);
|
||||
|
||||
// Aktiver Zahlungsstatus → API-berechtigt (ApiAccessEligibilityService).
|
||||
UserPaymentOption::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'payment_option_id' => PaymentOption::factory()->create()->id,
|
||||
'status' => UserPaymentOptionStatus::Grandfathered->value,
|
||||
'grandfathered_until' => null,
|
||||
'current_period_start' => now()->subMonth(),
|
||||
'current_period_end' => now()->addMonths(11),
|
||||
'stripe_subscription_id' => null,
|
||||
]);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
test('the importer turns legacy api keys into sanctum tokens idempotently', function () {
|
||||
$user = eligibleLegacyApiUser();
|
||||
|
||||
// Nicht berechtigter Kandidat (inaktiv) und unbekanntes Legacy-Mapping.
|
||||
$inactive = User::factory()->create([
|
||||
'is_active' => false,
|
||||
'legacy_portal' => 'businessportal24',
|
||||
'legacy_id' => 999,
|
||||
]);
|
||||
|
||||
$rows = [
|
||||
['legacy_id' => 4711, 'api_key' => 'legacy-key-abc123'],
|
||||
['legacy_id' => 999, 'api_key' => 'legacy-key-inactive'],
|
||||
['legacy_id' => 123456, 'api_key' => 'legacy-key-unmapped'],
|
||||
];
|
||||
|
||||
$importer = app(LegacyApiKeyImporter::class);
|
||||
$result = $importer->import('businessportal24', $rows);
|
||||
|
||||
expect($result['imported'])->toBe(1);
|
||||
expect($result['ineligible'])->toBe(1);
|
||||
expect($result['ineligible_users'])->toBe([$inactive->email]);
|
||||
expect($result['unknown_user'])->toBe(1);
|
||||
|
||||
$token = PersonalAccessToken::findToken('legacy-key-abc123');
|
||||
expect($token)->not->toBeNull();
|
||||
expect($token->tokenable_id)->toBe($user->id);
|
||||
expect($token->name)->toBe(LegacyApiKeyImporter::TOKEN_NAME);
|
||||
expect($token->abilities)->toBe(LegacyApiKeyImporter::ABILITIES);
|
||||
|
||||
// Re-Run legt keine Duplikate an.
|
||||
$rerun = $importer->import('businessportal24', $rows);
|
||||
expect($rerun['imported'])->toBe(0);
|
||||
expect($rerun['already_present'])->toBe(1);
|
||||
expect(PersonalAccessToken::query()->where('token', hash('sha256', 'legacy-key-abc123'))->count())->toBe(1);
|
||||
});
|
||||
|
||||
test('an imported legacy key authenticates via the old header and query forms', function () {
|
||||
/** @var TestCase $this */
|
||||
eligibleLegacyApiUser();
|
||||
app(LegacyApiKeyImporter::class)->import('businessportal24', [
|
||||
['legacy_id' => 4711, 'api_key' => 'legacy-key-abc123'],
|
||||
]);
|
||||
|
||||
// Dokumentierter Alt-Header der Portale: X-ApiKey.
|
||||
$this->getJson('/api/v1/categories', ['X-ApiKey' => 'legacy-key-abc123'])
|
||||
->assertOk();
|
||||
|
||||
// Verbreitete Schreibvariante + Query-Parameter.
|
||||
$this->getJson('/api/v1/categories', ['X-Api-Key' => 'legacy-key-abc123'])
|
||||
->assertOk();
|
||||
$this->getJson('/api/v1/categories?api_key=legacy-key-abc123')
|
||||
->assertOk();
|
||||
});
|
||||
|
||||
test('unknown legacy keys still get the 410 migration hint', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->getJson('/api/v1/categories', ['X-ApiKey' => 'gibt-es-nicht'])
|
||||
->assertStatus(410)
|
||||
->assertJsonStructure(['message', 'migration_url', 'docs_url']);
|
||||
});
|
||||
|
||||
test('the documented legacy service paths keep working with an imported key', function () {
|
||||
/** @var TestCase $this */
|
||||
eligibleLegacyApiUser();
|
||||
app(LegacyApiKeyImporter::class)->import('businessportal24', [
|
||||
['legacy_id' => 4711, 'api_key' => 'legacy-key-abc123'],
|
||||
]);
|
||||
|
||||
$this->getJson('/service/pressreleases', ['X-ApiKey' => 'legacy-key-abc123'])
|
||||
->assertOk();
|
||||
|
||||
$this->getJson('/service/users/me/companies', ['X-ApiKey' => 'legacy-key-abc123'])
|
||||
->assertOk();
|
||||
});
|
||||
|
||||
test('the legacy service paths require authentication', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->getJson('/service/pressreleases')->assertUnauthorized();
|
||||
});
|
||||
|
||||
test('retired legacy service endpoints answer with a helpful 410 instead of 404', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->postJson('/service/users/me/companies', ['company' => ['name' => 'Test']])
|
||||
->assertStatus(410)
|
||||
->assertJsonStructure(['message', 'account_url', 'docs_url']);
|
||||
|
||||
$this->getJson('/service/irgendwas')->assertStatus(410);
|
||||
});
|
||||
|
|
@ -7,11 +7,13 @@ use App\Models\User;
|
|||
use Laravel\Sanctum\Sanctum;
|
||||
use Tests\TestCase;
|
||||
|
||||
test('legacy api key requests receive gone response', function () {
|
||||
test('unknown legacy api key requests receive gone response', function () {
|
||||
/** @var TestCase $this */
|
||||
// Übernahme 06.07.: importierte Legacy-Keys sind gültige Tokens
|
||||
// (LegacyApiCompatibilityTest); nur unbekannte Keys bekommen 410.
|
||||
$this->getJson('/api/v1/categories?api_key=legacy-key')
|
||||
->assertStatus(410)
|
||||
->assertJsonPath('message', 'Legacy API keys are no longer supported.');
|
||||
->assertJsonStructure(['message', 'migration_url', 'docs_url']);
|
||||
});
|
||||
|
||||
test('api user can read only own companies', function () {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ test('api v1 documentation is publicly available', function () {
|
|||
->assertHeader('content-type', 'application/yaml; charset=UTF-8')
|
||||
->assertSee('openapi: 3.1.0', false)
|
||||
->assertSee('/press-releases:', false)
|
||||
->assertSee('legacy API keys are no longer supported', false);
|
||||
// Übernahme der Legacy-Integrationen (06.07.): Alt-Schlüssel bleiben
|
||||
// gültig, unbekannte bekommen den Migrationshinweis.
|
||||
->assertSee('Bestehende API-Schlüssel der Altportale wurden übernommen', false);
|
||||
});
|
||||
|
||||
test('the public api page documents the real endpoints', function () {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue