fakecloud_iam/
resource_policy.rs1use std::sync::Arc;
10
11use fakecloud_core::auth::ResourcePolicyProvider;
12
13use crate::state::SharedIamState;
14
15pub struct StsResourcePolicyProvider {
16 state: SharedIamState,
17}
18
19impl StsResourcePolicyProvider {
20 pub fn new(state: SharedIamState) -> Self {
21 Self { state }
22 }
23
24 pub fn shared(state: SharedIamState) -> Arc<dyn ResourcePolicyProvider> {
25 Arc::new(Self::new(state))
26 }
27}
28
29impl ResourcePolicyProvider for StsResourcePolicyProvider {
30 fn resource_policy(&self, service: &str, resource_arn: &str) -> Option<String> {
31 if !service.eq_ignore_ascii_case("sts") {
32 return None;
33 }
34 let parts: Vec<&str> = resource_arn.split(':').collect();
37 if parts.len() < 6 {
38 return None;
39 }
40 let account_id = parts[4];
41 let resource = parts[5];
42 let role_name = resource.strip_prefix("role/")?;
43 let role_name = role_name.rsplit('/').next().unwrap_or(role_name);
45
46 let accounts = self.state.read();
47 let state = accounts.get(account_id)?;
48 let role = state.roles.get(role_name)?;
49 Some(role.assume_role_policy_document.clone())
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56 use crate::state::{IamRole, IamState};
57 use fakecloud_aws::arn::Arn;
58 use fakecloud_core::multi_account::MultiAccountState;
59 use parking_lot::RwLock;
60
61 fn make_state_with_role(
62 account_id: &str,
63 role_name: &str,
64 trust_policy: &str,
65 ) -> SharedIamState {
66 let mut mas = MultiAccountState::<IamState>::new(account_id, "us-east-1", "");
67 let state = mas.get_or_create(account_id);
68 state.roles.insert(
69 role_name.to_string(),
70 IamRole {
71 role_name: role_name.to_string(),
72 role_id: "AROATEST".to_string(),
73 arn: Arn::global("iam", account_id, &format!("role/{role_name}")).to_string(),
74 path: "/".to_string(),
75 assume_role_policy_document: trust_policy.to_string(),
76 created_at: chrono::Utc::now(),
77 description: None,
78 max_session_duration: 3600,
79 tags: Vec::new(),
80 permissions_boundary: None,
81 },
82 );
83 Arc::new(RwLock::new(mas))
84 }
85
86 #[test]
87 fn returns_trust_policy_for_sts_role_arn() {
88 let trust = r#"{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":"*","Action":"sts:AssumeRole"}]}"#;
89 let state = make_state_with_role("222222222222", "my-role", trust);
90 let provider = StsResourcePolicyProvider::new(state);
91 assert_eq!(
92 provider.resource_policy("sts", "arn:aws:iam::222222222222:role/my-role"),
93 Some(trust.to_string())
94 );
95 }
96
97 #[test]
98 fn returns_none_for_non_sts_service() {
99 let state = make_state_with_role("222222222222", "r", "{}");
100 let provider = StsResourcePolicyProvider::new(state);
101 assert_eq!(
102 provider.resource_policy("s3", "arn:aws:iam::222222222222:role/r"),
103 None
104 );
105 }
106
107 #[test]
108 fn returns_none_for_missing_role() {
109 let state = make_state_with_role("222222222222", "existing", "{}");
110 let provider = StsResourcePolicyProvider::new(state);
111 assert_eq!(
112 provider.resource_policy("sts", "arn:aws:iam::222222222222:role/missing"),
113 None
114 );
115 }
116
117 #[test]
118 fn returns_none_for_missing_account() {
119 let state = make_state_with_role("111111111111", "r", "{}");
120 let provider = StsResourcePolicyProvider::new(state);
121 assert_eq!(
122 provider.resource_policy("sts", "arn:aws:iam::999999999999:role/r"),
123 None
124 );
125 }
126}