b25e074e1d
Implements Phase 4 of v0.1 Foundation: - internal/store/migrations/0003_audit_log.sql: audit_log table with indexes - internal/store/audit_repo.go: AuditRepo (Append + List) - internal/store/audit_repo_test.go: 2 tests for audit persistence - internal/engine/audit.go: Audit wrapper that persists to SQLite AND logs via slog - internal/cli/audit.go: orca audit list command (text + JSON) - Registry now records every join/leave/forget with actor/action/resource/result Verified: audit entries persist across restarts, JSON output includes metadata, node operations emit audit records. All tests pass with -race. ---ci--- project: orca phase: 4 milestone: v0.1 status: execute req_covered: - REQ-005 - REQ-006 - REQ-008 - REQ-017 - REQ-018 ---/ci---
53 lines
1.2 KiB
Go
53 lines
1.2 KiB
Go
package engine
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
|
|
"git.cloudinit.dev/coreci/orca/internal/store"
|
|
)
|
|
|
|
// Audit wraps a slog.Logger and persists structured audit entries to SQLite.
|
|
type Audit struct {
|
|
repo *store.AuditRepo
|
|
log *slog.Logger
|
|
}
|
|
|
|
func NewAudit(repo *store.AuditRepo, log *slog.Logger) *Audit {
|
|
if log == nil {
|
|
log = slog.Default()
|
|
}
|
|
return &Audit{repo: repo, log: log}
|
|
}
|
|
|
|
func (a *Audit) Record(ctx context.Context, actor, action, resource, result string, err error, meta map[string]any) {
|
|
entry := &store.AuditEntry{
|
|
Actor: actor,
|
|
Action: action,
|
|
Resource: resource,
|
|
Result: result,
|
|
Metadata: meta,
|
|
}
|
|
if err != nil {
|
|
entry.Error = err.Error()
|
|
}
|
|
if persistErr := a.repo.Append(ctx, entry); persistErr != nil {
|
|
a.log.Error("audit persist failed",
|
|
slog.String("action", action),
|
|
slog.String("resource", resource),
|
|
slog.String("error", persistErr.Error()))
|
|
}
|
|
attrs := []any{
|
|
slog.String("actor", actor),
|
|
slog.String("action", action),
|
|
slog.String("resource", resource),
|
|
slog.String("result", result),
|
|
}
|
|
if err != nil {
|
|
attrs = append(attrs, slog.String("error", err.Error()))
|
|
a.log.Warn("audit", attrs...)
|
|
} else {
|
|
a.log.Info("audit", attrs...)
|
|
}
|
|
}
|