72 lines
2.2 KiB
TypeScript
72 lines
2.2 KiB
TypeScript
|
|
/**
|
||
|
|
* 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<void> {
|
||
|
|
// 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 })
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|