Skip to main content

vantage_aws/models/iam/
user.rs

1use serde::{Deserialize, Serialize};
2use vantage_table::table::Table;
3
4use crate::{AwsAccount, eq};
5
6use super::access_key::{AccessKey, access_keys_table};
7use super::attached_policy::{AttachedPolicy, attached_user_policies_table};
8use super::group::{Group, groups_for_user_table};
9
10/// One IAM user from `ListUsers`. Field names match the wire shape —
11/// the Query protocol returns these as XML elements; we expose them
12/// 1:1 so existing IAM docs translate directly.
13///
14/// Dates come through as the raw ISO-8601 string AWS sends.
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct User {
17    #[serde(rename = "UserName")]
18    pub user_name: String,
19    #[serde(rename = "UserId", default)]
20    pub user_id: String,
21    #[serde(rename = "Arn", default)]
22    pub arn: String,
23    #[serde(rename = "Path", default)]
24    pub path: String,
25    #[serde(rename = "CreateDate", default)]
26    pub create_date: String,
27    #[serde(rename = "PasswordLastUsed", default)]
28    pub password_last_used: String,
29}
30
31/// `ListUsers` table — every IAM user in the account. Optional
32/// filters: `PathPrefix` to narrow by path, `MaxItems` to cap the
33/// page (v0 still only reads the first page either way).
34///
35/// Three relations:
36///   - `groups` → `ListGroupsForUser` for this user
37///   - `access_keys` → `ListAccessKeys` for this user
38///   - `attached_policies` → `ListAttachedUserPolicies` for this user
39///
40/// AWS doesn't accept multi-value filters, so the source has to
41/// narrow to a single user before traversal — otherwise the call
42/// errors at execute time.
43///
44/// ```no_run
45/// # use vantage_aws::{AwsAccount, eq};
46/// # use vantage_aws::models::iam::users_table;
47/// # async fn run() -> vantage_core::Result<()> {
48/// # let aws = AwsAccount::from_default()?;
49/// let mut users = users_table(aws);
50/// users.add_condition(eq("PathPrefix", "/admin/"));
51/// # Ok(()) }
52/// ```
53pub fn users_table(aws: AwsAccount) -> Table<AwsAccount, User> {
54    Table::new("query/Users:iam/2010-05-08.ListUsers", aws)
55        .with_id_column("UserName")
56        .with_column_of::<String>("UserId")
57        .with_column_of::<String>("Arn")
58        .with_column_of::<String>("Path")
59        .with_column_of::<String>("CreateDate")
60        .with_column_of::<String>("PasswordLastUsed")
61        .with_many("groups", "UserName", groups_for_user_table)
62        .with_many("access_keys", "UserName", access_keys_table)
63        .with_many(
64            "attached_policies",
65            "UserName",
66            attached_user_policies_table,
67        )
68}
69
70impl User {
71    /// Groups *this* user belongs to.
72    pub fn ref_groups(&self, aws: AwsAccount) -> Table<AwsAccount, Group> {
73        let mut t = groups_for_user_table(aws);
74        t.add_condition(eq("UserName", self.user_name.clone()));
75        t
76    }
77
78    /// Access keys for *this* user.
79    pub fn ref_access_keys(&self, aws: AwsAccount) -> Table<AwsAccount, AccessKey> {
80        let mut t = access_keys_table(aws);
81        t.add_condition(eq("UserName", self.user_name.clone()));
82        t
83    }
84
85    /// Attached managed policies for *this* user.
86    pub fn ref_attached_policies(&self, aws: AwsAccount) -> Table<AwsAccount, AttachedPolicy> {
87        let mut t = attached_user_policies_table(aws);
88        t.add_condition(eq("UserName", self.user_name.clone()));
89        t
90    }
91}