Skip to main content

rusticity_core/
iam.rs

1use crate::config::AwsConfig;
2
3pub struct IamClient {
4    config: AwsConfig,
5}
6
7impl IamClient {
8    pub fn new(config: AwsConfig) -> Self {
9        Self { config }
10    }
11
12    pub async fn list_users(&self) -> Result<Vec<aws_sdk_iam::types::User>, String> {
13        let client = self.config.iam_client().await;
14
15        let mut users = Vec::new();
16        let mut marker = None;
17
18        loop {
19            let mut request = client.list_users();
20            if let Some(m) = marker {
21                request = request.marker(m);
22            }
23
24            let response = request
25                .send()
26                .await
27                .map_err(|e| format!("Failed to list users: {}", e))?;
28
29            let is_truncated = response.is_truncated();
30            marker = response.marker;
31            users.extend(response.users);
32
33            if !is_truncated {
34                break;
35            }
36        }
37
38        Ok(users)
39    }
40
41    pub async fn list_roles(&self) -> Result<Vec<aws_sdk_iam::types::Role>, String> {
42        let client = self.config.iam_client().await;
43
44        let mut roles = Vec::new();
45        let mut marker = None;
46
47        loop {
48            let mut request = client.list_roles();
49            if let Some(m) = marker {
50                request = request.marker(m);
51            }
52
53            let response = request
54                .send()
55                .await
56                .map_err(|e| format!("Failed to list roles: {}", e))?;
57
58            let is_truncated = response.is_truncated();
59            marker = response.marker;
60            roles.extend(response.roles);
61
62            if !is_truncated {
63                break;
64            }
65        }
66
67        Ok(roles)
68    }
69
70    pub async fn list_groups(&self) -> Result<Vec<aws_sdk_iam::types::Group>, String> {
71        let client = self.config.iam_client().await;
72
73        let mut groups = Vec::new();
74        let mut marker = None;
75
76        loop {
77            let mut request = client.list_groups();
78            if let Some(m) = marker {
79                request = request.marker(m);
80            }
81
82            let response = request
83                .send()
84                .await
85                .map_err(|e| format!("Failed to list groups: {}", e))?;
86
87            let is_truncated = response.is_truncated();
88            marker = response.marker;
89            groups.extend(response.groups);
90
91            if !is_truncated {
92                break;
93            }
94        }
95
96        Ok(groups)
97    }
98
99    pub async fn list_attached_role_policies(
100        &self,
101        role_name: &str,
102    ) -> Result<Vec<aws_sdk_iam::types::AttachedPolicy>, String> {
103        let client = self.config.iam_client().await;
104
105        let response = client
106            .list_attached_role_policies()
107            .role_name(role_name)
108            .send()
109            .await
110            .map_err(|e| format!("Failed to list attached role policies: {}", e))?;
111
112        Ok(response.attached_policies.unwrap_or_default())
113    }
114
115    pub async fn list_attached_group_policies(
116        &self,
117        group_name: &str,
118    ) -> Result<Vec<aws_sdk_iam::types::AttachedPolicy>, String> {
119        let client = self.config.iam_client().await;
120
121        let response = client
122            .list_attached_group_policies()
123            .group_name(group_name)
124            .send()
125            .await
126            .map_err(|e| format!("Failed to list attached group policies: {}", e))?;
127
128        Ok(response.attached_policies.unwrap_or_default())
129    }
130
131    pub async fn list_role_policies(&self, role_name: &str) -> Result<Vec<String>, String> {
132        let client = self.config.iam_client().await;
133
134        let response = client
135            .list_role_policies()
136            .role_name(role_name)
137            .send()
138            .await
139            .map_err(|e| format!("Failed to list role policies: {}", e))?;
140
141        Ok(response.policy_names)
142    }
143
144    pub async fn get_group(&self, group_name: &str) -> Result<usize, String> {
145        let client = self.config.iam_client().await;
146
147        let response = client
148            .get_group()
149            .group_name(group_name)
150            .send()
151            .await
152            .map_err(|e| format!("Failed to get group: {}", e))?;
153
154        Ok(response.users.len())
155    }
156
157    pub async fn get_group_users(
158        &self,
159        group_name: &str,
160    ) -> Result<Vec<aws_sdk_iam::types::User>, String> {
161        let client = self.config.iam_client().await;
162
163        let response = client
164            .get_group()
165            .group_name(group_name)
166            .send()
167            .await
168            .map_err(|e| format!("Failed to get group users: {}", e))?;
169
170        Ok(response.users)
171    }
172
173    pub async fn list_group_policies(&self, group_name: &str) -> Result<Vec<String>, String> {
174        let client = self.config.iam_client().await;
175
176        let response = client
177            .list_group_policies()
178            .group_name(group_name)
179            .send()
180            .await
181            .map_err(|e| format!("Failed to list group policies: {}", e))?;
182
183        Ok(response.policy_names)
184    }
185
186    pub async fn get_role_policy(
187        &self,
188        role_name: &str,
189        policy_name: &str,
190    ) -> Result<String, String> {
191        let client = self.config.iam_client().await;
192
193        let response = client
194            .get_role_policy()
195            .role_name(role_name)
196            .policy_name(policy_name)
197            .send()
198            .await
199            .map_err(|e| format!("Failed to get role policy: {}", e))?;
200
201        let policy_document = response.policy_document();
202
203        // URL decode and pretty print JSON
204        let decoded = percent_encoding::percent_decode_str(policy_document)
205            .decode_utf8()
206            .map_err(|e| format!("Failed to decode policy: {}", e))?;
207
208        let json: serde_json::Value = serde_json::from_str(&decoded)
209            .map_err(|e| format!("Failed to parse policy JSON: {}", e))?;
210
211        serde_json::to_string_pretty(&json)
212            .map_err(|e| format!("Failed to format policy JSON: {}", e))
213    }
214
215    pub async fn get_policy_version(&self, policy_arn: &str) -> Result<String, String> {
216        let client = self.config.iam_client().await;
217
218        // Get the policy to find the default version
219        let policy_response = client
220            .get_policy()
221            .policy_arn(policy_arn)
222            .send()
223            .await
224            .map_err(|e| format!("Failed to get policy: {}", e))?;
225
226        let default_version = policy_response
227            .policy()
228            .and_then(|p| p.default_version_id())
229            .ok_or_else(|| "No default version found".to_string())?;
230
231        // Get the policy version document
232        let version_response = client
233            .get_policy_version()
234            .policy_arn(policy_arn)
235            .version_id(default_version)
236            .send()
237            .await
238            .map_err(|e| format!("Failed to get policy version: {}", e))?;
239
240        let policy_document = version_response
241            .policy_version()
242            .and_then(|v| v.document())
243            .ok_or_else(|| "No policy document found".to_string())?;
244
245        // URL decode and pretty print JSON
246        let decoded = percent_encoding::percent_decode_str(policy_document)
247            .decode_utf8()
248            .map_err(|e| format!("Failed to decode policy: {}", e))?;
249
250        let json: serde_json::Value = serde_json::from_str(&decoded)
251            .map_err(|e| format!("Failed to parse policy JSON: {}", e))?;
252
253        serde_json::to_string_pretty(&json)
254            .map_err(|e| format!("Failed to format policy JSON: {}", e))
255    }
256
257    pub async fn get_role(&self, role_name: &str) -> Result<String, String> {
258        let client = self.config.iam_client().await;
259
260        let response = client
261            .get_role()
262            .role_name(role_name)
263            .send()
264            .await
265            .map_err(|e| format!("Failed to get role: {}", e))?;
266
267        let assume_role_policy = response
268            .role()
269            .and_then(|r| r.assume_role_policy_document())
270            .ok_or_else(|| "No assume role policy document found".to_string())?;
271
272        // URL decode and pretty print JSON
273        let decoded = percent_encoding::percent_decode_str(assume_role_policy)
274            .decode_utf8()
275            .map_err(|e| format!("Failed to decode policy: {}", e))?;
276
277        let json: serde_json::Value = serde_json::from_str(&decoded)
278            .map_err(|e| format!("Failed to parse policy JSON: {}", e))?;
279
280        serde_json::to_string_pretty(&json)
281            .map_err(|e| format!("Failed to format policy JSON: {}", e))
282    }
283
284    pub async fn list_role_tags(&self, role_name: &str) -> Result<Vec<(String, String)>, String> {
285        let client = self.config.iam_client().await;
286
287        let response = client
288            .list_role_tags()
289            .role_name(role_name)
290            .send()
291            .await
292            .map_err(|e| format!("Failed to list role tags: {}", e))?;
293
294        Ok(response
295            .tags()
296            .iter()
297            .map(|t| (t.key().to_string(), t.value().to_string()))
298            .collect())
299    }
300
301    pub async fn list_user_tags(&self, user_name: &str) -> Result<Vec<(String, String)>, String> {
302        let client = self.config.iam_client().await;
303
304        let response = client
305            .list_user_tags()
306            .user_name(user_name)
307            .send()
308            .await
309            .map_err(|e| format!("Failed to list user tags: {}", e))?;
310
311        Ok(response
312            .tags()
313            .iter()
314            .map(|t| (t.key().to_string(), t.value().to_string()))
315            .collect())
316    }
317
318    pub async fn get_login_profile(&self, user_name: &str) -> Result<bool, String> {
319        let client = self.config.iam_client().await;
320
321        match client.get_login_profile().user_name(user_name).send().await {
322            Ok(_) => Ok(true),
323            Err(_) => Ok(false),
324        }
325    }
326
327    pub async fn list_access_keys(&self, user_name: &str) -> Result<usize, String> {
328        let client = self.config.iam_client().await;
329
330        let response = client
331            .list_access_keys()
332            .user_name(user_name)
333            .send()
334            .await
335            .map_err(|e| format!("Failed to list access keys: {}", e))?;
336
337        Ok(response.access_key_metadata().len())
338    }
339}