push newest version
This commit is contained in:
126
backend/internal/core/audit.go
Normal file
126
backend/internal/core/audit.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
var berlinLocation *time.Location
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
berlinLocation, err = time.LoadLocation("Europe/Berlin")
|
||||
if err != nil {
|
||||
// Fallback zu UTC falls Europe/Berlin nicht verfügbar ist
|
||||
log.Printf("Warnung: Europe/Berlin Zeitzone nicht verfügbar, verwende UTC: %v", err)
|
||||
berlinLocation = time.UTC
|
||||
}
|
||||
}
|
||||
|
||||
// AuditService handles audit logging
|
||||
type AuditService struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// NewAuditService creates a new AuditService instance
|
||||
func NewAuditService(db *sql.DB) *AuditService {
|
||||
return &AuditService{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// Track logs an audit event asynchronously
|
||||
// ctx: context for the operation
|
||||
// action: the action performed (e.g., "CREATE", "UPDATE", "DELETE")
|
||||
// entity: the entity type (e.g., "user", "space", "fqdn", "csr")
|
||||
// entityID: the ID of the entity (optional)
|
||||
// userID: the ID of the user performing the action (optional)
|
||||
// username: the username of the user performing the action (optional)
|
||||
// details: additional details as a map (will be stored as JSON)
|
||||
// ipAddress: IP address of the request (optional)
|
||||
// userAgent: User-Agent header (optional)
|
||||
func (s *AuditService) Track(ctx context.Context, action, entity, entityID, userID, username string, details map[string]interface{}, ipAddress, userAgent string) {
|
||||
// Check if service is nil (should not happen, but safety check)
|
||||
if s == nil {
|
||||
log.Printf("Warnung: AuditService ist nil, kann kein Audit-Log schreiben")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if database is available
|
||||
if s.db == nil {
|
||||
log.Printf("Warnung: Datenbank ist nil im AuditService, kann kein Audit-Log schreiben")
|
||||
return
|
||||
}
|
||||
|
||||
// Execute asynchronously in a goroutine to not block the main operation
|
||||
go func() {
|
||||
if err := s.trackSync(ctx, action, entity, entityID, userID, username, details, ipAddress, userAgent); err != nil {
|
||||
// Log errors but don't fail the main operation
|
||||
log.Printf("Fehler beim Schreiben des Audit-Logs: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// trackSync performs the actual database write synchronously
|
||||
func (s *AuditService) trackSync(ctx context.Context, action, entity, entityID, userID, username string, details map[string]interface{}, ipAddress, userAgent string) error {
|
||||
// Safety check: ensure service and database are not nil
|
||||
if s == nil {
|
||||
return fmt.Errorf("AuditService ist nil")
|
||||
}
|
||||
if s.db == nil {
|
||||
return fmt.Errorf("Datenbank ist nil im AuditService")
|
||||
}
|
||||
|
||||
// Generate unique ID for the log entry
|
||||
logID := uuid.New().String()
|
||||
|
||||
// Format timestamp for SQLite - verwende Europe/Berlin Zeitzone
|
||||
// Speichere als ISO-String mit Zeitzone für bessere Kompatibilität
|
||||
// Format: YYYY-MM-DD HH:MM:SS (SQLite DATETIME Format)
|
||||
// Die Zeit wird in Europe/Berlin Zeitzone gespeichert
|
||||
now := time.Now().In(berlinLocation)
|
||||
timestamp := now.Format("2006-01-02 15:04:05")
|
||||
|
||||
// Marshal details to JSON
|
||||
var detailsJSON string
|
||||
if details != nil && len(details) > 0 {
|
||||
jsonBytes, err := json.Marshal(details)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Fehler beim Marshalling der Details: %w", err)
|
||||
}
|
||||
detailsJSON = string(jsonBytes)
|
||||
}
|
||||
|
||||
// Create context with timeout for database operation
|
||||
dbCtx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
|
||||
// Insert audit log entry
|
||||
result, err := s.db.ExecContext(dbCtx,
|
||||
"INSERT INTO audit_logs (id, timestamp, user_id, username, action, resource_type, resource_id, details, ip_address, user_agent) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
logID, timestamp, userID, username, action, entity, entityID, detailsJSON, ipAddress, userAgent)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Fehler beim INSERT: %w", err)
|
||||
}
|
||||
|
||||
// Verify that the insert was successful
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Fehler beim Prüfen der RowsAffected: %w", err)
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return fmt.Errorf("Keine Zeile eingefügt")
|
||||
}
|
||||
|
||||
log.Printf("Audit-Log geschrieben: %s %s %s (ID: %s)", action, entity, entityID, logID)
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user