diff --git a/public/index.php b/public/index.php index f945313..81dc4f7 100644 --- a/public/index.php +++ b/public/index.php @@ -138,6 +138,25 @@ if ($route === 'attachment') { readfile($path); exit; } +if ($route === 'credential_reveal') { + require_permission('credentials.view'); + if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') { http_response_code(405); exit('Reveal requires POST'); } + verify_csrf(); + $credentialId = filter_var(scalar_input($_POST['credential_id'] ?? null), FILTER_VALIDATE_INT); + $credentialStmt = db()->prepare('SELECT id, client_id, secret_ciphertext FROM credentials WHERE id = :id AND is_active = 1'); + $credentialStmt->execute(['id' => $credentialId]); + $credential = $credentialStmt->fetch(); + if (!$credential || !can_access_client((int)$credential['client_id'])) { http_response_code(404); header('Content-Type: application/json'); echo json_encode(['ok' => false, 'error' => 'Credential not found.']); exit; } + try { + $secret = (new \App\Domain\Credential\CredentialVault())->decrypt($credential['secret_ciphertext']); + audit('credential_revealed', 'credential', (int)$credential['id'], ['client_id' => (int)$credential['client_id']]); + header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0'); + header('Content-Type: application/json; charset=UTF-8'); + echo json_encode(['ok' => true, 'secret' => $secret], JSON_THROW_ON_ERROR); + } catch (Throwable) { http_response_code(500); header('Content-Type: application/json'); echo json_encode(['ok' => false, 'error' => 'Credential could not be decrypted.']); } + exit; +} + if ($route === 'client_history') { require_permission('clients.view'); $clientId = filter_var(scalar_input($_GET['id'] ?? null), FILTER_VALIDATE_INT); @@ -529,7 +548,7 @@ if ($route === 'client') { $credentialRows = $credentialStmt->fetchAll(); } render_header('Client details'); - echo '
'; + echo ''; echo 'No contacts recorded.
'; foreach ($contacts as $contact) { echo '' . e($revealedCredential['secret']) . '' : '••••••••••••••••••••') . '