// audit.go provides comprehensive audit trail logging functionality for compliance and monitoring. // This file now uses the `log/slog` structured logging package introduced in Go 1.21. // A single environment variable LOG_FORMAT controls the output format: // // LOG_FORMAT=text -> human‑readable text (default) // LOG_FORMAT=json -> JSON // // The public API (type AuditLogger and all its methods) is unchanged, so no call‑sites // (e.g. cognito-permit-sync.go) need to be modified. package main import ( "context" "log/slog" "os" ) const envLogFormat = "LOG_FORMAT" // AuditLogger handles audit‑trail logging for compliance. type AuditLogger struct { logger *slog.Logger file *os.File } // NewAuditLogger initializes the audit logger. // The signature is unchanged: pass isDryRun=true to write to dry-run-audit.log; // otherwise logs are appended to audit.log in the working directory. func NewAuditLogger(isDryRun bool) (*AuditLogger, error) { filename := "audit.log" if isDryRun { filename = "dry-run-audit.log" } file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { return nil, err } // Choose handler based on LOG_FORMAT var handler slog.Handler switch os.Getenv(envLogFormat) { case "json": handler = slog.NewJSONHandler(file, &slog.HandlerOptions{AddSource: false}) default: // "text" or unset handler = slog.NewTextHandler(file, &slog.HandlerOptions{AddSource: false}) } logger := slog.New(handler) return &AuditLogger{ logger: logger, file: file, }, nil } // Close closes the underlying audit file. func (a *AuditLogger) Close() error { if a.file != nil { return a.file.Close() } return nil } // ----- Public logging helpers ------------------------------------------------ // ActionType represents the type of action being logged. type ActionType string const ( ActionCreate ActionType = "CREATE" ActionDelete ActionType = "DELETE" ActionDisable ActionType = "DISABLE" ActionEnable ActionType = "ENABLE" ActionAssignRole ActionType = "ASSIGN_ROLE" ActionUnassignRole ActionType = "UNASSIGN_ROLE" ActionUpdate ActionType = "UPDATE" ActionSkip ActionType = "SKIP" ) // SystemType represents the system where the action occurred. type SystemType string const ( SystemCognito SystemType = "COGNITO" SystemPermit SystemType = "PERMIT" ) // ResultType represents the outcome of the action. type ResultType string const ( ResultSuccess ResultType = "SUCCESS" ResultFailure ResultType = "FAILURE" ResultSkipped ResultType = "SKIPPED" ) // LogAction logs a generic action (without subjectID or role). func (a *AuditLogger) LogAction(action ActionType, system SystemType, userEmail string, result ResultType, details string) { a.logger.InfoContext(context.TODO(), "audit_event", "action", action, "system", system, "userEmail", userEmail, "result", result, "details", details, ) } // LogActionWithSubjectID logs an action involving a specific subject identifier. func (a *AuditLogger) LogActionWithSubjectID(action ActionType, system SystemType, userEmail, subjectID string, result ResultType, details string) { a.logger.InfoContext(context.TODO(), "audit_event", "action", action, "system", system, "userEmail", userEmail, "subjectID", subjectID, "result", result, "details", details, ) } // LogRoleAction logs role assignment/unassignment activities. func (a *AuditLogger) LogRoleAction(action ActionType, userEmail, role string, result ResultType, details string) { a.logger.InfoContext(context.TODO(), "audit_event", "action", action, "system", SystemPermit, "userEmail", userEmail, "role", role, "result", result, "details", details, ) } // LogDryRunStart marks the beginning of a dry‑run execution. func (a *AuditLogger) LogDryRunStart() { a.logger.InfoContext(context.TODO(), "dry_run_start") } // LogDryRunEnd marks the completion of a dry‑run execution. func (a *AuditLogger) LogDryRunEnd() { a.logger.InfoContext(context.TODO(), "dry_run_end") }