Files
Investidor-V2/tools/portal-qa-auth-trace.php
T
nala 6ddafc95ba
tests / ci (push) Canceled after 0s
oportunidades 4
2026-10-10 07:03:59 +02:00

122 lines
5.8 KiB
PHP

<?php
// Loaded exclusively by the guarded QA router. Never log request bodies or cookie values.
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;
class PortalQaAuthTrace
{
public static function pseudonym(?string $value): ?string
{
if (! $value) {
return null;
}
$file = storage_path('app/private/investmoz-v2/portal-audit/auth-trace-salt-private');
if (! is_file($file)) {
$handle = fopen($file, 'x');
if ($handle) {
fwrite($handle, random_bytes(32));
fclose($handle);
}
}
return substr(hash_hmac('sha256', $value, file_get_contents($file)), 0, 24);
}
public static function destination(?string $url): ?array
{
if (! $url) {
return null;
}
$parts = parse_url($url);
return ['origin' => isset($parts['host']) ? ($parts['scheme'] ?? 'http').'://'.$parts['host'].(isset($parts['port']) ? ':'.$parts['port'] : '') : null,
'path' => $parts['path'] ?? '/']; // Query strings may contain tokens: excluded.
}
public static function state(Request $request): ?array
{
if (! $request->hasSession()) {
return null;
}
$session = $request->session();
$guard = Auth::guard(config('fortify.guard'));
return ['session' => self::pseudonym($session->getId()),
'accepted_cookie_session' => self::pseudonym($request->cookies->get(config('session.cookie'))),
'pending_mfa_user' => $session->get('login.id'),
'stored_web_user' => $session->get($guard->getName()), 'authenticated_user' => $guard->id(),
'intended' => self::destination($session->get('url.intended')),
'handler' => get_class($session->getHandler())];
}
public function handle(Request $request, Closure $next): Response
{
if (! app()->environment('testing') || (int) $request->getPort() !== 18097) {
return $next($request);
}
$trace = bin2hex(random_bytes(8));
$request->attributes->set('qa_auth_trace', $trace);
$names = [];
foreach (explode(';', $request->headers->get('Cookie', '')) as $cookie) {
$name = trim(explode('=', $cookie, 2)[0]);
if ($name === 'XSRF-TOKEN' || str_ends_with($name, '_qa') || str_starts_with($name, 'investmoz_qa_')) {
$names[$name] = ($names[$name] ?? 0) + 1;
}
}
$response = $next($request);
$state = self::state($request);
$persisted = null;
if ($request->hasSession()) {
$raw = $request->session()->getHandler()->read($request->session()->getId());
$data = $raw ? json_decode($raw, true) : null;
$guardKey = Auth::guard(config('fortify.guard'))->getName();
$persisted = ['readable' => is_array($data), 'stored_web_user' => is_array($data) ? ($data[$guardKey] ?? null) : null];
}
$cookies = [];
foreach ($response->headers->getCookies() as $cookie) {
$cookies[] = ['name' => $cookie->getName(), 'domain' => $cookie->getDomain(), 'path' => $cookie->getPath(),
'secure' => $cookie->isSecure(), 'http_only' => $cookie->isHttpOnly(), 'same_site' => $cookie->getSameSite()];
}
$middleware = $request->route() ? app('router')->gatherRouteMiddleware($request->route()) : [];
$handlerDirectory = null;
if ($request->hasSession() && $request->session()->getHandler() instanceof \Illuminate\Session\FileSessionHandler) {
$property = new \ReflectionProperty(\Illuminate\Session\FileSessionHandler::class, 'path');
$handlerDirectory = $property->getValue($request->session()->getHandler());
}
$entry = ['at_utc' => gmdate('c'), 'trace' => $trace, 'method' => $request->method(), 'path' => '/'.$request->path(),
'origin' => $request->getSchemeAndHttpHost(), 'cookie_name_counts' => $names,
'inertia' => $request->headers->has('X-Inertia'), 'json_expected' => $request->expectsJson(),
'qa_restored_account_rejected' => $request->attributes->get('qa_restored_account_rejected', false),
'loaded' => $request->attributes->get('qa_auth_loaded'), 'after' => $state, 'persisted' => $persisted,
'status' => $response->getStatusCode(), 'redirect' => self::destination($response->headers->get('Location') ?? $response->headers->get('X-Inertia-Location')),
'set_cookie_attributes' => $cookies, 'default_guard' => config('auth.defaults.guard'), 'fortify_guard' => config('fortify.guard'),
'session_driver' => config('session.driver'), 'lifetime_minutes' => config('session.lifetime'),
'handler_directory_matches_config' => $handlerDirectory === config('session.files'),
'middleware' => $middleware,
'key_fingerprint' => hash('sha256', app('encrypter')->getKey()),
'qa_target' => config('database.connections.pgsql.database').':'.config('database.connections.pgsql.port'),
'config_cached' => app()->configurationIsCached()];
$file = storage_path('app/private/investmoz-v2/portal-audit/auth-session-trace-qa.jsonl');
if (! is_file($file) || filesize($file) < 5 * 1024 * 1024) {
file_put_contents($file, json_encode($entry, JSON_THROW_ON_ERROR).PHP_EOL, FILE_APPEND | LOCK_EX);
}
$response->headers->set('X-Investmoz-QA-Auth-Trace', $trace);
return $response;
}
}
class PortalQaSessionTrace
{
public function handle(Request $request, Closure $next): Response
{
if ($request->attributes->has('qa_auth_trace')) {
$request->attributes->set('qa_auth_loaded', PortalQaAuthTrace::state($request));
}
return $next($request);
}
}