gomcp/internal/domain/soc/retention.go

138 lines
3.5 KiB
Go

package soc
import (
"sync"
"time"
)
// DataRetentionPolicy implements §19 — configurable data lifecycle management.
// Enforces retention windows and auto-archives/purges old events.
type DataRetentionPolicy struct {
mu sync.RWMutex
policies map[string]RetentionRule
}
// RetentionRule defines how long data of a given type is kept.
type RetentionRule struct {
DataType string `json:"data_type"` // events, incidents, audit, anomaly_alerts
RetainDays int `json:"retain_days"` // Max age in days
Action string `json:"action"` // archive, delete, compress
Enabled bool `json:"enabled"`
LastRun time.Time `json:"last_run"`
ItemsPurged int `json:"items_purged"`
}
// NewDataRetentionPolicy creates default retention rules.
func NewDataRetentionPolicy() *DataRetentionPolicy {
return &DataRetentionPolicy{
policies: map[string]RetentionRule{
"events": {
DataType: "events",
RetainDays: 90,
Action: "archive",
Enabled: true,
},
"incidents": {
DataType: "incidents",
RetainDays: 365,
Action: "archive",
Enabled: true,
},
"audit": {
DataType: "audit",
RetainDays: 730, // 2 years for compliance
Action: "compress",
Enabled: true,
},
"anomaly_alerts": {
DataType: "anomaly_alerts",
RetainDays: 30,
Action: "delete",
Enabled: true,
},
"playbook_log": {
DataType: "playbook_log",
RetainDays: 180,
Action: "archive",
Enabled: true,
},
},
}
}
// SetPolicy updates a retention rule.
func (d *DataRetentionPolicy) SetPolicy(dataType string, retainDays int, action string) {
d.mu.Lock()
defer d.mu.Unlock()
d.policies[dataType] = RetentionRule{
DataType: dataType,
RetainDays: retainDays,
Action: action,
Enabled: true,
}
}
// GetPolicy returns the retention rule for a data type.
func (d *DataRetentionPolicy) GetPolicy(dataType string) (RetentionRule, bool) {
d.mu.RLock()
defer d.mu.RUnlock()
r, ok := d.policies[dataType]
return r, ok
}
// ListPolicies returns all retention policies.
func (d *DataRetentionPolicy) ListPolicies() []RetentionRule {
d.mu.RLock()
defer d.mu.RUnlock()
result := make([]RetentionRule, 0, len(d.policies))
for _, r := range d.policies {
result = append(result, r)
}
return result
}
// IsExpired checks if a timestamp has exceeded the retention window.
func (d *DataRetentionPolicy) IsExpired(dataType string, timestamp time.Time) bool {
d.mu.RLock()
defer d.mu.RUnlock()
r, ok := d.policies[dataType]
if !ok || !r.Enabled {
return false
}
cutoff := time.Now().AddDate(0, 0, -r.RetainDays)
return timestamp.Before(cutoff)
}
// Enforce runs retention checks and returns items to purge.
// In production, this would interact with the database.
func (d *DataRetentionPolicy) Enforce(dataType string, timestamps []time.Time) (expired int) {
d.mu.Lock()
defer d.mu.Unlock()
r, ok := d.policies[dataType]
if !ok || !r.Enabled {
return 0
}
cutoff := time.Now().AddDate(0, 0, -r.RetainDays)
for _, t := range timestamps {
if t.Before(cutoff) {
expired++
}
}
r.LastRun = time.Now()
r.ItemsPurged += expired
d.policies[dataType] = r
return expired
}
// RetentionStats returns retention policy statistics.
func (d *DataRetentionPolicy) RetentionStats() map[string]any {
d.mu.RLock()
defer d.mu.RUnlock()
return map[string]any{
"total_policies": len(d.policies),
"policies": d.policies,
}
}