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 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 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 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 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 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}