rusticity_core/
cloudtrail.rs1use crate::config::AwsConfig;
2use anyhow::Result;
3
4pub struct CloudTrailClient {
5 config: AwsConfig,
6}
7
8impl CloudTrailClient {
9 pub fn new(config: AwsConfig) -> Self {
10 Self { config }
11 }
12
13 pub async fn lookup_events(
14 &self,
15 max_results: Option<i32>,
16 next_token: Option<String>,
17 ) -> Result<(
18 Vec<(
19 String,
20 String,
21 String,
22 String,
23 String,
24 String,
25 String,
26 String,
27 String,
28 String,
29 String,
30 String,
31 String,
32 String,
33 String,
34 )>,
35 Option<String>,
36 )> {
37 let client = self.config.cloudtrail_client().await;
38
39 let mut request = client.lookup_events();
40 if let Some(max) = max_results {
41 request = request.max_results(max);
42 }
43 if let Some(token) = next_token {
44 request = request.next_token(token);
45 }
46
47 let resp = request.send().await?;
48
49 let mut events = Vec::new();
50 for event in resp.events() {
51 let event_name = event.event_name().unwrap_or("").to_string();
52 let event_time = event
53 .event_time()
54 .map(|t| {
55 let dt = chrono::DateTime::parse_from_rfc3339(&t.to_string())
56 .unwrap_or_else(|_| chrono::Utc::now().into());
57 format!("{} (UTC)", dt.format("%Y-%m-%d %H:%M:%S"))
58 })
59 .unwrap_or_default();
60 let username = event.username().unwrap_or("").to_string();
61 let event_source = event
62 .cloud_trail_event()
63 .and_then(|json| {
64 serde_json::from_str::<serde_json::Value>(json)
65 .ok()
66 .and_then(|v| v["eventSource"].as_str().map(|s| s.to_string()))
67 })
68 .unwrap_or_default();
69 let resource_type = event
70 .resources()
71 .first()
72 .and_then(|r| r.resource_type())
73 .unwrap_or("")
74 .to_string();
75 let resource_name = event
76 .resources()
77 .first()
78 .and_then(|r| r.resource_name())
79 .unwrap_or("")
80 .to_string();
81 let read_only = event.read_only().map(|b| b.to_string()).unwrap_or_default();
82 let aws_region = event
83 .cloud_trail_event()
84 .and_then(|json| {
85 serde_json::from_str::<serde_json::Value>(json)
86 .ok()
87 .and_then(|v| v["awsRegion"].as_str().map(|s| s.to_string()))
88 })
89 .unwrap_or_default();
90 let event_id = event.event_id().unwrap_or("").to_string();
91 let access_key_id = event.access_key_id().unwrap_or("").to_string();
92 let source_ip = event
93 .cloud_trail_event()
94 .and_then(|json| {
95 serde_json::from_str::<serde_json::Value>(json)
96 .ok()
97 .and_then(|v| v["sourceIPAddress"].as_str().map(|s| s.to_string()))
98 })
99 .unwrap_or_default();
100 let error_code = event
101 .cloud_trail_event()
102 .and_then(|json| {
103 serde_json::from_str::<serde_json::Value>(json)
104 .ok()
105 .and_then(|v| v["errorCode"].as_str().map(|s| s.to_string()))
106 })
107 .unwrap_or_default();
108 let request_id = event
109 .cloud_trail_event()
110 .and_then(|json| {
111 serde_json::from_str::<serde_json::Value>(json)
112 .ok()
113 .and_then(|v| v["requestID"].as_str().map(|s| s.to_string()))
114 })
115 .unwrap_or_default();
116 let event_type = event
117 .cloud_trail_event()
118 .and_then(|json| {
119 serde_json::from_str::<serde_json::Value>(json)
120 .ok()
121 .and_then(|v| v["eventType"].as_str().map(|s| s.to_string()))
122 })
123 .unwrap_or_default();
124 let cloud_trail_event_json = event
125 .cloud_trail_event()
126 .and_then(|json| {
127 serde_json::from_str::<serde_json::Value>(json)
128 .ok()
129 .and_then(|v| serde_json::to_string_pretty(&v).ok())
130 })
131 .unwrap_or_else(|| "{}".to_string());
132
133 events.push((
134 event_name,
135 event_time,
136 username,
137 event_source,
138 resource_type,
139 resource_name,
140 read_only,
141 aws_region,
142 event_id,
143 access_key_id,
144 source_ip,
145 error_code,
146 request_id,
147 event_type,
148 cloud_trail_event_json,
149 ));
150 }
151
152 Ok((events, resp.next_token().map(|s| s.to_string())))
153 }
154
155 pub async fn get_next_token(&self, current_token: String) -> Result<Option<String>> {
157 let client = self.config.cloudtrail_client().await;
158 let resp = client
159 .lookup_events()
160 .max_results(1)
161 .next_token(current_token)
162 .send()
163 .await?;
164 Ok(resp.next_token().map(|s| s.to_string()))
165 }
166}