44 lines
2.2 KiB
PHP
44 lines
2.2 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Middleware;
|
|
|
|
use Closure;
|
|
use Illuminate\Http\Request;
|
|
|
|
final class EditorialAuthentication
|
|
{
|
|
public function handle(Request $request, Closure $next)
|
|
{
|
|
abort_unless(config('editorial.enabled'), 503, 'Editorial API disabled');
|
|
if (app()->environment('local')) {
|
|
$db = config('database.connections.'.config('database.default'));
|
|
abort_unless(($db['database'] ?? '') === 'fna_v3' && in_array($db['host'] ?? '', ['localhost', '127.0.0.1']) && empty($db['url']), 503, 'Local editorial database binding mismatch');
|
|
}
|
|
$path = config('editorial.credential_file');
|
|
$credential = is_file($path) ? json_decode(file_get_contents($path), true) : null;
|
|
abort_unless(is_array($credential)
|
|
&& ($credential['environment'] ?? null) === app()->environment()
|
|
&& ($credential['project'] ?? null) === 'fna-v3'
|
|
&& ($credential['schema'] ?? null) === 'gemdocs.credential.v1'
|
|
&& ($credential['base_url'] ?? null) === config('editorial.base_url')
|
|
&& preg_match('/\Afna_ed_[a-f0-9]{64}\z/', $credential['fields']['api_key'] ?? ''), 503, 'Editorial credential unavailable');
|
|
abort_unless(hash_equals($credential['fields']['api_key'], $request->bearerToken() ?? ''), 401, 'Invalid editorial credential');
|
|
try {
|
|
$response = $next($request);
|
|
} catch (\Illuminate\Database\QueryException $e) {
|
|
return response()->json(['message' => 'Concurrent or incompatible database write; query current status before retry'], 409);
|
|
} catch (\Throwable $e) {
|
|
if ($e instanceof \Symfony\Component\HttpKernel\Exception\HttpExceptionInterface || $e instanceof \Illuminate\Validation\ValidationException) {
|
|
throw $e;
|
|
}
|
|
\Illuminate\Support\Facades\Log::error('Editorial operation failed', ['exception_type' => get_class($e)]);
|
|
|
|
return response()->json(['message' => 'Editorial operation failed; query the request ID before retrying'], 500);
|
|
}
|
|
$response->headers->set('Cache-Control', 'private, no-store');
|
|
$response->headers->set('X-Robots-Tag', 'noindex, nofollow');
|
|
|
|
return $response;
|
|
}
|
|
}
|