fakecloud_core/
anonymous.rs1#[path = "anonymous_gen.rs"]
10mod generated;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum AnonymousAccess {
16 Refused,
18 Authorized,
21 Unauthenticated,
23}
24
25impl AnonymousAccess {
26 pub fn for_operation(service: &str, operation: &str) -> Self {
31 if is_unauthenticated_operation(service, operation) {
32 Self::Unauthenticated
33 } else {
34 Self::Refused
35 }
36 }
37}
38
39pub fn is_unauthenticated_operation(service: &str, operation: &str) -> bool {
42 generated::UNAUTHENTICATED_OPERATIONS
43 .binary_search(&(service, operation))
44 .is_ok()
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50
51 #[test]
55 fn table_matches_the_models() {
56 let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../aws-models");
57 let mut derived: Vec<(String, String)> = Vec::new();
58 for entry in
59 std::fs::read_dir(&dir).unwrap_or_else(|e| panic!("read {}: {e}", dir.display()))
60 {
61 let path = entry.unwrap().path();
62 if path.extension().is_none_or(|e| e != "json") {
63 continue;
64 }
65 let text = std::fs::read_to_string(&path).unwrap();
66 if !text.contains("smithy.api#optionalAuth") && !text.contains("\"smithy.api#auth\"") {
67 continue;
68 }
69 let model: serde_json::Value = serde_json::from_str(&text).unwrap();
70 let Some(shapes) = model["shapes"].as_object() else {
71 continue;
72 };
73 let Some(service) = shapes.values().find(|s| s["type"] == "service") else {
74 continue;
75 };
76 let Some(signing) = service["traits"]["aws.auth#sigv4"]["name"].as_str() else {
77 continue;
78 };
79 let service_open = service["traits"]["smithy.api#auth"]
80 .as_array()
81 .is_some_and(|a| a.is_empty());
82 for (id, shape) in shapes {
83 if shape["type"] != "operation" {
84 continue;
85 }
86 let traits = &shape["traits"];
87 let auth = traits.get("smithy.api#auth");
88 let open = traits.get("smithy.api#optionalAuth").is_some()
89 || auth
90 .and_then(|a| a.as_array())
91 .is_some_and(|a| a.is_empty())
92 || (auth.is_none() && service_open);
93 if open {
94 let op = id.split_once('#').map_or(id.as_str(), |(_, op)| op);
95 derived.push((signing.to_string(), op.to_string()));
96 }
97 }
98 }
99 derived.sort();
100 derived.dedup();
101 let table: Vec<(String, String)> = generated::UNAUTHENTICATED_OPERATIONS
102 .iter()
103 .map(|(s, o)| (s.to_string(), o.to_string()))
104 .collect();
105 assert_eq!(
106 table, derived,
107 "rerun scripts/generate-unauthenticated-operations.py"
108 );
109 }
110
111 #[test]
112 fn table_is_sorted_for_binary_search() {
113 assert!(generated::UNAUTHENTICATED_OPERATIONS
114 .windows(2)
115 .all(|w| w[0] < w[1]));
116 }
117
118 #[test]
119 fn model_marked_operations_are_unauthenticated() {
120 for (service, op) in [
121 ("cognito-idp", "InitiateAuth"),
122 ("cognito-idp", "SignUp"),
123 ("cognito-idp", "ConfirmSignUp"),
124 ("cognito-identity", "GetId"),
125 ("cognito-identity", "GetOpenIdToken"),
126 ("cognito-identity", "GetCredentialsForIdentity"),
127 ("sts", "AssumeRoleWithWebIdentity"),
128 ("sts", "AssumeRoleWithSAML"),
129 ] {
130 assert_eq!(
131 AnonymousAccess::for_operation(service, op),
132 AnonymousAccess::Unauthenticated,
133 "{service} {op}"
134 );
135 }
136 for (service, op) in [
137 ("sts", "GetCallerIdentity"),
138 ("sts", "AssumeRoot"),
139 ("cognito-idp", "AdminInitiateAuth"),
140 ("cognito-identity", "DescribeIdentityPool"),
141 ("kms", "ListKeys"),
142 ] {
143 assert_eq!(
144 AnonymousAccess::for_operation(service, op),
145 AnonymousAccess::Refused,
146 "{service} {op}"
147 );
148 }
149 }
150}