-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaudit.go
More file actions
150 lines (129 loc) · 3.97 KB
/
audit.go
File metadata and controls
150 lines (129 loc) · 3.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Package paywall implements audit logging for escrow operations
package paywall
import (
"crypto/rand"
"encoding/hex"
"fmt"
"sync"
"time"
)
// AuditLogger defines the interface for audit trail operations
// Implementations must ensure append-only semantics and thread-safety
type AuditLogger interface {
// LogAction records an action in the audit trail
// Returns the audit entry ID and any error encountered
LogAction(entry *AuditLogEntry) (string, error)
// GetAuditTrail retrieves all audit entries for a payment
// Returns entries in chronological order
GetAuditTrail(paymentID string) ([]*AuditLogEntry, error)
// GetAllEntries retrieves all audit log entries
// Returns entries in chronological order
GetAllEntries() ([]*AuditLogEntry, error)
// Close closes the audit logger and releases resources
// Should be called when shutting down to ensure data persistence
Close() error
}
// MemoryAuditLogger is an in-memory implementation of AuditLogger
// WARNING: Audit logs are not persisted and will be lost on restart
// Use only for testing; production should use persistent storage
type MemoryAuditLogger struct {
mu sync.RWMutex
entries []*AuditLogEntry
}
// NewMemoryAuditLogger creates a new in-memory audit logger
func NewMemoryAuditLogger() *MemoryAuditLogger {
return &MemoryAuditLogger{
entries: make([]*AuditLogEntry, 0),
}
}
// LogAction records an action in the audit trail.
// Automatically generates an ID and timestamp if not provided.
// Returns the audit entry ID.
//
// Parameters:
// - entry: The audit log entry to record
//
// Returns:
// - string: The unique ID assigned to this entry
// - error: Any error encountered during logging
//
// Thread-safety: Protected by write lock
func (m *MemoryAuditLogger) LogAction(entry *AuditLogEntry) (string, error) {
if entry == nil {
return "", fmt.Errorf("audit entry cannot be nil")
}
// Generate ID if not provided
if entry.ID == "" {
id, err := generateAuditID()
if err != nil {
return "", fmt.Errorf("failed to generate audit ID: %w", err)
}
entry.ID = id
}
// Set timestamp if not provided
if entry.Timestamp.IsZero() {
entry.Timestamp = time.Now()
}
m.mu.Lock()
defer m.mu.Unlock()
// Append-only: create defensive copy and append
entryCopy := *entry
m.entries = append(m.entries, &entryCopy)
return entry.ID, nil
}
// GetAuditTrail retrieves all audit entries for a specific payment.
// Returns entries in chronological order (oldest first).
//
// Parameters:
// - paymentID: The payment ID to filter by
//
// Returns:
// - []*AuditLogEntry: Slice of audit entries for the payment
// - error: Always nil in this implementation
//
// Thread-safety: Protected by read lock
func (m *MemoryAuditLogger) GetAuditTrail(paymentID string) ([]*AuditLogEntry, error) {
m.mu.RLock()
defer m.mu.RUnlock()
var trail []*AuditLogEntry
for _, entry := range m.entries {
if entry.PaymentID == paymentID {
// Return defensive copy
entryCopy := *entry
trail = append(trail, &entryCopy)
}
}
return trail, nil
}
// GetAllEntries retrieves all audit log entries.
// Returns entries in chronological order (oldest first).
//
// Returns:
// - []*AuditLogEntry: Slice of all audit entries
// - error: Always nil in this implementation
//
// Thread-safety: Protected by read lock
func (m *MemoryAuditLogger) GetAllEntries() ([]*AuditLogEntry, error) {
m.mu.RLock()
defer m.mu.RUnlock()
// Return defensive copies
result := make([]*AuditLogEntry, len(m.entries))
for i, entry := range m.entries {
entryCopy := *entry
result[i] = &entryCopy
}
return result, nil
}
// Close is a no-op for MemoryAuditLogger as there are no resources to release
// Implements AuditLogger.Close
func (m *MemoryAuditLogger) Close() error {
return nil
}
// generateAuditID creates a unique identifier for an audit log entry
func generateAuditID() (string, error) {
bytes := make([]byte, 16)
if _, err := rand.Read(bytes); err != nil {
return "", err
}
return "audit_" + hex.EncodeToString(bytes), nil
}