use security_core::classification::DataClassification;
use security_core::severity::SecuritySeverity;
use security_events::event::{EventOutcome, EventValue, SecurityEvent};
use security_events::kind::EventKind;
use security_events::sink::SecuritySink;
use serde::Serialize;
use time::Duration;
use time::OffsetDateTime;
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
#[non_exhaustive]
pub enum RetentionStatus {
Active,
Expired,
NoPolicy,
}
pub struct RetentionPolicy {
retention_days: u64,
label: String,
}
impl RetentionPolicy {
#[must_use]
pub fn new(retention_days: u64, label: &str) -> Self {
Self {
retention_days,
label: label.to_string(),
}
}
#[must_use]
pub fn retention_days(&self) -> u64 {
self.retention_days
}
pub fn check_status(
&self,
created_at: OffsetDateTime,
now: OffsetDateTime,
sink: &dyn SecuritySink,
) -> RetentionStatus {
let age = now - created_at;
let limit = Duration::days(self.retention_days as i64);
if age > limit {
let mut event = SecurityEvent::new(
EventKind::RetentionExpiry,
SecuritySeverity::Medium,
EventOutcome::Failure,
);
event.labels.insert(
"policy_label".to_string(),
EventValue::Classified {
value: self.label.clone(),
classification: DataClassification::Internal,
},
);
event.labels.insert(
"retention_days".to_string(),
EventValue::Classified {
value: self.retention_days.to_string(),
classification: DataClassification::Internal,
},
);
event.labels.insert(
"data_age_days".to_string(),
EventValue::Classified {
value: age.whole_days().to_string(),
classification: DataClassification::Internal,
},
);
sink.write_event(&event);
RetentionStatus::Expired
} else {
RetentionStatus::Active
}
}
}
#[must_use]
pub fn check_no_policy() -> RetentionStatus {
RetentionStatus::NoPolicy
}