vault_audit_tools/commands/entity_analysis.rs
1//! Unified entity analysis command.
2//!
3//! Consolidates entity lifecycle tracking, creation analysis, preprocessing,
4//! gap detection, and timeline analysis into a single powerful command with
5//! intelligent auto-preprocessing to eliminate multi-step workflows.
6//!
7//! # Usage
8//!
9//! ```bash
10//! # Churn analysis (auto-preprocesses entity mappings)
11//! vault-audit entity-analysis churn logs/day1.log logs/day2.log
12//! vault-audit entity-analysis churn logs/*.log --baseline entities.json
13//!
14//! # Creation analysis by auth path
15//! vault-audit entity-analysis creation logs/*.log
16//! vault-audit entity-analysis creation logs/*.log --export creation_data.json
17//!
18//! # Extract entity mappings (preprocessing)
19//! vault-audit entity-analysis preprocess logs/*.log --output mappings.json
20//! vault-audit entity-analysis preprocess logs/*.log --format csv
21//!
22//! # Detect activity gaps for entities
23//! vault-audit entity-analysis gaps logs/*.log --window-seconds 300
24//!
25//! # Individual entity timeline
26//! vault-audit entity-analysis timeline logs/*.log --entity-id abc-123
27//! ```
28//!
29//! **Key Improvement**: Auto-preprocessing eliminates the need for separate
30//! preprocessing steps. Entity mappings are built in-memory automatically when
31//! needed by churn or creation analysis.
32//!
33//! # Subcommands
34//!
35//! ## churn
36//! Multi-day entity lifecycle tracking with ephemeral pattern detection.
37//! Automatically preprocesses entity mappings unless `--no-auto-preprocess` is specified.
38//!
39//! ## creation
40//! Analyzes when entities were first created, grouped by authentication path.
41//! Shows new entity onboarding patterns and growth trends.
42//!
43//! ## preprocess
44//! Extracts entity-to-display-name mappings from audit logs for external use.
45//! Outputs JSON or CSV format for integration with other tools.
46//!
47//! ## gaps
48//! Detects entities with suspicious activity gaps (potential compromised credentials
49//! or entities that should have been cleaned up).
50//!
51//! ## timeline
52//! Shows chronological activity for a specific entity ID, useful for debugging
53//! or investigating specific identity issues.
54
55use anyhow::Result;
56use std::fs::File;
57use std::io::Write;
58
59/// Helper to write entity map to temp JSON file for commands that expect file paths
60fn write_temp_entity_map(
61 entity_map: &std::collections::HashMap<
62 String,
63 crate::commands::preprocess_entities::EntityMapping,
64 >,
65) -> Result<String> {
66 let temp_path = format!(".vault-audit-autopreprocess-{}.json", std::process::id());
67
68 let file = File::create(&temp_path)?;
69 let mut writer = std::io::BufWriter::new(file);
70 let json = serde_json::to_string_pretty(&entity_map)?;
71 writer.write_all(json.as_bytes())?;
72 writer.flush()?;
73
74 Ok(temp_path)
75}
76
77/// Run churn analysis subcommand
78pub fn run_churn(
79 log_files: &[String],
80 entity_map: Option<&String>,
81 baseline: Option<&String>,
82 output: Option<&String>,
83 format: Option<&String>,
84 auto_preprocess: bool,
85) -> Result<()> {
86 // Auto-preprocessing: build entity map in-memory and write to temp file
87 let temp_map_file = if auto_preprocess && entity_map.is_none() {
88 eprintln!("Auto-preprocessing: Building entity mappings in-memory...");
89 let map = crate::commands::preprocess_entities::build_entity_map(log_files)?;
90 let temp_path = write_temp_entity_map(&map)?;
91 eprintln!("Entity mappings ready\n");
92 Some(temp_path)
93 } else {
94 None
95 };
96
97 // Use provided map or auto-generated temp map
98 let map_to_use = entity_map.map(|s| s.as_str()).or(temp_map_file.as_deref());
99
100 // Delegate to existing entity_churn implementation
101 let result = crate::commands::entity_churn::run(
102 log_files,
103 map_to_use,
104 baseline.map(|s| s.as_str()),
105 output.map(|s| s.as_str()),
106 format.map(|s| s.as_str()),
107 );
108
109 // Cleanup temp file
110 if let Some(temp) = temp_map_file {
111 let _ = std::fs::remove_file(temp);
112 }
113
114 result
115}
116
117/// Run creation analysis subcommand
118pub fn run_creation(
119 log_files: &[String],
120 entity_map: Option<&String>,
121 output: Option<&String>,
122 auto_preprocess: bool,
123) -> Result<()> {
124 // Auto-preprocessing: build entity map in-memory and write to temp file
125 let temp_map_file = if auto_preprocess && entity_map.is_none() {
126 eprintln!("Auto-preprocessing: Building entity mappings in-memory...");
127 let map = crate::commands::preprocess_entities::build_entity_map(log_files)?;
128 let temp_path = write_temp_entity_map(&map)?;
129 eprintln!("Entity mappings ready\n");
130 Some(temp_path)
131 } else {
132 None
133 };
134
135 // Use provided map or auto-generated temp map
136 let map_to_use = entity_map.map(|s| s.as_str()).or(temp_map_file.as_deref());
137
138 // Delegate to existing entity_creation implementation
139 let result =
140 crate::commands::entity_creation::run(log_files, map_to_use, output.map(|s| s.as_str()));
141
142 // Cleanup temp file
143 if let Some(temp) = temp_map_file {
144 let _ = std::fs::remove_file(temp);
145 }
146
147 result
148}
149
150/// Run preprocess subcommand
151pub fn run_preprocess(log_files: &[String], output: &str, format: &str) -> Result<()> {
152 // Delegate to existing preprocess_entities implementation
153 crate::commands::preprocess_entities::run(log_files, output, format)
154}
155
156/// Run gaps detection subcommand
157pub fn run_gaps(log_files: &[String], window_seconds: u64) -> Result<()> {
158 // Delegate to existing entity_gaps implementation
159 crate::commands::entity_gaps::run(log_files, window_seconds)
160}
161
162/// Run timeline subcommand
163pub fn run_timeline(
164 log_files: &[String],
165 entity_id: &str,
166 display_name: Option<&String>,
167) -> Result<()> {
168 // Convert Option<&String> to &Option<String> for compatibility
169 let display_name_owned = display_name.cloned();
170 // Delegate to existing entity_timeline implementation
171 crate::commands::entity_timeline::run(log_files, entity_id, &display_name_owned)
172}