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