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...\n");
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
99 .map(std::string::String::as_str)
100 .or(temp_map_file.as_deref());
101
102 // Delegate to existing entity_churn implementation
103 let result = crate::commands::entity_churn::run(
104 log_files,
105 map_to_use,
106 baseline.map(std::string::String::as_str),
107 output.map(std::string::String::as_str),
108 format.map(std::string::String::as_str),
109 );
110
111 // Cleanup temp file
112 if let Some(temp) = temp_map_file {
113 let _ = std::fs::remove_file(temp);
114 }
115
116 result
117}
118
119/// Run creation analysis subcommand
120pub fn run_creation(
121 log_files: &[String],
122 entity_map: Option<&String>,
123 output: Option<&String>,
124 auto_preprocess: bool,
125) -> Result<()> {
126 // Auto-preprocessing: build entity map in-memory and write to temp file
127 let temp_map_file = if auto_preprocess && entity_map.is_none() {
128 eprintln!("Auto-preprocessing: Building entity mappings in-memory...\n");
129 let map = crate::commands::preprocess_entities::build_entity_map(log_files)?;
130 let temp_path = write_temp_entity_map(&map)?;
131 eprintln!("Entity mappings ready\n");
132 Some(temp_path)
133 } else {
134 None
135 };
136
137 // Use provided map or auto-generated temp map
138 let map_to_use = entity_map
139 .map(std::string::String::as_str)
140 .or(temp_map_file.as_deref());
141
142 // Delegate to existing entity_creation implementation
143 let result = crate::commands::entity_creation::run(
144 log_files,
145 map_to_use,
146 output.map(std::string::String::as_str),
147 );
148
149 // Cleanup temp file
150 if let Some(temp) = temp_map_file {
151 let _ = std::fs::remove_file(temp);
152 }
153
154 result
155}
156
157/// Run preprocess subcommand
158pub fn run_preprocess(log_files: &[String], output: &str, format: &str) -> Result<()> {
159 // Delegate to existing preprocess_entities implementation
160 crate::commands::preprocess_entities::run(log_files, output, format)
161}
162
163/// Run gaps detection subcommand
164pub fn run_gaps(log_files: &[String], window_seconds: u64) -> Result<()> {
165 // Delegate to existing entity_gaps implementation
166 crate::commands::entity_gaps::run(log_files, window_seconds)
167}
168
169/// Run timeline subcommand
170pub fn run_timeline(
171 log_files: &[String],
172 entity_id: &str,
173 display_name: Option<&String>,
174) -> Result<()> {
175 // Delegate to existing entity_timeline implementation
176 crate::commands::entity_timeline::run(log_files, entity_id, display_name)
177}