objectscale_client/
iam.rs

1//
2// Copyright (c) Dell Inc., or its subsidiaries. All Rights Reserved.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10
11//! Defines identity and access resources details.
12//!
13
14use crate::client::ManagementClient;
15use crate::response::get_content_text;
16use anyhow::{anyhow, Context as _, Result};
17use derive_builder::Builder;
18use reqwest::header::{ACCEPT, AUTHORIZATION};
19use serde::{Deserialize, Serialize};
20use serde_aux::field_attributes::{deserialize_bool_from_anything, deserialize_default_from_null};
21
22#[derive(Debug, Deserialize)]
23#[serde(rename_all = "PascalCase")]
24struct CreateAccountResult {
25    pub account: Account,
26}
27
28#[derive(Debug, Deserialize)]
29#[serde(rename_all = "PascalCase")]
30struct ResponseMetadata {
31    pub request_id: String,
32}
33
34#[derive(Debug, Deserialize)]
35#[serde(rename_all = "PascalCase")]
36struct CreateAccountResponse {
37    pub response_metadata: ResponseMetadata,
38    pub create_account_result: CreateAccountResult,
39}
40
41#[derive(Debug, Deserialize)]
42#[serde(rename_all = "PascalCase")]
43struct TagAccountResponse {
44    pub response_metadata: ResponseMetadata,
45}
46
47#[derive(Debug, Deserialize)]
48#[serde(rename_all = "PascalCase")]
49struct GetAccountResult {
50    pub account: Account,
51}
52
53#[derive(Debug, Deserialize)]
54#[serde(rename_all = "PascalCase")]
55struct GetAccountResponse {
56    pub response_metadata: ResponseMetadata,
57    pub get_account_result: GetAccountResult,
58}
59
60#[derive(Debug, Deserialize)]
61#[serde(rename_all = "PascalCase")]
62struct UpdateAccountResult {
63    pub account: Account,
64}
65
66#[derive(Debug, Deserialize)]
67#[serde(rename_all = "PascalCase")]
68struct UpdateAccountResponse {
69    pub response_metadata: ResponseMetadata,
70    pub update_account_result: UpdateAccountResult,
71}
72
73#[derive(Debug, Deserialize)]
74#[serde(rename_all = "PascalCase")]
75struct DeleteAccountResponse {
76    pub response_metadata: ResponseMetadata,
77}
78
79#[derive(Debug, Deserialize)]
80#[serde(rename_all = "PascalCase")]
81struct DisableAccountResponse {
82    pub response_metadata: ResponseMetadata,
83}
84
85#[derive(Debug, Deserialize)]
86#[serde(rename_all = "PascalCase")]
87struct ListAccountsResult {
88    pub account_metadata: Vec<Account>,
89    pub is_truncated: bool,
90    pub marker: Option<String>,
91}
92
93#[derive(Debug, Deserialize)]
94#[serde(rename_all = "PascalCase")]
95struct ListAccountsResponse {
96    // Does not align with API description
97    pub response_metadata: ResponseMetadata,
98    pub list_accounts_result: ListAccountsResult,
99}
100
101/// Lables for IAM account, role and user.
102#[derive(Clone, Debug, Deserialize, Serialize)]
103#[serde(rename_all = "PascalCase")]
104pub struct Tag {
105    /// tag key
106    pub key: String,
107    /// tag value
108    pub value: String,
109}
110
111/// An ObjectScale Account is a logical construct that corresponds to a customer business unit, tenant, project, and so on.
112///
113/// You can build an Account with AccountBuilder and pass to [`create_account`](`ManagementClient::create_account`) method.
114///  [`get_account`](`ManagementClient::get_account`) would fetch the existing Account from ObjectScale server.
115///
116/// # Examples
117/// ```no_run
118/// use objectscale_client::iam::{AccountBuilder, Tag};
119/// let account = AccountBuilder::default()
120///     .alias("test")
121///     .encryption_enabled(true)
122///     .description("test")
123///     .tags(vec![Tag {
124///         key: "key1".to_string(),
125///         value: "value1".to_string(),
126///     }])
127///     .build()
128///     .expect("account");
129/// ```
130#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize)]
131#[serde(rename_all = "PascalCase")]
132#[builder(setter(skip))]
133pub struct Account {
134    /// The Id of the account
135    pub account_id: String,
136    /// The name/id of the object scale that the account is associated with
137    pub objscale: String,
138    /// The date and time, in the format of YYYY-MM-DDTHH:mm:ssZ, when the account created
139    pub create_date: String,
140    /// Indicate if encryption is enabled for the account
141    #[builder(setter(skip = false), default = "false")]
142    #[serde(deserialize_with = "deserialize_bool_from_anything")]
143    pub encryption_enabled: bool,
144    /// account disabled
145    #[serde(deserialize_with = "deserialize_bool_from_anything")]
146    pub account_disabled: bool,
147    #[builder(setter(into))]
148    /// An Alias for an account
149    pub alias: String,
150    #[builder(setter(into), default)]
151    /// The description for an account
152    // It does not have description field if to create account on GUI
153    #[serde(default)]
154    pub description: String,
155    /// protection enabled
156    #[serde(deserialize_with = "deserialize_bool_from_anything")]
157    pub protection_enabled: bool,
158    /// Tso id
159    // list account API won't return tso_id
160    #[serde(default)]
161    pub tso_id: String,
162    /// Labels
163    // If tags are not set, it won't have the field of "Tags" in create/get account reponse
164    #[builder(setter(skip = false), default)]
165    #[serde(default, deserialize_with = "deserialize_default_from_null")]
166    pub tags: Vec<Tag>,
167}
168
169impl Account {
170    pub(crate) fn create_account(
171        client: &mut ManagementClient,
172        account: Account,
173    ) -> Result<Account> {
174        // EncryptionEnabled dose not align with api description
175        // IsComplianceEnabled dose not work
176        let request_url = format!(
177            "{}iam?Action=CreateAccount&Alias={}&EncryptionEnabled={}&Description={}",
178            client.endpoint, account.alias, account.encryption_enabled, account.description,
179        );
180        let resp = client
181            .http_client
182            .post(request_url)
183            .header(ACCEPT, "application/json")
184            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
185            .send()?;
186        let text = get_content_text(resp)?;
187        let resp: CreateAccountResponse = serde_json::from_str(&text).with_context(|| {
188            format!(
189                "Unable to deserialise CreateAccountResponse. Body was: \"{}\"",
190                text
191            )
192        })?;
193        Ok(resp.create_account_result.account)
194    }
195
196    pub(crate) fn tag_account(
197        client: &mut ManagementClient,
198        account_id: &str,
199        tags: Vec<Tag>,
200    ) -> Result<()> {
201        if tags.is_empty() {
202            return Ok(());
203        }
204        let mut request_url = format!(
205            "{}iam?Action=TagAccount&AccountId={}",
206            client.endpoint, account_id,
207        );
208        for (index, tag) in tags.iter().enumerate() {
209            request_url = format!(
210                "{}&Tags.member.{}.Key={}&Tags.member.{}.Value={}",
211                request_url,
212                index + 1,
213                tag.key,
214                index + 1,
215                tag.value
216            );
217        }
218
219        let resp = client
220            .http_client
221            .post(request_url)
222            .header(ACCEPT, "application/json")
223            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
224            .send()?;
225        let text = get_content_text(resp)?;
226        let _: TagAccountResponse = serde_json::from_str(&text).with_context(|| {
227            format!(
228                "Unable to deserialise TagAccountResponse. Body was: \"{}\"",
229                text
230            )
231        })?;
232        Ok(())
233    }
234
235    pub(crate) fn get_account(client: &mut ManagementClient, account_id: &str) -> Result<Account> {
236        let request_url = format!(
237            "{}iam?Action=GetAccount&AccountId={}",
238            client.endpoint, account_id,
239        );
240        let resp = client
241            .http_client
242            .post(request_url)
243            .header(ACCEPT, "application/json")
244            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
245            .send()?;
246        let text = get_content_text(resp)?;
247        let resp: GetAccountResponse = serde_json::from_str(&text).with_context(|| {
248            format!(
249                "Unable to deserialise GetAccountResponse. Body was: \"{}\"",
250                text
251            )
252        })?;
253        Ok(resp.get_account_result.account)
254    }
255
256    pub(crate) fn update_account(
257        client: &mut ManagementClient,
258        account: Account,
259    ) -> Result<Account> {
260        let request_url = format!(
261            "{}iam?Action=UpdateAccount&AccountId={}&Alias={}&Description={}",
262            client.endpoint, account.account_id, account.alias, account.description,
263        );
264        let resp = client
265            .http_client
266            .post(request_url)
267            .header(ACCEPT, "application/json")
268            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
269            .send()?;
270        let text = get_content_text(resp)?;
271        let resp: UpdateAccountResponse = serde_json::from_str(&text).with_context(|| {
272            format!(
273                "Unable to deserialise UpdateAccountResponse. Body was: \"{}\"",
274                text
275            )
276        })?;
277        Ok(resp.update_account_result.account)
278    }
279
280    pub(crate) fn disable_account(client: &mut ManagementClient, account_id: &str) -> Result<()> {
281        let request_url = format!(
282            "{}iam?Action=DisableAccount&AccountId={}",
283            client.endpoint, account_id
284        );
285        let resp = client
286            .http_client
287            .post(request_url)
288            .header(ACCEPT, "application/json")
289            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
290            .send()?;
291        let text = get_content_text(resp)?;
292        let _: DisableAccountResponse = serde_json::from_str(&text).with_context(|| {
293            format!(
294                "Unable to deserialise DisableAccountResponse. Body was: \"{}\"",
295                text
296            )
297        })?;
298        Ok(())
299    }
300
301    pub(crate) fn delete_account(client: &mut ManagementClient, account_id: &str) -> Result<()> {
302        let request_url = format!(
303            "{}iam?Action=DeleteAccount&AccountId={}",
304            client.endpoint, account_id
305        );
306        let resp = client
307            .http_client
308            .post(request_url)
309            .header(ACCEPT, "application/json")
310            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
311            .send()?;
312        let text = get_content_text(resp)?;
313        let _: DeleteAccountResponse = serde_json::from_str(&text).with_context(|| {
314            format!(
315                "Unable to deserialise DeleteAccountResponse. Body was: \"{}\"",
316                text
317            )
318        })?;
319        Ok(())
320    }
321
322    pub(crate) fn list_accounts(client: &mut ManagementClient) -> Result<Vec<Account>> {
323        let request_url = format!("{}iam?Action=ListAccounts", client.endpoint);
324        let resp = client
325            .http_client
326            .post(request_url)
327            .header(ACCEPT, "application/json")
328            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
329            .send()?;
330        let text = get_content_text(resp)?;
331        let mut resp: ListAccountsResponse = serde_json::from_str(&text).with_context(|| {
332            format!(
333                "Unable to deserialise ListAccountsResponse. Body was: \"{}\"",
334                text
335            )
336        })?;
337        let mut accounts: Vec<Account> = vec![];
338        accounts.extend(resp.list_accounts_result.account_metadata);
339        while resp.list_accounts_result.is_truncated {
340            let request_url = format!(
341                "{}iam?Action=ListAccounts?Marker={}",
342                client.endpoint,
343                resp.list_accounts_result
344                    .marker
345                    .ok_or_else(|| anyhow!("No marker found"))?,
346            );
347            let response = client
348                .http_client
349                .post(request_url)
350                .header(ACCEPT, "application/json")
351                .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
352                .send()?;
353            let text = get_content_text(response)?;
354            resp = serde_json::from_str(&text).with_context(|| {
355                format!(
356                    "Unable to deserialise ListAccountsResponse. Body was: \"{}\"",
357                    text
358                )
359            })?;
360            accounts.extend(resp.list_accounts_result.account_metadata);
361        }
362        Ok(accounts)
363    }
364}
365
366/// In ObjectScale, an IAM User is a person or application in the account.
367// TODO:
368// - Support for `inline_policy` for user, group and role
369#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize)]
370#[serde(rename_all = "PascalCase")]
371#[builder(setter(skip))]
372pub struct User {
373    /// Arn that identifies the user.
374    pub arn: String,
375    /// ISO 8601 format DateTime when user was created.
376    // serde(default) is for GetGroupResponse which doesn't contain create_date
377    #[serde(default)]
378    pub create_date: String,
379    /// The path to the IAM User.
380    pub path: String,
381    /// Permissions boundary
382    // list users API won't return permissions_boundary
383    #[builder(setter(skip = false), default)]
384    #[serde(default)]
385    pub permissions_boundary: PermissionsBoundary,
386    /// Unique Id associated with the User.
387    pub user_id: String,
388    /// Simple name identifying the User.
389    #[builder(setter(into))]
390    pub user_name: String,
391    /// The list of Tags associated with the User.
392    #[builder(setter(skip = false), default)]
393    #[serde(default, deserialize_with = "deserialize_default_from_null")]
394    pub tags: Vec<Tag>,
395    #[builder(setter(into))]
396    #[serde(default)]
397    pub namespace: String,
398}
399
400#[derive(Clone, Debug, Default, Deserialize, Serialize)]
401#[serde(rename_all = "PascalCase")]
402pub struct PermissionsBoundary {
403    /// The ARN of the policy set as permissions boundary.
404    pub permissions_boundary_arn: String,
405    /// The permissions boundary usage type that indicates what type of IAM resource is used as the permissions boundary for an entity. This data type can only have a value of Policy.
406    pub permissions_boundary_type: String,
407}
408
409#[derive(Debug, Deserialize)]
410#[serde(rename_all = "PascalCase")]
411struct CreateUserResult {
412    pub user: User,
413}
414
415#[derive(Debug, Deserialize)]
416#[serde(rename_all = "PascalCase")]
417struct CreateUserResponse {
418    pub response_metadata: ResponseMetadata,
419    pub create_user_result: CreateUserResult,
420}
421
422#[derive(Debug, Deserialize)]
423#[serde(rename_all = "PascalCase")]
424struct TagUserResponse {
425    pub response_metadata: ResponseMetadata,
426}
427
428#[derive(Debug, Deserialize)]
429#[serde(rename_all = "PascalCase")]
430struct GetUserResult {
431    pub user: User,
432}
433
434#[derive(Debug, Deserialize)]
435#[serde(rename_all = "PascalCase")]
436struct GetUserResponse {
437    pub response_metadata: ResponseMetadata,
438    pub get_user_result: GetUserResult,
439}
440
441#[derive(Debug, Deserialize)]
442#[serde(rename_all = "PascalCase")]
443struct DeleteUserResponse {
444    pub response_metadata: ResponseMetadata,
445}
446
447#[derive(Debug, Deserialize)]
448#[serde(rename_all = "PascalCase")]
449struct ListUsersResult {
450    pub users: Vec<User>,
451    pub is_truncated: bool,
452    pub marker: Option<String>,
453}
454
455#[derive(Debug, Deserialize)]
456#[serde(rename_all = "PascalCase")]
457struct ListUsersResponse {
458    pub response_metadata: ResponseMetadata,
459    pub list_users_result: ListUsersResult,
460}
461
462impl User {
463    pub(crate) fn create(client: &mut ManagementClient, user: User) -> Result<User> {
464        let request_url = format!(
465            "{}iam?Action=CreateUser&UserName={}",
466            client.endpoint, user.user_name,
467        );
468        let namespace = user.namespace;
469        let mut req = client
470            .http_client
471            .post(request_url)
472            .header(ACCEPT, "application/json")
473            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
474            .header("x-emc-namespace", &namespace);
475
476        if !user
477            .permissions_boundary
478            .permissions_boundary_arn
479            .is_empty()
480        {
481            req = req.query(&[(
482                "PermissionsBoundary",
483                user.permissions_boundary.permissions_boundary_arn,
484            )]);
485        }
486        for (index, tag) in user.tags.iter().enumerate() {
487            req = req.query(&[(&format!("Tags.member.{}.Key", index + 1), &tag.key)]);
488            req = req.query(&[(&format!("Tags.member.{}.Value", index + 1), &tag.value)]);
489        }
490
491        let resp = req.send()?;
492        let text = get_content_text(resp)?;
493        let resp: CreateUserResponse = serde_json::from_str(&text).with_context(|| {
494            format!(
495                "Unable to deserialise CreateUserResponse. Body was: \"{}\"",
496                text
497            )
498        })?;
499        let mut user = resp.create_user_result.user;
500        user.namespace = namespace;
501        Ok(user)
502    }
503
504    pub(crate) fn tag_user(
505        client: &mut ManagementClient,
506        user_name: &str,
507        namespace: &str,
508        tags: Vec<Tag>,
509    ) -> Result<()> {
510        if tags.is_empty() {
511            return Ok(());
512        }
513        let mut request_url = format!(
514            "{}iam?Action=TagUser&UserName={}",
515            client.endpoint, user_name,
516        );
517        for (index, tag) in tags.iter().enumerate() {
518            request_url = format!(
519                "{}&Tags.member.{}.Key={}&Tags.member.{}.Value={}",
520                request_url,
521                index + 1,
522                tag.key,
523                index + 1,
524                tag.value
525            );
526        }
527
528        let resp = client
529            .http_client
530            .post(request_url)
531            .header(ACCEPT, "application/json")
532            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
533            .header("x-emc-namespace", namespace)
534            .send()?;
535        let text = get_content_text(resp)?;
536        let _: TagUserResponse = serde_json::from_str(&text).with_context(|| {
537            format!(
538                "Unable to deserialise TagUserResponse. Body was: \"{}\"",
539                text
540            )
541        })?;
542        Ok(())
543    }
544
545    pub(crate) fn get(
546        client: &mut ManagementClient,
547        user_name: &str,
548        namespace: &str,
549    ) -> Result<User> {
550        let request_url = format!(
551            "{}iam?Action=GetUser&UserName={}",
552            client.endpoint, user_name,
553        );
554        let resp = client
555            .http_client
556            .post(request_url)
557            .header(ACCEPT, "application/json")
558            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
559            .header("x-emc-namespace", namespace)
560            .send()?;
561        let text = get_content_text(resp)?;
562        let resp: GetUserResponse = serde_json::from_str(&text).with_context(|| {
563            format!(
564                "Unable to deserialise GetUserResponse. Body was: \"{}\"",
565                text
566            )
567        })?;
568        let mut user = resp.get_user_result.user;
569        user.namespace = namespace.to_string();
570        Ok(user)
571    }
572
573    pub(crate) fn delete(
574        client: &mut ManagementClient,
575        user_name: &str,
576        namespace: &str,
577    ) -> Result<()> {
578        let request_url = format!(
579            "{}iam?Action=DeleteUser&UserName={}",
580            client.endpoint, user_name,
581        );
582        let resp = client
583            .http_client
584            .post(request_url)
585            .header(ACCEPT, "application/json")
586            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
587            .header("x-emc-namespace", namespace)
588            .send()?;
589        let text = get_content_text(resp)?;
590        let _: DeleteUserResponse = serde_json::from_str(&text).with_context(|| {
591            format!(
592                "Unable to deserialise DeleteUserResponse. Body was: \"{}\"",
593                text
594            )
595        })?;
596        Ok(())
597    }
598
599    pub(crate) fn list(client: &mut ManagementClient, namespace: &str) -> Result<Vec<User>> {
600        let request_url = format!("{}iam?Action=ListUsers", client.endpoint);
601        let resp = client
602            .http_client
603            .post(request_url)
604            .header(ACCEPT, "application/json")
605            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
606            .header("x-emc-namespace", namespace)
607            .send()?;
608        let text = get_content_text(resp)?;
609        let mut resp: ListUsersResponse = serde_json::from_str(&text).with_context(|| {
610            format!(
611                "Unable to deserialise ListUsersResponse. Body was: \"{}\"",
612                text
613            )
614        })?;
615        let mut users: Vec<User> = vec![];
616        users.extend(resp.list_users_result.users);
617        while resp.list_users_result.is_truncated {
618            let request_url = format!(
619                "{}iam?Action=ListUsers&Marker={}",
620                client.endpoint,
621                resp.list_users_result
622                    .marker
623                    .ok_or_else(|| anyhow!("No marker found"))?,
624            );
625            let response = client
626                .http_client
627                .post(request_url)
628                .header(ACCEPT, "application/json")
629                .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
630                .header("x-emc-namespace", namespace)
631                .send()?;
632            let text = get_content_text(response)?;
633            resp = serde_json::from_str(&text).with_context(|| {
634                format!(
635                    "Unable to deserialise ListUsersResponse. Body was: \"{}\"",
636                    text
637                )
638            })?;
639            users.extend(resp.list_users_result.users);
640        }
641        users
642            .iter_mut()
643            .for_each(|user| user.namespace = namespace.to_string());
644        Ok(users)
645    }
646}
647
648#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize)]
649#[serde(rename_all = "PascalCase")]
650#[builder(setter(skip))]
651pub struct UserPolicyAttachment {
652    #[builder(setter(into))]
653    #[serde(default)]
654    pub user_name: String,
655    pub policy_name: String,
656    #[builder(setter(into))]
657    pub policy_arn: String,
658    #[builder(setter(into))]
659    #[serde(default)]
660    pub namespace: String,
661}
662
663#[derive(Debug, Deserialize)]
664#[serde(rename_all = "PascalCase")]
665struct AttachUserPolicyResponse {
666    pub response_metadata: ResponseMetadata,
667}
668
669#[derive(Debug, Deserialize)]
670#[serde(rename_all = "PascalCase")]
671struct DetachUserPolicyResponse {
672    pub response_metadata: ResponseMetadata,
673}
674
675#[derive(Debug, Deserialize)]
676#[serde(rename_all = "PascalCase")]
677struct ListAttachedUserPoliciesResult {
678    pub attached_policies: Vec<UserPolicyAttachment>,
679    pub is_truncated: bool,
680    pub marker: Option<String>,
681}
682
683#[derive(Debug, Deserialize)]
684#[serde(rename_all = "PascalCase")]
685struct ListAttachedUserPoliciesResponse {
686    pub response_metadata: ResponseMetadata,
687    pub list_attached_user_policies_result: ListAttachedUserPoliciesResult,
688}
689
690impl UserPolicyAttachment {
691    pub(crate) fn create(
692        client: &mut ManagementClient,
693        user_policy_attachment: UserPolicyAttachment,
694    ) -> Result<UserPolicyAttachment> {
695        let request_url = format!(
696            "{}iam?Action=AttachUserPolicy&UserName={}&PolicyArn={}",
697            client.endpoint, user_policy_attachment.user_name, user_policy_attachment.policy_arn,
698        );
699
700        let resp = client
701            .http_client
702            .post(request_url)
703            .header(ACCEPT, "application/json")
704            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
705            .header("x-emc-namespace", &user_policy_attachment.namespace)
706            .send()?;
707        let text = get_content_text(resp)?;
708        let _: AttachUserPolicyResponse = serde_json::from_str(&text).with_context(|| {
709            format!(
710                "Unable to deserialise AttachUserPolicyResponse. Body was: \"{}\"",
711                text
712            )
713        })?;
714        Ok(user_policy_attachment)
715    }
716
717    pub(crate) fn delete(
718        client: &mut ManagementClient,
719        user_policy_attachment: UserPolicyAttachment,
720    ) -> Result<()> {
721        let request_url = format!(
722            "{}iam?Action=DetachUserPolicy&UserName={}&PolicyArn={}",
723            client.endpoint, user_policy_attachment.user_name, user_policy_attachment.policy_arn,
724        );
725
726        let resp = client
727            .http_client
728            .post(request_url)
729            .header(ACCEPT, "application/json")
730            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
731            .header("x-emc-namespace", user_policy_attachment.namespace)
732            .send()?;
733        let text = get_content_text(resp)?;
734        let _: DetachUserPolicyResponse = serde_json::from_str(&text).with_context(|| {
735            format!(
736                "Unable to deserialise DetachUserPolicyResponse. Body was: \"{}\"",
737                text
738            )
739        })?;
740        Ok(())
741    }
742
743    pub(crate) fn list(
744        client: &mut ManagementClient,
745        user_name: &str,
746        namespace: &str,
747    ) -> Result<Vec<UserPolicyAttachment>> {
748        let request_url = format!(
749            "{}iam?Action=ListAttachedUserPolicies&UserName={}",
750            client.endpoint, user_name,
751        );
752        let resp = client
753            .http_client
754            .post(request_url)
755            .header(ACCEPT, "application/json")
756            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
757            .header("x-emc-namespace", namespace)
758            .send()?;
759        let text = get_content_text(resp)?;
760        let mut resp: ListAttachedUserPoliciesResponse =
761            serde_json::from_str(&text).with_context(|| {
762                format!(
763                    "Unable to deserialise ListAttachedUserPoliciesResponse. Body was: \"{}\"",
764                    text
765                )
766            })?;
767        let mut attachments: Vec<UserPolicyAttachment> = vec![];
768        attachments.extend(resp.list_attached_user_policies_result.attached_policies);
769        while resp.list_attached_user_policies_result.is_truncated {
770            let request_url = format!(
771                "{}iam?Action=ListAttachedUserPolicies&UserName={}&Marker={}",
772                client.endpoint,
773                user_name,
774                resp.list_attached_user_policies_result
775                    .marker
776                    .ok_or_else(|| anyhow!("No marker found"))?,
777            );
778            let response = client
779                .http_client
780                .post(request_url)
781                .header(ACCEPT, "application/json")
782                .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
783                .header("x-emc-namespace", namespace)
784                .send()?;
785            let text = get_content_text(response)?;
786            resp = serde_json::from_str(&text).with_context(|| {
787                format!(
788                    "Unable to deserialise ListAttachedUserPoliciesResponse. Body was: \"{}\"",
789                    text
790                )
791            })?;
792            attachments.extend(resp.list_attached_user_policies_result.attached_policies);
793        }
794        attachments.iter_mut().for_each(|attachment| {
795            attachment.namespace = namespace.to_string();
796            attachment.user_name = user_name.to_string();
797        });
798        Ok(attachments)
799    }
800}
801
802#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize)]
803#[serde(rename_all = "PascalCase")]
804#[builder(setter(skip))]
805pub struct LoginProfile {
806    pub create_date: String,
807    #[builder(setter(into))]
808    pub user_name: String,
809    #[builder(setter(skip = false), default)]
810    #[serde(deserialize_with = "deserialize_default_from_null")]
811    pub password_reset_required: bool,
812    #[builder(setter(into))]
813    #[serde(default)]
814    pub password: String,
815    #[builder(setter(into))]
816    #[serde(default)]
817    pub namespace: String,
818}
819
820#[derive(Debug, Deserialize)]
821#[serde(rename_all = "PascalCase")]
822struct CreateLoginProfileResult {
823    pub login_profile: LoginProfile,
824}
825
826#[derive(Debug, Deserialize)]
827#[serde(rename_all = "PascalCase")]
828struct CreateLoginProfileResponse {
829    pub response_metadata: ResponseMetadata,
830    pub create_login_profile_result: CreateLoginProfileResult,
831}
832
833#[derive(Debug, Deserialize)]
834#[serde(rename_all = "PascalCase")]
835struct GetLoginProfileResult {
836    pub login_profile: LoginProfile,
837}
838
839#[derive(Debug, Deserialize)]
840#[serde(rename_all = "PascalCase")]
841struct GetLoginProfileResponse {
842    pub response_metadata: ResponseMetadata,
843    pub get_login_profile_result: GetLoginProfileResult,
844}
845
846#[derive(Debug, Deserialize)]
847#[serde(rename_all = "PascalCase")]
848struct DeleteLoginProfileResponse {
849    pub response_metadata: ResponseMetadata,
850}
851
852impl LoginProfile {
853    pub(crate) fn create(
854        client: &mut ManagementClient,
855        login_profile: LoginProfile,
856    ) -> Result<LoginProfile> {
857        let request_url = format!(
858            "{}iam?Action=CreateLoginProfile&UserName={}&Password={}&PasswordResetRequired={}",
859            client.endpoint,
860            login_profile.user_name,
861            login_profile.password,
862            login_profile.password_reset_required,
863        );
864
865        let namespace = login_profile.namespace;
866        let resp = client
867            .http_client
868            .post(request_url)
869            .header(ACCEPT, "application/json")
870            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
871            .header("x-emc-namespace", &namespace)
872            .send()?;
873        let text = get_content_text(resp)?;
874        let resp: CreateLoginProfileResponse = serde_json::from_str(&text).with_context(|| {
875            format!(
876                "Unable to deserialise CreateLoginProfileResponse. Body was: \"{}\"",
877                text
878            )
879        })?;
880        let mut login_profile = resp.create_login_profile_result.login_profile;
881        login_profile.namespace = namespace;
882        Ok(login_profile)
883    }
884
885    pub(crate) fn get(
886        client: &mut ManagementClient,
887        user_name: &str,
888        namespace: &str,
889    ) -> Result<LoginProfile> {
890        let request_url = format!(
891            "{}iam?Action=GetLoginProfile&UserName={}",
892            client.endpoint, user_name,
893        );
894
895        let resp = client
896            .http_client
897            .post(request_url)
898            .header(ACCEPT, "application/json")
899            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
900            .header("x-emc-namespace", namespace)
901            .send()?;
902        let text = get_content_text(resp)?;
903        let resp: GetLoginProfileResponse = serde_json::from_str(&text).with_context(|| {
904            format!(
905                "Unable to deserialise GetLoginProfileResponse. Body was: \"{}\"",
906                text
907            )
908        })?;
909        let mut login_profile = resp.get_login_profile_result.login_profile;
910        login_profile.namespace = namespace.to_string();
911        Ok(login_profile)
912    }
913
914    pub(crate) fn delete(
915        client: &mut ManagementClient,
916        user_name: &str,
917        namespace: &str,
918    ) -> Result<()> {
919        let request_url = format!(
920            "{}iam?Action=DeleteLoginProfile&UserName={}",
921            client.endpoint, user_name,
922        );
923
924        let resp = client
925            .http_client
926            .post(request_url)
927            .header(ACCEPT, "application/json")
928            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
929            .header("x-emc-namespace", namespace)
930            .send()?;
931        let text = get_content_text(resp)?;
932        let _: DeleteLoginProfileResponse = serde_json::from_str(&text).with_context(|| {
933            format!(
934                "Unable to deserialise DeleteLoginProfileResponse. Body was: \"{}\"",
935                text
936            )
937        })?;
938        Ok(())
939    }
940}
941
942/// IAM User access key
943#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize)]
944#[serde(rename_all = "PascalCase")]
945#[builder(setter(skip))]
946pub struct AccessKey {
947    /// The Id of this access key
948    pub access_key_id: String,
949    /// The date and time, in the format of YYYY-MM-DDTHH:mm:ssZ, when the access key was created.
950    pub create_date: String,
951    /// The secret key
952    #[serde(default)]
953    pub secret_access_key: String,
954    /// The status of the access key {Active | Inactive}
955    pub status: String,
956    /// The name of the user that the access key is associated with.
957    #[builder(setter(into))]
958    pub user_name: String,
959    #[builder(setter(into))]
960    #[serde(default)]
961    pub namespace: String,
962}
963
964#[derive(Debug, Deserialize)]
965#[serde(rename_all = "PascalCase")]
966struct CreateAccessKeyResult {
967    pub access_key: AccessKey,
968}
969
970#[derive(Debug, Deserialize)]
971#[serde(rename_all = "PascalCase")]
972struct CreateAccessKeyResponse {
973    pub response_metadata: ResponseMetadata,
974    pub create_access_key_result: CreateAccessKeyResult,
975}
976
977#[derive(Debug, Deserialize)]
978#[serde(rename_all = "PascalCase")]
979struct UpdateAccessKeyResponse {
980    pub response_metadata: ResponseMetadata,
981}
982
983#[derive(Debug, Deserialize)]
984#[serde(rename_all = "PascalCase")]
985struct DeleteAccessKeyResponse {
986    pub response_metadata: ResponseMetadata,
987}
988
989#[derive(Debug, Deserialize)]
990#[serde(rename_all = "PascalCase")]
991struct ListAccessKeysResult {
992    pub access_key_metadata: Vec<AccessKey>,
993    pub is_truncated: bool,
994    pub marker: Option<String>,
995}
996
997#[derive(Debug, Deserialize)]
998#[serde(rename_all = "PascalCase")]
999struct ListAccessKeysResponse {
1000    pub response_metadata: ResponseMetadata,
1001    pub list_access_keys_result: ListAccessKeysResult,
1002}
1003
1004impl AccessKey {
1005    pub(crate) fn create(
1006        client: &mut ManagementClient,
1007        access_key: AccessKey,
1008    ) -> Result<AccessKey> {
1009        let request_url = format!(
1010            "{}iam?Action=CreateAccessKey&UserName={}",
1011            client.endpoint, access_key.user_name,
1012        );
1013        let namespace = access_key.namespace;
1014        let resp = client
1015            .http_client
1016            .post(request_url)
1017            .header(ACCEPT, "application/json")
1018            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1019            .header("x-emc-namespace", &namespace)
1020            .send()?;
1021        let text = get_content_text(resp)?;
1022        let resp: CreateAccessKeyResponse = serde_json::from_str(&text).with_context(|| {
1023            format!(
1024                "Unable to deserialise CreateAccessKeyResponse. Body was: \"{}\"",
1025                text
1026            )
1027        })?;
1028        let mut access_key = resp.create_access_key_result.access_key;
1029        access_key.namespace = namespace;
1030        Ok(access_key)
1031    }
1032
1033    pub(crate) fn update(client: &mut ManagementClient, access_key: AccessKey) -> Result<()> {
1034        let request_url = format!(
1035            "{}iam?Action=UpdateAccessKey&UserName={}&AccessKeyId={}&Status={}",
1036            client.endpoint, access_key.user_name, access_key.access_key_id, access_key.status
1037        );
1038        let namespace = access_key.namespace;
1039        let resp = client
1040            .http_client
1041            .post(request_url)
1042            .header(ACCEPT, "application/json")
1043            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1044            .header("x-emc-namespace", &namespace)
1045            .send()?;
1046        let text = get_content_text(resp)?;
1047        let _: UpdateAccessKeyResponse = serde_json::from_str(&text).with_context(|| {
1048            format!(
1049                "Unable to deserialise UpdateAccessKeyResponse. Body was: \"{}\"",
1050                text
1051            )
1052        })?;
1053        Ok(())
1054    }
1055
1056    pub(crate) fn delete(
1057        client: &mut ManagementClient,
1058        access_key_id: &str,
1059        user_name: &str,
1060        namespace: &str,
1061    ) -> Result<()> {
1062        let request_url = format!(
1063            "{}iam?Action=DeleteAccessKey&UserName={}&AccessKeyId={}",
1064            client.endpoint, user_name, access_key_id,
1065        );
1066        let resp = client
1067            .http_client
1068            .post(request_url)
1069            .header(ACCEPT, "application/json")
1070            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1071            .header("x-emc-namespace", namespace)
1072            .send()?;
1073        let text = get_content_text(resp)?;
1074        let _: DeleteAccessKeyResponse = serde_json::from_str(&text).with_context(|| {
1075            format!(
1076                "Unable to deserialise DeleteAccessKeyResponse. Body was: \"{}\"",
1077                text
1078            )
1079        })?;
1080        Ok(())
1081    }
1082
1083    pub(crate) fn list(
1084        client: &mut ManagementClient,
1085        user_name: &str,
1086        namespace: &str,
1087    ) -> Result<Vec<AccessKey>> {
1088        let request_url = format!(
1089            "{}iam?Action=ListAccessKeys&UserName={}",
1090            client.endpoint, user_name
1091        );
1092        let resp = client
1093            .http_client
1094            .post(request_url)
1095            .header(ACCEPT, "application/json")
1096            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1097            .header("x-emc-namespace", namespace)
1098            .send()?;
1099        let text = get_content_text(resp)?;
1100        let mut resp: ListAccessKeysResponse = serde_json::from_str(&text).with_context(|| {
1101            format!(
1102                "Unable to deserialise ListAccessKeysResponse. Body was: \"{}\"",
1103                text
1104            )
1105        })?;
1106        let mut access_keys: Vec<AccessKey> = vec![];
1107        access_keys.extend(resp.list_access_keys_result.access_key_metadata);
1108        while resp.list_access_keys_result.is_truncated {
1109            let request_url = format!(
1110                "{}iam?Action=ListAccessKeys&UserName={}&Marker={}",
1111                client.endpoint,
1112                user_name,
1113                resp.list_access_keys_result
1114                    .marker
1115                    .ok_or_else(|| anyhow!("No marker found"))?,
1116            );
1117            let response = client
1118                .http_client
1119                .post(request_url)
1120                .header(ACCEPT, "application/json")
1121                .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1122                .header("x-emc-namespace", namespace)
1123                .send()?;
1124            let text = get_content_text(response)?;
1125            resp = serde_json::from_str(&text).with_context(|| {
1126                format!(
1127                    "Unable to deserialise ListAccessKeysResponse. Body was: \"{}\"",
1128                    text
1129                )
1130            })?;
1131            access_keys.extend(resp.list_access_keys_result.access_key_metadata);
1132        }
1133        access_keys
1134            .iter_mut()
1135            .for_each(|access_key| access_key.namespace = namespace.to_string());
1136        Ok(access_keys)
1137    }
1138}
1139
1140/// IAM Account access key
1141#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize)]
1142#[serde(rename_all = "PascalCase")]
1143#[builder(setter(skip))]
1144pub struct AccountAccessKey {
1145    /// The Id of this access key
1146    pub access_key_id: String,
1147    /// The date and time, in the format of YYYY-MM-DDTHH:mm:ssZ, when the access key was created.
1148    pub create_date: String,
1149    /// The secret key
1150    #[serde(default)]
1151    pub secret_access_key: String,
1152    /// The status of the access key {Active | Inactive}
1153    pub status: String,
1154    /// The name of the user that the access key is associated with.
1155    #[builder(setter(into))]
1156    pub account_id: String,
1157}
1158
1159#[derive(Debug, Deserialize)]
1160#[serde(rename_all = "PascalCase")]
1161struct CreateAccountAccessKeyResult {
1162    pub access_key: AccountAccessKey,
1163}
1164
1165#[derive(Debug, Deserialize)]
1166#[serde(rename_all = "PascalCase")]
1167struct CreateAccountAccessKeyResponse {
1168    pub response_metadata: ResponseMetadata,
1169    pub create_account_access_key_result: CreateAccountAccessKeyResult,
1170}
1171
1172#[derive(Debug, Deserialize)]
1173#[serde(rename_all = "PascalCase")]
1174struct UpdateAccountAccessKeyResponse {
1175    pub response_metadata: ResponseMetadata,
1176}
1177
1178#[derive(Debug, Deserialize)]
1179#[serde(rename_all = "PascalCase")]
1180struct DeleteAccountAccessKeyResponse {
1181    pub response_metadata: ResponseMetadata,
1182}
1183
1184#[derive(Debug, Deserialize)]
1185#[serde(rename_all = "PascalCase")]
1186struct ListAccountAccessKeysResult {
1187    #[serde(deserialize_with = "deserialize_default_from_null")]
1188    pub access_key_metadata: Vec<AccountAccessKey>,
1189}
1190
1191#[derive(Debug, Deserialize)]
1192#[serde(rename_all = "PascalCase")]
1193struct ListAccountAccessKeysResponse {
1194    pub response_metadata: ResponseMetadata,
1195    pub list_account_access_keys_result: ListAccountAccessKeysResult,
1196}
1197
1198impl AccountAccessKey {
1199    pub(crate) fn create(
1200        client: &mut ManagementClient,
1201        account_access_key: AccountAccessKey,
1202    ) -> Result<AccountAccessKey> {
1203        let request_url = format!(
1204            "{}iam?Action=CreateAccountAccessKey&AccountId={}",
1205            client.endpoint, account_access_key.account_id,
1206        );
1207        let resp = client
1208            .http_client
1209            .post(request_url)
1210            .header(ACCEPT, "application/json")
1211            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1212            .send()?;
1213        let text = get_content_text(resp)?;
1214        let resp: CreateAccountAccessKeyResponse =
1215            serde_json::from_str(&text).with_context(|| {
1216                format!(
1217                    "Unable to deserialise CreateAccountAccessKeyResponse. Body was: \"{}\"",
1218                    text
1219                )
1220            })?;
1221        Ok(resp.create_account_access_key_result.access_key)
1222    }
1223
1224    pub(crate) fn update(
1225        client: &mut ManagementClient,
1226        account_access_key: AccountAccessKey,
1227    ) -> Result<()> {
1228        let request_url = format!(
1229            "{}iam?Action=UpdateAccountAccessKey&AccountId={}&AccessKeyId={}&Status={}",
1230            client.endpoint,
1231            account_access_key.account_id,
1232            account_access_key.access_key_id,
1233            account_access_key.status
1234        );
1235        let resp = client
1236            .http_client
1237            .post(request_url)
1238            .header(ACCEPT, "application/json")
1239            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1240            .send()?;
1241        let text = get_content_text(resp)?;
1242        let _: UpdateAccountAccessKeyResponse = serde_json::from_str(&text).with_context(|| {
1243            format!(
1244                "Unable to deserialise UpdateAccountAccessKeyResponse. Body was: \"{}\"",
1245                text
1246            )
1247        })?;
1248        Ok(())
1249    }
1250
1251    pub(crate) fn delete(
1252        client: &mut ManagementClient,
1253        access_key_id: &str,
1254        account_id: &str,
1255    ) -> Result<()> {
1256        let request_url = format!(
1257            "{}iam?Action=DeleteAccountAccessKey&AccessKeyId={}&AccountId={}",
1258            client.endpoint, access_key_id, account_id,
1259        );
1260        let resp = client
1261            .http_client
1262            .post(request_url)
1263            .header(ACCEPT, "application/json")
1264            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1265            .send()?;
1266        let text = get_content_text(resp)?;
1267        let _: DeleteAccountAccessKeyResponse = serde_json::from_str(&text).with_context(|| {
1268            format!(
1269                "Unable to deserialise DeleteAccountAccessKeyResponse. Body was: \"{}\"",
1270                text
1271            )
1272        })?;
1273        Ok(())
1274    }
1275
1276    pub(crate) fn list(
1277        client: &mut ManagementClient,
1278        account_id: &str,
1279    ) -> Result<Vec<AccountAccessKey>> {
1280        let request_url = format!(
1281            "{}iam?Action=ListAccountAccessKeys&AccountId={}",
1282            client.endpoint, account_id
1283        );
1284        let resp = client
1285            .http_client
1286            .post(request_url)
1287            .header(ACCEPT, "application/json")
1288            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1289            .send()?;
1290        let text = get_content_text(resp)?;
1291        let resp: ListAccountAccessKeysResponse =
1292            serde_json::from_str(&text).with_context(|| {
1293                format!(
1294                    "Unable to deserialise ListAccountAccessKeysResponse. Body was: \"{}\"",
1295                    text
1296                )
1297            })?;
1298
1299        Ok(resp.list_account_access_keys_result.access_key_metadata)
1300    }
1301}
1302
1303/// IAM policies are documents in JSON format that define permissions for an operation regardless of the method that you use to perform the operation.
1304#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize)]
1305#[serde(rename_all = "PascalCase")]
1306#[builder(setter(skip))]
1307pub struct Policy {
1308    /// The resource name of the policy.
1309    pub arn: String,
1310    /// The number of entities (users, groups, and roles) that the policy is attached to.
1311    pub attachment_count: i64,
1312    /// The date and time, in ISO 8601 date-time format, when the policy was created.
1313    pub create_date: String,
1314    /// The identifier for the version of the policy that is set as the default version.
1315    pub default_version_id: String,
1316    /// A friendly description of the policy.
1317    #[builder(setter(into), default)]
1318    pub description: String,
1319    /// Specifies whether the policy can be attached to user, group, or role.
1320    pub is_attachable: bool,
1321    /// The path to the policy
1322    pub path: String,
1323    /// Resource name of the policy that is used to set permissions boundary for the policy.
1324    pub permissions_boundary_usage_count: i64,
1325    /// The stable and unique string identifying the policy.
1326    pub policy_id: String,
1327    /// The friendly name of the policy.
1328    #[builder(setter(into))]
1329    pub policy_name: String,
1330    /// The date and time, in ISO 8601 date-time format, when the policy was created.
1331    pub update_date: String,
1332    #[builder(setter(into))]
1333    #[serde(default)]
1334    pub policy_document: String,
1335    #[builder(setter(into))]
1336    #[serde(default)]
1337    pub namespace: String,
1338}
1339
1340#[derive(Debug, Deserialize)]
1341#[serde(rename_all = "PascalCase")]
1342struct CreatePolicyResult {
1343    pub policy: Policy,
1344}
1345
1346#[derive(Debug, Deserialize)]
1347#[serde(rename_all = "PascalCase")]
1348struct CreatePolicyResponse {
1349    pub response_metadata: ResponseMetadata,
1350    pub create_policy_result: CreatePolicyResult,
1351}
1352
1353#[derive(Debug, Deserialize)]
1354#[serde(rename_all = "PascalCase")]
1355struct GetPolicyResult {
1356    pub policy: Policy,
1357}
1358
1359#[derive(Debug, Deserialize)]
1360#[serde(rename_all = "PascalCase")]
1361struct GetPolicyResponse {
1362    pub response_metadata: ResponseMetadata,
1363    pub get_policy_result: GetPolicyResult,
1364}
1365
1366#[derive(Debug, Deserialize)]
1367#[serde(rename_all = "PascalCase")]
1368struct DeletePolicyResponse {
1369    pub response_metadata: ResponseMetadata,
1370}
1371
1372#[derive(Debug, Deserialize)]
1373#[serde(rename_all = "PascalCase")]
1374struct ListPoliciesResult {
1375    pub policies: Vec<Policy>,
1376    pub is_truncated: bool,
1377    pub marker: Option<String>,
1378}
1379
1380#[derive(Debug, Deserialize)]
1381#[serde(rename_all = "PascalCase")]
1382struct ListPoliciesResponse {
1383    pub response_metadata: ResponseMetadata,
1384    pub list_policies_result: ListPoliciesResult,
1385}
1386
1387impl Policy {
1388    pub(crate) fn create(client: &mut ManagementClient, policy: Policy) -> Result<Policy> {
1389        let request_url = format!(
1390            "{}iam?Action=CreatePolicy&PolicyName={}&PolicyDocument={}&Description={}",
1391            client.endpoint, policy.policy_name, policy.policy_document, policy.description,
1392        );
1393        let namespace = policy.namespace;
1394        let resp = client
1395            .http_client
1396            .post(request_url)
1397            .header(ACCEPT, "application/json")
1398            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1399            .header("x-emc-namespace", &namespace)
1400            .send()?;
1401        let text = get_content_text(resp)?;
1402        let resp: CreatePolicyResponse = serde_json::from_str(&text).with_context(|| {
1403            format!(
1404                "Unable to deserialise CreatePolicyResponse. Body was: \"{}\"",
1405                text
1406            )
1407        })?;
1408        let mut policy = resp.create_policy_result.policy;
1409        policy.namespace = namespace;
1410        Ok(policy)
1411    }
1412
1413    pub(crate) fn get(
1414        client: &mut ManagementClient,
1415        policy_arn: &str,
1416        namespace: &str,
1417    ) -> Result<Policy> {
1418        let request_url = format!(
1419            "{}iam?Action=GetPolicy&PolicyArn={}",
1420            client.endpoint, policy_arn,
1421        );
1422        let resp = client
1423            .http_client
1424            .post(request_url)
1425            .header(ACCEPT, "application/json")
1426            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1427            .header("x-emc-namespace", namespace)
1428            .send()?;
1429        let text = get_content_text(resp)?;
1430        let resp: GetPolicyResponse = serde_json::from_str(&text).with_context(|| {
1431            format!(
1432                "Unable to deserialise GetPolicyResponse. Body was: \"{}\"",
1433                text
1434            )
1435        })?;
1436        let mut policy = resp.get_policy_result.policy;
1437        policy.namespace = namespace.to_string();
1438        Ok(policy)
1439    }
1440
1441    pub(crate) fn delete(
1442        client: &mut ManagementClient,
1443        policy_arn: &str,
1444        namespace: &str,
1445    ) -> Result<()> {
1446        let request_url = format!(
1447            "{}iam?Action=DeletePolicy&PolicyArn={}",
1448            client.endpoint, policy_arn,
1449        );
1450        let resp = client
1451            .http_client
1452            .post(request_url)
1453            .header(ACCEPT, "application/json")
1454            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1455            .header("x-emc-namespace", namespace)
1456            .send()?;
1457        let text = get_content_text(resp)?;
1458        let _: DeletePolicyResponse = serde_json::from_str(&text).with_context(|| {
1459            format!(
1460                "Unable to deserialise DeletePolicyResponse. Body was: \"{}\"",
1461                text
1462            )
1463        })?;
1464        Ok(())
1465    }
1466
1467    pub(crate) fn list(client: &mut ManagementClient, namespace: &str) -> Result<Vec<Policy>> {
1468        let request_url = format!("{}iam?Action=ListPolicies", client.endpoint);
1469        let resp = client
1470            .http_client
1471            .post(request_url)
1472            .header(ACCEPT, "application/json")
1473            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1474            .header("x-emc-namespace", namespace)
1475            .send()?;
1476        let text = get_content_text(resp)?;
1477        let mut resp: ListPoliciesResponse = serde_json::from_str(&text).with_context(|| {
1478            format!(
1479                "Unable to deserialise ListPoliciesResponse. Body was: \"{}\"",
1480                text
1481            )
1482        })?;
1483        let mut policies: Vec<Policy> = vec![];
1484        policies.extend(resp.list_policies_result.policies);
1485        while resp.list_policies_result.is_truncated {
1486            let request_url = format!(
1487                "{}iam?Action=ListPolicies&Marker={}",
1488                client.endpoint,
1489                resp.list_policies_result
1490                    .marker
1491                    .ok_or_else(|| anyhow!("No marker found"))?,
1492            );
1493            let response = client
1494                .http_client
1495                .post(request_url)
1496                .header(ACCEPT, "application/json")
1497                .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1498                .header("x-emc-namespace", namespace)
1499                .send()?;
1500            let text = get_content_text(response)?;
1501            resp = serde_json::from_str(&text).with_context(|| {
1502                format!(
1503                    "Unable to deserialise ListPoliciesResponse. Body was: \"{}\"",
1504                    text
1505                )
1506            })?;
1507            policies.extend(resp.list_policies_result.policies);
1508        }
1509        policies
1510            .iter_mut()
1511            .for_each(|policy| policy.namespace = namespace.to_string());
1512        Ok(policies)
1513    }
1514}
1515
1516/// A Group is a collection of Users. You can use groups to specify permissions for a collection of users.
1517#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize)]
1518#[serde(rename_all = "PascalCase")]
1519#[builder(setter(skip))]
1520pub struct Group {
1521    /// Arn that identifies the Group.
1522    pub arn: String,
1523    /// ISO 8601 format DateTime when group was created.
1524    // serde(default) is for ListGroupsForUserResponse which doesn't contain create_date
1525    #[serde(default)]
1526    pub create_date: String,
1527    /// The path to the IAM Group.
1528    pub path: String,
1529    /// Unique Id associated with the Group.
1530    pub group_id: String,
1531    /// Simple name identifying the Group.
1532    #[builder(setter(into))]
1533    pub group_name: String,
1534    #[builder(setter(into))]
1535    #[serde(default)]
1536    pub namespace: String,
1537}
1538#[derive(Debug, Deserialize)]
1539#[serde(rename_all = "PascalCase")]
1540struct CreateGroupResult {
1541    pub group: Group,
1542}
1543
1544#[derive(Debug, Deserialize)]
1545#[serde(rename_all = "PascalCase")]
1546struct CreateGroupResponse {
1547    pub response_metadata: ResponseMetadata,
1548    pub create_group_result: CreateGroupResult,
1549}
1550
1551#[derive(Debug, Deserialize)]
1552#[serde(rename_all = "PascalCase")]
1553struct GetGroupResult {
1554    pub group: Group,
1555    pub users: Vec<User>,
1556    pub is_truncated: bool,
1557    pub marker: Option<String>,
1558}
1559
1560#[derive(Debug, Deserialize)]
1561#[serde(rename_all = "PascalCase")]
1562struct GetGroupResponse {
1563    pub response_metadata: ResponseMetadata,
1564    pub get_group_result: GetGroupResult,
1565}
1566
1567#[derive(Debug, Deserialize)]
1568#[serde(rename_all = "PascalCase")]
1569struct DeleteGroupResponse {
1570    pub response_metadata: ResponseMetadata,
1571}
1572
1573#[derive(Debug, Deserialize)]
1574#[serde(rename_all = "PascalCase")]
1575struct ListGroupsResult {
1576    pub groups: Vec<Group>,
1577    pub is_truncated: bool,
1578    pub marker: Option<String>,
1579}
1580
1581#[derive(Debug, Deserialize)]
1582#[serde(rename_all = "PascalCase")]
1583struct ListGroupsResponse {
1584    pub response_metadata: ResponseMetadata,
1585    pub list_groups_result: ListGroupsResult,
1586}
1587
1588impl Group {
1589    pub(crate) fn create(client: &mut ManagementClient, group: Group) -> Result<Group> {
1590        let request_url = format!(
1591            "{}iam?Action=CreateGroup&GroupName={}",
1592            client.endpoint, group.group_name,
1593        );
1594        let namespace = group.namespace;
1595        let resp = client
1596            .http_client
1597            .post(request_url)
1598            .header(ACCEPT, "application/json")
1599            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1600            .header("x-emc-namespace", &namespace)
1601            .send()?;
1602        let text = get_content_text(resp)?;
1603        let resp: CreateGroupResponse = serde_json::from_str(&text).with_context(|| {
1604            format!(
1605                "Unable to deserialise CreateGroupResponse. Body was: \"{}\"",
1606                text
1607            )
1608        })?;
1609        let mut group = resp.create_group_result.group;
1610        group.namespace = namespace;
1611        Ok(group)
1612    }
1613
1614    pub(crate) fn get(
1615        client: &mut ManagementClient,
1616        group_name: &str,
1617        namespace: &str,
1618    ) -> Result<Group> {
1619        let request_url = format!(
1620            "{}iam?Action=GetGroup&GroupName={}",
1621            client.endpoint, group_name,
1622        );
1623        let resp = client
1624            .http_client
1625            .post(request_url)
1626            .header(ACCEPT, "application/json")
1627            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1628            .header("x-emc-namespace", namespace)
1629            .send()?;
1630        let text = get_content_text(resp)?;
1631        let resp: GetGroupResponse = serde_json::from_str(&text).with_context(|| {
1632            format!(
1633                "Unable to deserialise GetGroupResponse. Body was: \"{}\"",
1634                text
1635            )
1636        })?;
1637        let mut group = resp.get_group_result.group;
1638        group.namespace = namespace.to_string();
1639        Ok(group)
1640    }
1641
1642    pub(crate) fn delete(
1643        client: &mut ManagementClient,
1644        group_name: &str,
1645        namespace: &str,
1646    ) -> Result<()> {
1647        let request_url = format!(
1648            "{}iam?Action=DeleteGroup&GroupName={}",
1649            client.endpoint, group_name,
1650        );
1651        let resp = client
1652            .http_client
1653            .post(request_url)
1654            .header(ACCEPT, "application/json")
1655            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1656            .header("x-emc-namespace", namespace)
1657            .send()?;
1658        let text = get_content_text(resp)?;
1659        let _: DeleteGroupResponse = serde_json::from_str(&text).with_context(|| {
1660            format!(
1661                "Unable to deserialise DeleteGroupResponse. Body was: \"{}\"",
1662                text
1663            )
1664        })?;
1665        Ok(())
1666    }
1667
1668    pub(crate) fn list(client: &mut ManagementClient, namespace: &str) -> Result<Vec<Group>> {
1669        let request_url = format!("{}iam?Action=ListGroups", client.endpoint);
1670        let resp = client
1671            .http_client
1672            .post(request_url)
1673            .header(ACCEPT, "application/json")
1674            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1675            .header("x-emc-namespace", namespace)
1676            .send()?;
1677        let text = get_content_text(resp)?;
1678        let mut resp: ListGroupsResponse = serde_json::from_str(&text).with_context(|| {
1679            format!(
1680                "Unable to deserialise ListGroupsResponse. Body was: \"{}\"",
1681                text
1682            )
1683        })?;
1684        let mut groups: Vec<Group> = vec![];
1685        groups.extend(resp.list_groups_result.groups);
1686        while resp.list_groups_result.is_truncated {
1687            let request_url = format!(
1688                "{}iam?Action=ListGroups&Marker={}",
1689                client.endpoint,
1690                resp.list_groups_result
1691                    .marker
1692                    .ok_or_else(|| anyhow!("No marker found"))?,
1693            );
1694            let response = client
1695                .http_client
1696                .post(request_url)
1697                .header(ACCEPT, "application/json")
1698                .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1699                .header("x-emc-namespace", namespace)
1700                .send()?;
1701            let text = get_content_text(response)?;
1702            resp = serde_json::from_str(&text).with_context(|| {
1703                format!(
1704                    "Unable to deserialise ListGroupsResponse. Body was: \"{}\"",
1705                    text
1706                )
1707            })?;
1708            groups.extend(resp.list_groups_result.groups);
1709        }
1710        groups
1711            .iter_mut()
1712            .for_each(|group| group.namespace = namespace.to_string());
1713        Ok(groups)
1714    }
1715}
1716
1717#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize)]
1718#[serde(rename_all = "PascalCase")]
1719#[builder(setter(skip))]
1720pub struct GroupPolicyAttachment {
1721    #[builder(setter(into))]
1722    #[serde(default)]
1723    pub group_name: String,
1724    pub policy_name: String,
1725    #[builder(setter(into))]
1726    pub policy_arn: String,
1727    #[builder(setter(into))]
1728    #[serde(default)]
1729    pub namespace: String,
1730}
1731
1732#[derive(Debug, Deserialize)]
1733#[serde(rename_all = "PascalCase")]
1734struct AttachGroupPolicyResponse {
1735    pub response_metadata: ResponseMetadata,
1736}
1737
1738#[derive(Debug, Deserialize)]
1739#[serde(rename_all = "PascalCase")]
1740struct DetachGroupPolicyResponse {
1741    pub response_metadata: ResponseMetadata,
1742}
1743
1744#[derive(Debug, Deserialize)]
1745#[serde(rename_all = "PascalCase")]
1746struct ListAttachedGroupPoliciesResult {
1747    pub attached_policies: Vec<GroupPolicyAttachment>,
1748    pub is_truncated: bool,
1749    pub marker: Option<String>,
1750}
1751
1752#[derive(Debug, Deserialize)]
1753#[serde(rename_all = "PascalCase")]
1754struct ListAttachedGroupPoliciesResponse {
1755    pub response_metadata: ResponseMetadata,
1756    pub list_attached_group_policies_result: ListAttachedGroupPoliciesResult,
1757}
1758
1759impl GroupPolicyAttachment {
1760    pub(crate) fn create(
1761        client: &mut ManagementClient,
1762        group_policy_attachment: GroupPolicyAttachment,
1763    ) -> Result<GroupPolicyAttachment> {
1764        let request_url = format!(
1765            "{}iam?Action=AttachGroupPolicy&GroupName={}&PolicyArn={}",
1766            client.endpoint, group_policy_attachment.group_name, group_policy_attachment.policy_arn,
1767        );
1768
1769        let resp = client
1770            .http_client
1771            .post(request_url)
1772            .header(ACCEPT, "application/json")
1773            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1774            .header("x-emc-namespace", &group_policy_attachment.namespace)
1775            .send()?;
1776        let text = get_content_text(resp)?;
1777        let _: AttachGroupPolicyResponse = serde_json::from_str(&text).with_context(|| {
1778            format!(
1779                "Unable to deserialise AttachGroupPolicyResponse. Body was: \"{}\"",
1780                text
1781            )
1782        })?;
1783        Ok(group_policy_attachment)
1784    }
1785
1786    pub(crate) fn delete(
1787        client: &mut ManagementClient,
1788        group_policy_attachment: GroupPolicyAttachment,
1789    ) -> Result<()> {
1790        let request_url = format!(
1791            "{}iam?Action=DetachGroupPolicy&GroupName={}&PolicyArn={}",
1792            client.endpoint, group_policy_attachment.group_name, group_policy_attachment.policy_arn,
1793        );
1794
1795        let resp = client
1796            .http_client
1797            .post(request_url)
1798            .header(ACCEPT, "application/json")
1799            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1800            .header("x-emc-namespace", group_policy_attachment.namespace)
1801            .send()?;
1802        let text = get_content_text(resp)?;
1803        let _: DetachGroupPolicyResponse = serde_json::from_str(&text).with_context(|| {
1804            format!(
1805                "Unable to deserialise DetachGroupPolicyResponse. Body was: \"{}\"",
1806                text
1807            )
1808        })?;
1809        Ok(())
1810    }
1811
1812    pub(crate) fn list(
1813        client: &mut ManagementClient,
1814        group_name: &str,
1815        namespace: &str,
1816    ) -> Result<Vec<GroupPolicyAttachment>> {
1817        let request_url = format!(
1818            "{}iam?Action=ListAttachedGroupPolicies&GroupName={}",
1819            client.endpoint, group_name,
1820        );
1821        let resp = client
1822            .http_client
1823            .post(request_url)
1824            .header(ACCEPT, "application/json")
1825            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1826            .header("x-emc-namespace", namespace)
1827            .send()?;
1828        let text = get_content_text(resp)?;
1829        let mut resp: ListAttachedGroupPoliciesResponse = serde_json::from_str(&text)
1830            .with_context(|| {
1831                format!(
1832                    "Unable to deserialise ListAttachedGroupPoliciesResponse. Body was: \"{}\"",
1833                    text
1834                )
1835            })?;
1836        let mut attachments: Vec<GroupPolicyAttachment> = vec![];
1837        attachments.extend(resp.list_attached_group_policies_result.attached_policies);
1838        while resp.list_attached_group_policies_result.is_truncated {
1839            let request_url = format!(
1840                "{}iam?Action=ListAttachedGroupPolicies&GroupName={}&Marker={}",
1841                client.endpoint,
1842                group_name,
1843                resp.list_attached_group_policies_result
1844                    .marker
1845                    .ok_or_else(|| anyhow!("No marker found"))?,
1846            );
1847            let response = client
1848                .http_client
1849                .post(request_url)
1850                .header(ACCEPT, "application/json")
1851                .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1852                .header("x-emc-namespace", namespace)
1853                .send()?;
1854            let text = get_content_text(response)?;
1855            resp = serde_json::from_str(&text).with_context(|| {
1856                format!(
1857                    "Unable to deserialise ListAttachedGroupPolicies. Body was: \"{}\"",
1858                    text
1859                )
1860            })?;
1861            attachments.extend(resp.list_attached_group_policies_result.attached_policies);
1862        }
1863        attachments.iter_mut().for_each(|attachment| {
1864            attachment.namespace = namespace.to_string();
1865            attachment.group_name = group_name.to_string();
1866        });
1867        Ok(attachments)
1868    }
1869}
1870
1871/// A role is similar to a user, in that it is an identity with permission policies that determine what the identity can and cannot do.
1872#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize)]
1873#[serde(rename_all = "PascalCase")]
1874#[builder(setter(skip))]
1875pub struct Role {
1876    /// Arn that identifies the role.
1877    pub arn: String,
1878    /// The trust relationship policy document that grants an entity permission to assume the role.
1879    #[builder(setter(into))]
1880    pub assume_role_policy_document: String,
1881    /// ISO 8601 DateTime when role was created.
1882    pub create_date: String,
1883    /// The description of the IAM role.
1884    #[builder(setter(into), default)]
1885    pub description: String,
1886    /// The maximum session duration (in seconds) that you want to set for the specified role.
1887    #[builder(setter(skip = false), default)]
1888    pub max_session_duration: i32,
1889    /// The path to the IAM role.
1890    pub path: String,
1891    /// Unique Id associated with the role.
1892    pub role_id: String,
1893    /// Simple name identifying the role.
1894    #[builder(setter(into))]
1895    pub role_name: String,
1896    /// The list of Tags associated with the role.
1897    #[builder(setter(skip = false), default)]
1898    #[serde(default, deserialize_with = "deserialize_default_from_null")]
1899    pub tags: Vec<Tag>,
1900    /// Permissions boundary
1901    // list role API won't return permissions_boundary if not set
1902    #[builder(setter(skip = false), default)]
1903    #[serde(default)]
1904    pub permissions_boundary: PermissionsBoundary,
1905    #[builder(setter(into))]
1906    #[serde(default)]
1907    pub namespace: String,
1908}
1909#[derive(Debug, Deserialize)]
1910#[serde(rename_all = "PascalCase")]
1911struct CreateRoleResult {
1912    pub role: Role,
1913}
1914
1915#[derive(Debug, Deserialize)]
1916#[serde(rename_all = "PascalCase")]
1917struct CreateRoleResponse {
1918    pub response_metadata: ResponseMetadata,
1919    pub create_role_result: CreateRoleResult,
1920}
1921
1922#[derive(Debug, Deserialize)]
1923#[serde(rename_all = "PascalCase")]
1924struct GetRoleResult {
1925    pub role: Role,
1926}
1927
1928#[derive(Debug, Deserialize)]
1929#[serde(rename_all = "PascalCase")]
1930struct GetRoleResponse {
1931    pub response_metadata: ResponseMetadata,
1932    pub get_role_result: GetRoleResult,
1933}
1934
1935#[derive(Debug, Deserialize)]
1936#[serde(rename_all = "PascalCase")]
1937struct UpdateRoleResult {
1938    pub role: Role,
1939}
1940
1941#[derive(Debug, Deserialize)]
1942#[serde(rename_all = "PascalCase")]
1943struct UpdateRoleResponse {
1944    pub response_metadata: ResponseMetadata,
1945    pub update_role_result: UpdateRoleResult,
1946}
1947
1948#[derive(Debug, Deserialize)]
1949#[serde(rename_all = "PascalCase")]
1950struct DeleteRoleResponse {
1951    pub response_metadata: ResponseMetadata,
1952}
1953
1954#[derive(Debug, Deserialize)]
1955#[serde(rename_all = "PascalCase")]
1956struct ListRolesResult {
1957    pub roles: Vec<Role>,
1958    pub is_truncated: bool,
1959    pub marker: Option<String>,
1960}
1961
1962#[derive(Debug, Deserialize)]
1963#[serde(rename_all = "PascalCase")]
1964struct ListRolesResponse {
1965    pub response_metadata: ResponseMetadata,
1966    pub list_roles_result: ListRolesResult,
1967}
1968
1969impl Role {
1970    pub(crate) fn create(client: &mut ManagementClient, role: Role) -> Result<Role> {
1971        let request_url = format!(
1972            "{}iam?Action=CreateRole&RoleName={}&AssumeRolePolicyDocument={}",
1973            client.endpoint, role.role_name, role.assume_role_policy_document,
1974        );
1975        let namespace = role.namespace;
1976        let mut req = client
1977            .http_client
1978            .post(request_url)
1979            .header(ACCEPT, "application/json")
1980            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
1981            .header("x-emc-namespace", &namespace);
1982
1983        if !role.description.is_empty() {
1984            req = req.query(&[("Description", role.description)]);
1985        }
1986        if role.max_session_duration > 0 {
1987            req = req.query(&[("MaxSessionDuration", role.max_session_duration)]);
1988        }
1989        if !role
1990            .permissions_boundary
1991            .permissions_boundary_arn
1992            .is_empty()
1993        {
1994            req = req.query(&[(
1995                "PermissionsBoundary",
1996                role.permissions_boundary.permissions_boundary_arn,
1997            )]);
1998        }
1999        for (index, tag) in role.tags.iter().enumerate() {
2000            req = req.query(&[(&format!("Tags.member.{}.Key", index + 1), &tag.key)]);
2001            req = req.query(&[(&format!("Tags.member.{}.Value", index + 1), &tag.value)]);
2002        }
2003        let resp = req.send()?;
2004        let text = get_content_text(resp)?;
2005        let resp: CreateRoleResponse = serde_json::from_str(&text).with_context(|| {
2006            format!(
2007                "Unable to deserialise CreateRoleResponse. Body was: \"{}\"",
2008                text
2009            )
2010        })?;
2011        let mut role = resp.create_role_result.role;
2012        role.namespace = namespace;
2013        Ok(role)
2014    }
2015
2016    pub(crate) fn get(
2017        client: &mut ManagementClient,
2018        role_name: &str,
2019        namespace: &str,
2020    ) -> Result<Role> {
2021        let request_url = format!(
2022            "{}iam?Action=GetRole&RoleName={}",
2023            client.endpoint, role_name,
2024        );
2025        let resp = client
2026            .http_client
2027            .post(request_url)
2028            .header(ACCEPT, "application/json")
2029            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
2030            .header("x-emc-namespace", namespace)
2031            .send()?;
2032        let text = get_content_text(resp)?;
2033        let resp: GetRoleResponse = serde_json::from_str(&text).with_context(|| {
2034            format!(
2035                "Unable to deserialise GetRoleResponse. Body was: \"{}\"",
2036                text
2037            )
2038        })?;
2039        let mut role = resp.get_role_result.role;
2040        role.namespace = namespace.to_string();
2041        Ok(role)
2042    }
2043
2044    pub(crate) fn update(client: &mut ManagementClient, role: Role) -> Result<Role> {
2045        let request_url = format!(
2046            "{}iam?Action=UpdateRole&RoleName={}",
2047            client.endpoint, role.role_name,
2048        );
2049        let namespace = role.namespace;
2050        let mut req = client
2051            .http_client
2052            .post(request_url)
2053            .header(ACCEPT, "application/json")
2054            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
2055            .header("x-emc-namespace", &namespace);
2056
2057        if !role.description.is_empty() {
2058            req = req.query(&[("Description", role.description)]);
2059        }
2060        if role.max_session_duration > 0 {
2061            req = req.query(&[("MaxSessionDuration", role.max_session_duration)]);
2062        }
2063
2064        let resp = req.send()?;
2065        let text = get_content_text(resp)?;
2066        let resp: UpdateRoleResponse = serde_json::from_str(&text).with_context(|| {
2067            format!(
2068                "Unable to deserialise UpdateRoleResponse. Body was: \"{}\"",
2069                text
2070            )
2071        })?;
2072        let mut role = resp.update_role_result.role;
2073        role.namespace = namespace;
2074        Ok(role)
2075    }
2076
2077    pub(crate) fn delete(
2078        client: &mut ManagementClient,
2079        role_name: &str,
2080        namespace: &str,
2081    ) -> Result<()> {
2082        let request_url = format!(
2083            "{}iam?Action=DeleteRole&RoleName={}",
2084            client.endpoint, role_name,
2085        );
2086        let resp = client
2087            .http_client
2088            .post(request_url)
2089            .header(ACCEPT, "application/json")
2090            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
2091            .header("x-emc-namespace", namespace)
2092            .send()?;
2093        let text = get_content_text(resp)?;
2094        let _: DeleteRoleResponse = serde_json::from_str(&text).with_context(|| {
2095            format!(
2096                "Unable to deserialise DeleteRoleResponse. Body was: \"{}\"",
2097                text
2098            )
2099        })?;
2100        Ok(())
2101    }
2102
2103    pub(crate) fn list(client: &mut ManagementClient, namespace: &str) -> Result<Vec<Role>> {
2104        let request_url = format!("{}iam?Action=ListRoles", client.endpoint);
2105        let resp = client
2106            .http_client
2107            .post(request_url)
2108            .header(ACCEPT, "application/json")
2109            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
2110            .header("x-emc-namespace", namespace)
2111            .send()?;
2112        let text = get_content_text(resp)?;
2113        let mut resp: ListRolesResponse = serde_json::from_str(&text).with_context(|| {
2114            format!(
2115                "Unable to deserialise ListRolesResponse. Body was: \"{}\"",
2116                text
2117            )
2118        })?;
2119        let mut roles: Vec<Role> = vec![];
2120        roles.extend(resp.list_roles_result.roles);
2121        while resp.list_roles_result.is_truncated {
2122            let request_url = format!(
2123                "{}iam?Action=ListRoles&Marker={}",
2124                client.endpoint,
2125                resp.list_roles_result
2126                    .marker
2127                    .ok_or_else(|| anyhow!("No marker found"))?,
2128            );
2129            let response = client
2130                .http_client
2131                .post(request_url)
2132                .header(ACCEPT, "application/json")
2133                .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
2134                .header("x-emc-namespace", namespace)
2135                .send()?;
2136            let text = get_content_text(response)?;
2137            resp = serde_json::from_str(&text).with_context(|| {
2138                format!(
2139                    "Unable to deserialise ListRolesResponse. Body was: \"{}\"",
2140                    text
2141                )
2142            })?;
2143            roles.extend(resp.list_roles_result.roles);
2144        }
2145        roles
2146            .iter_mut()
2147            .for_each(|role| role.namespace = namespace.to_string());
2148        Ok(roles)
2149    }
2150}
2151
2152#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize)]
2153#[serde(rename_all = "PascalCase")]
2154#[builder(setter(skip))]
2155pub struct RolePolicyAttachment {
2156    #[builder(setter(into))]
2157    #[serde(default)]
2158    pub role_name: String,
2159    pub policy_name: String,
2160    #[builder(setter(into))]
2161    pub policy_arn: String,
2162    #[builder(setter(into))]
2163    #[serde(default)]
2164    pub namespace: String,
2165}
2166
2167#[derive(Debug, Deserialize)]
2168#[serde(rename_all = "PascalCase")]
2169struct AttachRolePolicyResponse {
2170    pub response_metadata: ResponseMetadata,
2171}
2172
2173#[derive(Debug, Deserialize)]
2174#[serde(rename_all = "PascalCase")]
2175struct DetachRolePolicyResponse {
2176    pub response_metadata: ResponseMetadata,
2177}
2178
2179#[derive(Debug, Deserialize)]
2180#[serde(rename_all = "PascalCase")]
2181struct ListAttachedRolePoliciesResult {
2182    pub attached_policies: Vec<RolePolicyAttachment>,
2183    pub is_truncated: bool,
2184    pub marker: Option<String>,
2185}
2186
2187#[derive(Debug, Deserialize)]
2188#[serde(rename_all = "PascalCase")]
2189struct ListAttachedRolePoliciesResponse {
2190    pub response_metadata: ResponseMetadata,
2191    pub list_attached_role_policies_result: ListAttachedRolePoliciesResult,
2192}
2193
2194impl RolePolicyAttachment {
2195    pub(crate) fn create(
2196        client: &mut ManagementClient,
2197        role_policy_attachment: RolePolicyAttachment,
2198    ) -> Result<RolePolicyAttachment> {
2199        let request_url = format!(
2200            "{}iam?Action=AttachRolePolicy&RoleName={}&PolicyArn={}",
2201            client.endpoint, role_policy_attachment.role_name, role_policy_attachment.policy_arn,
2202        );
2203
2204        let resp = client
2205            .http_client
2206            .post(request_url)
2207            .header(ACCEPT, "application/json")
2208            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
2209            .header("x-emc-namespace", &role_policy_attachment.namespace)
2210            .send()?;
2211        let text = get_content_text(resp)?;
2212        let _: AttachRolePolicyResponse = serde_json::from_str(&text).with_context(|| {
2213            format!(
2214                "Unable to deserialise AttachRolePolicyResponse. Body was: \"{}\"",
2215                text
2216            )
2217        })?;
2218        Ok(role_policy_attachment)
2219    }
2220
2221    pub(crate) fn delete(
2222        client: &mut ManagementClient,
2223        role_policy_attachment: RolePolicyAttachment,
2224    ) -> Result<()> {
2225        let request_url = format!(
2226            "{}iam?Action=DetachRolePolicy&RoleName={}&PolicyArn={}",
2227            client.endpoint, role_policy_attachment.role_name, role_policy_attachment.policy_arn,
2228        );
2229
2230        let resp = client
2231            .http_client
2232            .post(request_url)
2233            .header(ACCEPT, "application/json")
2234            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
2235            .header("x-emc-namespace", role_policy_attachment.namespace)
2236            .send()?;
2237        let text = get_content_text(resp)?;
2238        let _: DetachRolePolicyResponse = serde_json::from_str(&text).with_context(|| {
2239            format!(
2240                "Unable to deserialise DetachRolePolicyResponse. Body was: \"{}\"",
2241                text
2242            )
2243        })?;
2244        Ok(())
2245    }
2246
2247    pub(crate) fn list(
2248        client: &mut ManagementClient,
2249        role_name: &str,
2250        namespace: &str,
2251    ) -> Result<Vec<RolePolicyAttachment>> {
2252        let request_url = format!(
2253            "{}iam?Action=ListAttachedRolePolicies&RoleName={}",
2254            client.endpoint, role_name,
2255        );
2256        let resp = client
2257            .http_client
2258            .post(request_url)
2259            .header(ACCEPT, "application/json")
2260            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
2261            .header("x-emc-namespace", namespace)
2262            .send()?;
2263        let text = get_content_text(resp)?;
2264        let mut resp: ListAttachedRolePoliciesResponse =
2265            serde_json::from_str(&text).with_context(|| {
2266                format!(
2267                    "Unable to deserialise ListAttachedRolePoliciesResponse. Body was: \"{}\"",
2268                    text
2269                )
2270            })?;
2271        let mut attachments: Vec<RolePolicyAttachment> = vec![];
2272        attachments.extend(resp.list_attached_role_policies_result.attached_policies);
2273        while resp.list_attached_role_policies_result.is_truncated {
2274            let request_url = format!(
2275                "{}iam?Action=ListAttachedRolePolicies&RoleName={}&Marker={}",
2276                client.endpoint,
2277                role_name,
2278                resp.list_attached_role_policies_result
2279                    .marker
2280                    .ok_or_else(|| anyhow!("No marker found"))?,
2281            );
2282            let response = client
2283                .http_client
2284                .post(request_url)
2285                .header(ACCEPT, "application/json")
2286                .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
2287                .header("x-emc-namespace", namespace)
2288                .send()?;
2289            let text = get_content_text(response)?;
2290            resp = serde_json::from_str(&text).with_context(|| {
2291                format!(
2292                    "Unable to deserialise ListAttachedRolePolicies. Body was: \"{}\"",
2293                    text
2294                )
2295            })?;
2296            attachments.extend(resp.list_attached_role_policies_result.attached_policies);
2297        }
2298        attachments.iter_mut().for_each(|attachment| {
2299            attachment.namespace = namespace.to_string();
2300            attachment.role_name = role_name.to_string();
2301        });
2302        Ok(attachments)
2303    }
2304}
2305
2306#[derive(Clone, Debug, Serialize)]
2307#[serde(rename_all = "PascalCase")]
2308pub struct EntitiesForPolicy {
2309    pub users: Vec<String>,
2310    pub groups: Vec<String>,
2311    pub roles: Vec<String>,
2312}
2313
2314#[derive(Debug, Deserialize)]
2315#[serde(rename_all = "PascalCase")]
2316struct PolicyUser {
2317    pub user_name: String,
2318    pub user_id: String,
2319}
2320
2321#[derive(Debug, Deserialize)]
2322#[serde(rename_all = "PascalCase")]
2323struct PolicyGroup {
2324    pub group_name: String,
2325    pub group_id: String,
2326}
2327
2328#[derive(Debug, Deserialize)]
2329#[serde(rename_all = "PascalCase")]
2330struct PolicyRole {
2331    pub role_name: String,
2332    pub role_id: String,
2333}
2334
2335#[derive(Debug, Deserialize)]
2336#[serde(rename_all = "PascalCase")]
2337struct ListEntitiesForPolicyResult {
2338    pub policy_users: Vec<PolicyUser>,
2339    pub policy_groups: Vec<PolicyGroup>,
2340    pub policy_roles: Vec<PolicyRole>,
2341    pub is_truncated: bool,
2342    pub marker: Option<String>,
2343}
2344
2345#[derive(Debug, Deserialize)]
2346#[serde(rename_all = "PascalCase")]
2347struct ListEntitiesForPolicyResponse {
2348    pub response_metadata: ResponseMetadata,
2349    pub list_entities_for_policy_result: ListEntitiesForPolicyResult,
2350}
2351
2352impl EntitiesForPolicy {
2353    pub(crate) fn get(
2354        client: &mut ManagementClient,
2355        policy_arn: &str,
2356        namespace: &str,
2357        entity_filter: &str,
2358        usage_filter: &str,
2359    ) -> Result<EntitiesForPolicy> {
2360        let mut request_url = format!(
2361            "{}iam?Action=ListEntitiesForPolicy&PolicyArn={}",
2362            client.endpoint, policy_arn,
2363        );
2364        if !entity_filter.is_empty() {
2365            request_url = format!("{}&EntityFilter={}", request_url, entity_filter);
2366        }
2367        if !usage_filter.is_empty() {
2368            request_url = format!("{}&PolicyUsageFilter={}", request_url, usage_filter);
2369        }
2370        let resp = client
2371            .http_client
2372            .post(request_url)
2373            .header(ACCEPT, "application/json")
2374            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
2375            .header("x-emc-namespace", namespace)
2376            .send()?;
2377        let text = get_content_text(resp)?;
2378        let mut resp: ListEntitiesForPolicyResponse =
2379            serde_json::from_str(&text).with_context(|| {
2380                format!(
2381                    "Unable to deserialise ListEntitiesForPolicyResponse. Body was: \"{}\"",
2382                    text
2383                )
2384            })?;
2385        let mut attachment = EntitiesForPolicy {
2386            users: vec![],
2387            groups: vec![],
2388            roles: vec![],
2389        };
2390        attachment.users.extend(
2391            resp.list_entities_for_policy_result
2392                .policy_users
2393                .into_iter()
2394                .map(|u| u.user_name)
2395                .collect::<Vec<String>>(),
2396        );
2397        attachment.groups.extend(
2398            resp.list_entities_for_policy_result
2399                .policy_groups
2400                .into_iter()
2401                .map(|u| u.group_name)
2402                .collect::<Vec<String>>(),
2403        );
2404        attachment.roles.extend(
2405            resp.list_entities_for_policy_result
2406                .policy_roles
2407                .into_iter()
2408                .map(|u| u.role_name)
2409                .collect::<Vec<String>>(),
2410        );
2411        while resp.list_entities_for_policy_result.is_truncated {
2412            let request_url = format!(
2413                "{}iam?Action=ListEntitiesForPolicy&PolicyArn={}&PolicyUsageFilter={}&Marker={}",
2414                client.endpoint,
2415                policy_arn,
2416                entity_filter,
2417                resp.list_entities_for_policy_result
2418                    .marker
2419                    .ok_or_else(|| anyhow!("No marker found"))?,
2420            );
2421            let response = client
2422                .http_client
2423                .post(request_url)
2424                .header(ACCEPT, "application/json")
2425                .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
2426                .header("x-emc-namespace", namespace)
2427                .send()?;
2428            let text = get_content_text(response)?;
2429            resp = serde_json::from_str(&text).with_context(|| {
2430                format!(
2431                    "Unable to deserialise ListEntitiesForPolicyResponse. Body was: \"{}\"",
2432                    text
2433                )
2434            })?;
2435            attachment.users.extend(
2436                resp.list_entities_for_policy_result
2437                    .policy_users
2438                    .into_iter()
2439                    .map(|u| u.user_name)
2440                    .collect::<Vec<String>>(),
2441            );
2442            attachment.groups.extend(
2443                resp.list_entities_for_policy_result
2444                    .policy_groups
2445                    .into_iter()
2446                    .map(|u| u.group_name)
2447                    .collect::<Vec<String>>(),
2448            );
2449            attachment.roles.extend(
2450                resp.list_entities_for_policy_result
2451                    .policy_roles
2452                    .into_iter()
2453                    .map(|u| u.role_name)
2454                    .collect::<Vec<String>>(),
2455            );
2456        }
2457        Ok(attachment)
2458    }
2459}
2460
2461#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize)]
2462#[serde(rename_all = "PascalCase")]
2463#[builder(setter(skip))]
2464pub struct UserGroupMembership {
2465    #[builder(setter(into))]
2466    pub user_name: String,
2467    #[builder(setter(into))]
2468    pub group_name: String,
2469    #[builder(setter(into))]
2470    pub namespace: String,
2471}
2472
2473#[derive(Debug, Deserialize)]
2474#[serde(rename_all = "PascalCase")]
2475struct AddUserToGroupResponse {
2476    pub response_metadata: ResponseMetadata,
2477}
2478
2479#[derive(Debug, Deserialize)]
2480#[serde(rename_all = "PascalCase")]
2481struct RemoveUserFromGroupResponse {
2482    pub response_metadata: ResponseMetadata,
2483}
2484
2485#[derive(Debug, Deserialize)]
2486#[serde(rename_all = "PascalCase")]
2487struct ListGroupsForUserResult {
2488    pub groups: Vec<Group>,
2489    pub is_truncated: bool,
2490    pub marker: Option<String>,
2491}
2492
2493#[derive(Debug, Deserialize)]
2494#[serde(rename_all = "PascalCase")]
2495struct ListGroupsForUserResponse {
2496    pub response_metadata: ResponseMetadata,
2497    pub list_groups_for_user_result: ListGroupsForUserResult,
2498}
2499
2500impl UserGroupMembership {
2501    pub(crate) fn create(
2502        client: &mut ManagementClient,
2503        user_group_membership: UserGroupMembership,
2504    ) -> Result<UserGroupMembership> {
2505        let request_url = format!(
2506            "{}iam?Action=AddUserToGroup&UserName={}&GroupName={}",
2507            client.endpoint, user_group_membership.user_name, user_group_membership.group_name,
2508        );
2509
2510        let resp = client
2511            .http_client
2512            .post(request_url)
2513            .header(ACCEPT, "application/json")
2514            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
2515            .header("x-emc-namespace", &user_group_membership.namespace)
2516            .send()?;
2517        let text = get_content_text(resp)?;
2518        let _: AddUserToGroupResponse = serde_json::from_str(&text).with_context(|| {
2519            format!(
2520                "Unable to deserialise AttachUserPolicyResponse. Body was: \"{}\"",
2521                text
2522            )
2523        })?;
2524        Ok(user_group_membership)
2525    }
2526
2527    pub(crate) fn delete(
2528        client: &mut ManagementClient,
2529        user_group_membership: UserGroupMembership,
2530    ) -> Result<()> {
2531        let request_url = format!(
2532            "{}iam?Action=RemoveUserFromGroup&UserName={}&GroupName={}",
2533            client.endpoint, user_group_membership.user_name, user_group_membership.group_name,
2534        );
2535
2536        let resp = client
2537            .http_client
2538            .post(request_url)
2539            .header(ACCEPT, "application/json")
2540            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
2541            .header("x-emc-namespace", user_group_membership.namespace)
2542            .send()?;
2543        let text = get_content_text(resp)?;
2544        let _: RemoveUserFromGroupResponse = serde_json::from_str(&text).with_context(|| {
2545            format!(
2546                "Unable to deserialise RemoveUserFromGroupResponse. Body was: \"{}\"",
2547                text
2548            )
2549        })?;
2550        Ok(())
2551    }
2552
2553    pub(crate) fn list_by_user(
2554        client: &mut ManagementClient,
2555        user_name: &str,
2556        namespace: &str,
2557    ) -> Result<Vec<UserGroupMembership>> {
2558        let request_url = format!(
2559            "{}iam?Action=ListGroupsForUser&UserName={}",
2560            client.endpoint, user_name,
2561        );
2562        let resp = client
2563            .http_client
2564            .post(request_url)
2565            .header(ACCEPT, "application/json")
2566            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
2567            .header("x-emc-namespace", namespace)
2568            .send()?;
2569        let text = get_content_text(resp)?;
2570        let mut resp: ListGroupsForUserResponse =
2571            serde_json::from_str(&text).with_context(|| {
2572                format!(
2573                    "Unable to deserialise ListGroupsForUserResponse. Body was: \"{}\"",
2574                    text
2575                )
2576            })?;
2577        let mut memberships: Vec<UserGroupMembership> = vec![];
2578        memberships.extend(
2579            resp.list_groups_for_user_result
2580                .groups
2581                .into_iter()
2582                .map(|u| UserGroupMembership {
2583                    user_name: user_name.to_string(),
2584                    group_name: u.group_name,
2585                    namespace: namespace.to_string(),
2586                })
2587                .collect::<Vec<UserGroupMembership>>(),
2588        );
2589        while resp.list_groups_for_user_result.is_truncated {
2590            let request_url = format!(
2591                "{}iam?Action=ListGroupsForUser&UserName={}&Marker={}",
2592                client.endpoint,
2593                user_name,
2594                resp.list_groups_for_user_result
2595                    .marker
2596                    .ok_or_else(|| anyhow!("No marker found"))?,
2597            );
2598            let response = client
2599                .http_client
2600                .post(request_url)
2601                .header(ACCEPT, "application/json")
2602                .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
2603                .header("x-emc-namespace", namespace)
2604                .send()?;
2605            let text = get_content_text(response)?;
2606            resp = serde_json::from_str(&text).with_context(|| {
2607                format!(
2608                    "Unable to deserialise ListGroupsForUserResponse. Body was: \"{}\"",
2609                    text
2610                )
2611            })?;
2612            memberships.extend(
2613                resp.list_groups_for_user_result
2614                    .groups
2615                    .into_iter()
2616                    .map(|u| UserGroupMembership {
2617                        user_name: user_name.to_string(),
2618                        group_name: u.group_name,
2619                        namespace: namespace.to_string(),
2620                    })
2621                    .collect::<Vec<UserGroupMembership>>(),
2622            );
2623        }
2624        Ok(memberships)
2625    }
2626
2627    pub(crate) fn list_by_group(
2628        client: &mut ManagementClient,
2629        group_name: &str,
2630        namespace: &str,
2631    ) -> Result<Vec<UserGroupMembership>> {
2632        let request_url = format!(
2633            "{}iam?Action=GetGroup&GroupName={}",
2634            client.endpoint, group_name,
2635        );
2636        let resp = client
2637            .http_client
2638            .post(request_url)
2639            .header(ACCEPT, "application/json")
2640            .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
2641            .header("x-emc-namespace", namespace)
2642            .send()?;
2643        let text = get_content_text(resp)?;
2644        let mut resp: GetGroupResponse = serde_json::from_str(&text).with_context(|| {
2645            format!(
2646                "Unable to deserialise GetGroupResponse. Body was: \"{}\"",
2647                text
2648            )
2649        })?;
2650        let mut memberships: Vec<UserGroupMembership> = vec![];
2651        memberships.extend(
2652            resp.get_group_result
2653                .users
2654                .into_iter()
2655                .map(|u| UserGroupMembership {
2656                    user_name: u.user_name,
2657                    group_name: group_name.to_string(),
2658                    namespace: namespace.to_string(),
2659                })
2660                .collect::<Vec<UserGroupMembership>>(),
2661        );
2662
2663        while resp.get_group_result.is_truncated {
2664            let request_url = format!(
2665                "{}iam?Action=GetGroup&GroupName={}&Marker={}",
2666                client.endpoint,
2667                group_name,
2668                resp.get_group_result
2669                    .marker
2670                    .ok_or_else(|| anyhow!("No marker found"))?,
2671            );
2672            let response = client
2673                .http_client
2674                .post(request_url)
2675                .header(ACCEPT, "application/json")
2676                .header(AUTHORIZATION, client.access_token.as_ref().unwrap())
2677                .header("x-emc-namespace", namespace)
2678                .send()?;
2679            let text = get_content_text(response)?;
2680            resp = serde_json::from_str(&text).with_context(|| {
2681                format!(
2682                    "Unable to deserialise GetGroupResponse. Body was: \"{}\"",
2683                    text
2684                )
2685            })?;
2686            memberships.extend(
2687                resp.get_group_result
2688                    .users
2689                    .into_iter()
2690                    .map(|u| UserGroupMembership {
2691                        user_name: u.user_name,
2692                        group_name: group_name.to_string(),
2693                        namespace: namespace.to_string(),
2694                    })
2695                    .collect::<Vec<UserGroupMembership>>(),
2696            );
2697        }
2698        Ok(memberships)
2699    }
2700}