rdev/internal/logging/audit.go
jordan d69da6d627 feat: add structured logging infrastructure and SDLC extensions
Major changes:
- Add internal/logging package with field constants, context propagation,
  sensitive data auto-redaction, and per-component log levels
- Add worker timeout constants (TimeoutQuickOp, TimeoutHealthCheck, etc.)
- Extend SDLC with callback handlers, generate endpoints, and executor
- Add new cookbook trees for aeries and slackpath progression
- Add skeleton templates for queue, realtime, and microservices
- Add worker component template with async job processing
- Refactor services and handlers to use new logging infrastructure
- Split component.go into component_infra.go and component_listing.go

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 22:56:04 -07:00

143 lines
3.9 KiB
Go

package logging
import (
"context"
"time"
)
// AuditAction represents a security-relevant action.
type AuditAction string
const (
AuditActionCreate AuditAction = "create"
AuditActionRead AuditAction = "read"
AuditActionUpdate AuditAction = "update"
AuditActionDelete AuditAction = "delete"
AuditActionLogin AuditAction = "login"
AuditActionLogout AuditAction = "logout"
AuditActionGrant AuditAction = "grant"
AuditActionRevoke AuditAction = "revoke"
AuditActionExecute AuditAction = "execute"
AuditActionDeploy AuditAction = "deploy"
AuditActionProvision AuditAction = "provision"
AuditActionDeprovision AuditAction = "deprovision"
)
// AuditResult represents the outcome of an audited action.
type AuditResult string
const (
AuditResultSuccess AuditResult = "success"
AuditResultFailure AuditResult = "failure"
AuditResultDenied AuditResult = "denied"
)
// AuditEvent represents a security audit event.
type AuditEvent struct {
// Timestamp is when the event occurred.
Timestamp time.Time
// Action is what was attempted.
Action AuditAction
// Resource is what the action was performed on.
Resource string
// ResourceID identifies the specific resource.
ResourceID string
// Result is the outcome of the action.
Result AuditResult
// UserID is who performed the action (empty for system actions).
UserID string
// APIKeyID is the API key used (empty for internal actions).
APIKeyID string
// RequestID correlates to the HTTP request.
RequestID string
// Details contains additional context.
Details map[string]any
}
// AuditLogger logs security-relevant events.
// Audit logs are always written regardless of log level.
type AuditLogger struct {
logger *Logger
}
// NewAuditLogger creates a new audit logger.
func NewAuditLogger(l *Logger) *AuditLogger {
return &AuditLogger{
logger: l.WithComponent("audit"),
}
}
// Log logs an audit event.
func (a *AuditLogger) Log(ctx context.Context, event AuditEvent) {
if event.Timestamp.IsZero() {
event.Timestamp = time.Now()
}
// Build attributes
attrs := []any{
FieldAuditAction, string(event.Action),
FieldAuditResource, event.Resource,
FieldAuditResult, string(event.Result),
"resource_id", event.ResourceID,
"timestamp", event.Timestamp.Format(time.RFC3339),
}
if event.UserID != "" {
attrs = append(attrs, FieldUserID, event.UserID)
}
if event.APIKeyID != "" {
attrs = append(attrs, FieldAPIKeyID, event.APIKeyID)
}
if event.RequestID != "" {
attrs = append(attrs, FieldRequestID, event.RequestID)
}
// Add details as individual fields
for k, v := range event.Details {
attrs = append(attrs, k, v)
}
// Audit logs are always Info level (never skipped)
a.logger.Info("audit event", attrs...)
}
// LogAction is a convenience method for logging simple actions.
func (a *AuditLogger) LogAction(ctx context.Context, action AuditAction, resource, resourceID string, result AuditResult) {
// Extract context values if available
l := FromContext(ctx)
event := AuditEvent{
Action: action,
Resource: resource,
ResourceID: resourceID,
Result: result,
}
// Try to extract request ID from the context logger
// This works because the middleware adds these fields
a.Log(ctx, event)
_ = l // We might want to extract fields from context in the future
}
// LogSuccess logs a successful action.
func (a *AuditLogger) LogSuccess(ctx context.Context, action AuditAction, resource, resourceID string) {
a.LogAction(ctx, action, resource, resourceID, AuditResultSuccess)
}
// LogFailure logs a failed action.
func (a *AuditLogger) LogFailure(ctx context.Context, action AuditAction, resource, resourceID string) {
a.LogAction(ctx, action, resource, resourceID, AuditResultFailure)
}
// LogDenied logs a denied action.
func (a *AuditLogger) LogDenied(ctx context.Context, action AuditAction, resource, resourceID string) {
a.LogAction(ctx, action, resource, resourceID, AuditResultDenied)
}