/** * Audit Middleware * * Automatically logs audit events for all requests * Per DoD/MilSpec requirements (NIST SP 800-53: AU-2, AU-3) */ import { FastifyRequest, FastifyReply } from 'fastify' import { logAuditEvent, logAuthentication, logDataAccess } from '../services/audit-logger' import { logger } from '../lib/logger' /** * Audit middleware - logs all requests for audit trail */ export async function auditMiddleware( request: FastifyRequest, reply: FastifyReply ): Promise { // Skip audit logging for health checks and WebSocket upgrades if (request.url === '/health' || request.url === '/graphql-ws') { return } const user = (request as any).user const startTime = Date.now() // Log request try { // Determine event type based on request if (request.url === '/graphql') { // GraphQL request - log based on operation const body = request.body as any const operation = body?.operationName || 'UNKNOWN' await logAuditEvent({ eventType: 'DATA_ACCESS', result: reply.statusCode < 400 ? 'SUCCESS' : 'FAILURE', userId: user?.id, userName: user?.name, userRole: user?.role, ipAddress: request.ip, userAgent: request.headers['user-agent'], action: `GRAPHQL_${operation}`, details: { query: body?.query?.substring(0, 200), // Log first 200 chars of query variables: body?.variables ? 'PRESENT' : 'NONE', // Don't log full variables }, }) } else { // Regular HTTP request await logAuditEvent({ eventType: 'DATA_ACCESS', result: reply.statusCode < 400 ? 'SUCCESS' : 'FAILURE', userId: user?.id, userName: user?.name, userRole: user?.role, ipAddress: request.ip, userAgent: request.headers['user-agent'], action: `${request.method} ${request.url}`, details: { statusCode: reply.statusCode, responseTime: Date.now() - startTime, }, }) } } catch (error) { // Don't fail the request if audit logging fails, but log the error logger.error('Failed to log audit event', { error, request: request.url }) } }