Files
atlas/internal/audit/store.go
othman.suseno 54e76d9304
Some checks failed
CI / test-build (push) Failing after 2m1s
add authentication method
2025-12-14 23:55:12 +07:00

107 lines
2.2 KiB
Go

package audit
import (
"fmt"
"sync"
"time"
"gitea.avt.data-center.id/othman.suseno/atlas/internal/models"
)
// Store manages audit logs
type Store struct {
mu sync.RWMutex
logs []models.AuditLog
nextID int64
maxLogs int // Maximum number of logs to keep (0 = unlimited)
}
// NewStore creates a new audit log store
func NewStore(maxLogs int) *Store {
return &Store{
logs: make([]models.AuditLog, 0),
nextID: 1,
maxLogs: maxLogs,
}
}
// Log records an audit log entry
func (s *Store) Log(actor, action, resource, result, message, ip, userAgent string) *models.AuditLog {
s.mu.Lock()
defer s.mu.Unlock()
id := fmt.Sprintf("audit-%d", s.nextID)
s.nextID++
entry := models.AuditLog{
ID: id,
Actor: actor,
Action: action,
Resource: resource,
Result: result,
Message: message,
IP: ip,
UserAgent: userAgent,
Timestamp: time.Now(),
}
s.logs = append(s.logs, entry)
// Enforce max logs limit
if s.maxLogs > 0 && len(s.logs) > s.maxLogs {
// Remove oldest logs
excess := len(s.logs) - s.maxLogs
s.logs = s.logs[excess:]
}
return &entry
}
// List returns audit logs, optionally filtered
func (s *Store) List(actor, action, resource string, limit int) []models.AuditLog {
s.mu.RLock()
defer s.mu.RUnlock()
var filtered []models.AuditLog
for i := len(s.logs) - 1; i >= 0; i-- { // Reverse iteration (newest first)
log := s.logs[i]
if actor != "" && log.Actor != actor {
continue
}
if action != "" && log.Action != action {
continue
}
if resource != "" && !containsResource(log.Resource, resource) {
continue
}
filtered = append(filtered, log)
if limit > 0 && len(filtered) >= limit {
break
}
}
return filtered
}
// Get returns a specific audit log by ID
func (s *Store) Get(id string) (*models.AuditLog, error) {
s.mu.RLock()
defer s.mu.RUnlock()
for _, log := range s.logs {
if log.ID == id {
return &log, nil
}
}
return nil, fmt.Errorf("audit log %s not found", id)
}
// containsResource checks if resource string contains the search term
func containsResource(resource, search string) bool {
return resource == search ||
(len(resource) > len(search) && resource[:len(search)] == search)
}