#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::sync::Arc;
use chronix::chronix_security::audit::{
AuditAction, AuditDecision, AuditEvent, AuditLogger, AuditSink, MemorySink, TracingSink,
};
struct SinkWrapper(Arc<MemorySink>);
impl AuditSink for SinkWrapper {
fn emit(&self, event: &AuditEvent) -> chronix::chronix_security::audit::error::Result<()> {
self.0.emit(event)
}
fn flush(&self) -> chronix::chronix_security::audit::error::Result<()> {
self.0.flush()
}
}
fn main() {
println!("=== Chronix Audit Logging ===\n");
println!("--- Setting Up Audit Logger ---");
let logger = AuditLogger::new();
let memory_sink = Arc::new(MemorySink::new(1000));
logger.add_sink(Box::new(SinkWrapper(Arc::clone(&memory_sink))));
logger.add_sink(Box::new(TracingSink));
println!("Logger configured with {} sinks\n", logger.sink_count());
println!("--- Logging Events ---");
logger.log(
AuditEvent::new(
"alice",
AuditAction::Read,
"cpu_metrics",
AuditDecision::Allow,
)
.with_source_ip("192.168.1.10")
.with_request_id("req-001")
.with_metadata("query_type", "range")
.with_metadata("time_range", "last_1h"),
);
println!(" Logged: alice READ cpu_metrics → Allow");
logger.log(
AuditEvent::new(
"bob",
AuditAction::Write,
"temperature",
AuditDecision::Allow,
)
.with_source_ip("192.168.1.20")
.with_request_id("req-002")
.with_metadata("points", "1000"),
);
println!(" Logged: bob WRITE temperature → Allow");
logger.log(
AuditEvent::new(
"carol",
AuditAction::Delete,
"secret_metrics",
AuditDecision::Deny,
)
.with_source_ip("10.0.0.5")
.with_request_id("req-003")
.with_metadata("reason", "insufficient_permissions"),
);
println!(" Logged: carol DELETE secret_metrics → Deny");
logger.log(
AuditEvent::new("admin", AuditAction::Admin, "system", AuditDecision::Allow)
.with_source_ip("127.0.0.1")
.with_metadata("action_detail", "config_update"),
);
println!(" Logged: admin ADMIN system → Allow");
logger.log(
AuditEvent::new(
"alice",
AuditAction::Custom("forecast".into()),
"cpu_metrics",
AuditDecision::Allow,
)
.with_request_id("req-004")
.with_metadata("model", "ARIMA"),
);
println!(" Logged: alice forecast cpu_metrics → Allow");
logger.log(
AuditEvent::new(
"bob",
AuditAction::SchemaChange,
"temperature",
AuditDecision::Allow,
)
.with_request_id("req-005")
.with_metadata("column", "celsius"),
);
println!(" Logged: bob SCHEMA_CHANGE temperature → Allow");
logger.log(
AuditEvent::new(
"eve",
AuditAction::LoginFailure,
"auth",
AuditDecision::Deny,
)
.with_source_ip("203.0.113.50")
.with_metadata("reason", "invalid_credentials"),
);
println!(" Logged: eve LOGIN_FAILURE → Deny");
logger.log(
AuditEvent::new(
"admin",
AuditAction::ApiKeyCreate,
"api_key:ingest",
AuditDecision::Allow,
)
.with_metadata("namespaces", "tenant-a"),
);
println!(" Logged: admin API_KEY_CREATE api_key:ingest → Allow");
logger.log(
AuditEvent::new(
"system",
AuditAction::DataExport,
"all_metrics",
AuditDecision::Allow,
)
.with_metadata("format", "parquet")
.with_metadata("destination", "s3://backup"),
);
println!(" Logged: system DATA_EXPORT all_metrics → Allow");
logger.flush();
println!("\n--- Querying Audit Trail ---");
let all_events = memory_sink.events();
println!("Total events logged: {}", all_events.len());
let alice_events = memory_sink.query_by_principal("alice");
println!("\nAlice's events ({}):", alice_events.len());
for event in &alice_events {
println!(
" {:?} on {} → {:?}",
event.action, event.resource, event.decision
);
}
let denied = memory_sink.query_by_decision(AuditDecision::Deny);
println!("\nDenied events ({}):", denied.len());
for event in &denied {
println!(
" {} attempted {:?} on {} (IP: {:?})",
event.principal,
event.action,
event.resource,
event.source_ip.as_deref().unwrap_or("unknown")
);
}
let cpu_events = memory_sink.query_by_resource("cpu_metrics");
println!("\nEvents on cpu_metrics ({}):", cpu_events.len());
for event in &cpu_events {
println!(
" {} → {:?} → {:?}",
event.principal, event.action, event.decision
);
}
println!("\n--- Sequence Tracking ---");
println!("Next sequence number: {}", logger.next_sequence());
println!("All events have monotonically increasing IDs:");
for (i, event) in all_events.iter().enumerate().take(5) {
println!(
" Event {i}: id={}, principal={}",
event.id, event.principal
);
}
println!("\n--- Event Metadata ---");
for event in &all_events {
if !event.metadata.is_empty() {
println!(
" {} {:?} → metadata: {:?}",
event.principal, event.action, event.metadata
);
}
}
println!("\n✓ Audit logging complete");
}