Skip to main content

aws_lite_rs/api/
accessanalyzer.rs

1//! AWS IAM Access Analyzer API client.
2//!
3//! Thin wrapper over generated ops. Adds ergonomic method signatures
4//! and auto-pagination.
5//!
6//! Needed by AWS CIS benchmark checks:
7//!   - CIS 2.19 (aws_iam_access_analyzer): confirm at least one ACTIVE
8//!     analyzer exists per region (ACCOUNT or ORGANIZATION type).
9
10use crate::{
11    AwsHttpClient, Result, ops::accessanalyzer::AccessanalyzerOps,
12    types::accessanalyzer::AnalyzerSummary,
13};
14
15/// Client for the AWS IAM Access Analyzer API.
16pub struct AccessAnalyzerClient<'a> {
17    ops: AccessanalyzerOps<'a>,
18}
19
20impl<'a> AccessAnalyzerClient<'a> {
21    /// Create a new Access Analyzer client.
22    pub(crate) fn new(client: &'a AwsHttpClient) -> Self {
23        Self {
24            ops: AccessanalyzerOps::new(client),
25        }
26    }
27
28    // ── Analyzers ─────────────────────────────────────────────────────
29
30    /// List all IAM Access Analyzers in the current region (auto-paginated).
31    ///
32    /// CIS 2.19: at least one analyzer with `status == "ACTIVE"` must exist
33    /// in every region. Check the returned list's `status` fields.
34    pub async fn list_analyzers(&self) -> Result<Vec<AnalyzerSummary>> {
35        let mut all = Vec::new();
36        let mut next_token = String::new();
37        loop {
38            let resp = self.ops.list_analyzers(&next_token, "", "").await?;
39            all.extend(resp.analyzers);
40            match resp.next_token {
41                Some(tok) if !tok.is_empty() => next_token = tok,
42                _ => break,
43            }
44        }
45        Ok(all)
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use serde_json::json;
52
53    #[tokio::test]
54    async fn test_list_analyzers_empty() {
55        let mut mock = crate::MockClient::new();
56        mock.expect_get("/analyzer")
57            .returning_json(json!({"analyzers": []}))
58            .times(1);
59
60        let client = crate::AwsHttpClient::from_mock(mock);
61        let aa = client.accessanalyzer();
62        let result = aa.list_analyzers().await;
63        assert!(result.is_ok());
64        assert_eq!(result.unwrap().len(), 0);
65    }
66
67    #[tokio::test]
68    async fn test_list_analyzers_active() {
69        let mut mock = crate::MockClient::new();
70        mock.expect_get("/analyzer")
71            .returning_json(json!({
72                "analyzers": [
73                    {
74                        "arn": "arn:aws:access-analyzer:us-east-1:123456789012:analyzer/my-analyzer",
75                        "name": "my-analyzer",
76                        "type": "ACCOUNT",
77                        "status": "ACTIVE",
78                        "createdAt": "2024-01-01T00:00:00Z"
79                    }
80                ]
81            }))
82            .times(1);
83
84        let client = crate::AwsHttpClient::from_mock(mock);
85        let aa = client.accessanalyzer();
86        let result = aa.list_analyzers().await;
87        assert!(result.is_ok());
88        let analyzers = result.unwrap();
89        assert_eq!(analyzers.len(), 1);
90        assert_eq!(analyzers[0].name, "my-analyzer");
91        assert_eq!(analyzers[0].status, "ACTIVE");
92        assert_eq!(analyzers[0].r#type, "ACCOUNT");
93        assert_eq!(
94            analyzers[0].arn,
95            "arn:aws:access-analyzer:us-east-1:123456789012:analyzer/my-analyzer"
96        );
97    }
98
99    #[tokio::test]
100    async fn test_list_analyzers_paginated() {
101        let mut mock = crate::MockClient::new();
102        // Register second page first (more-specific URL)
103        mock.expect_get("/analyzer?nextToken=tok1")
104            .returning_json(json!({
105                "analyzers": [{"arn": "arn:aws:access-analyzer:us-east-1:123456789012:analyzer/analyzer-2", "name": "analyzer-2", "type": "ACCOUNT", "status": "ACTIVE", "createdAt": "2024-01-02T00:00:00Z"}]
106            }))
107            .times(1);
108        // First page
109        mock.expect_get("/analyzer")
110            .returning_json(json!({
111                "analyzers": [{"arn": "arn:aws:access-analyzer:us-east-1:123456789012:analyzer/analyzer-1", "name": "analyzer-1", "type": "ACCOUNT", "status": "ACTIVE", "createdAt": "2024-01-01T00:00:00Z"}],
112                "nextToken": "tok1"
113            }))
114            .times(1);
115
116        let client = crate::AwsHttpClient::from_mock(mock);
117        let aa = client.accessanalyzer();
118        let result = aa.list_analyzers().await;
119        assert!(result.is_ok());
120        assert_eq!(result.unwrap().len(), 2);
121    }
122}