fakecloud_core/
auth_message.rs1use base64::Engine;
19use flate2::read::ZlibDecoder;
20use flate2::write::ZlibEncoder;
21use flate2::Compression;
22use serde_json::{json, Value};
23use std::io::{Read, Write};
24
25pub fn encode_deny(
32 explicit: bool,
33 action: Option<&str>,
34 principal_arn: Option<&str>,
35 matched_statements: Vec<Value>,
36 context: Option<Value>,
37) -> String {
38 let mut payload = json!({
39 "allowed": false,
40 "explicitDeny": explicit,
41 "matchedStatements": { "items": matched_statements },
42 });
43 if let Some(a) = action {
44 payload["action"] = json!(a);
45 }
46 if let Some(p) = principal_arn {
47 payload["principal"] = json!(p);
48 }
49 if let Some(c) = context {
50 payload["context"] = c;
51 }
52 let json_bytes = serde_json::to_vec(&payload).unwrap_or_default();
53 let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
54 encoder.write_all(&json_bytes).ok();
55 let compressed = encoder.finish().unwrap_or_default();
56 base64::engine::general_purpose::STANDARD.encode(compressed)
57}
58
59pub fn decode_message(encoded: &str) -> Result<String, &'static str> {
66 let compressed = base64::engine::general_purpose::STANDARD
67 .decode(encoded.as_bytes())
68 .map_err(|_| "EncodedMessage is not valid base64")?;
69 let mut decoder = ZlibDecoder::new(&compressed[..]);
70 let mut json_bytes = Vec::new();
71 decoder
72 .read_to_end(&mut json_bytes)
73 .map_err(|_| "EncodedMessage is not a valid deny token")?;
74 String::from_utf8(json_bytes).map_err(|_| "EncodedMessage payload is not valid UTF-8")
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn round_trip_explicit_deny() {
83 let token = encode_deny(
84 true,
85 Some("s3:GetObject"),
86 Some("arn:aws:iam::111122223333:user/alice"),
87 vec![json!({"sourcePolicyId": "PolicyA"})],
88 Some(json!({"aws:SourceIp": "1.2.3.4"})),
89 );
90 let decoded = decode_message(&token).unwrap();
91 let parsed: Value = serde_json::from_str(&decoded).unwrap();
92 assert_eq!(parsed["allowed"], false);
93 assert_eq!(parsed["explicitDeny"], true);
94 assert_eq!(parsed["action"], "s3:GetObject");
95 assert_eq!(parsed["principal"], "arn:aws:iam::111122223333:user/alice");
96 assert_eq!(
97 parsed["matchedStatements"]["items"][0]["sourcePolicyId"],
98 "PolicyA"
99 );
100 }
101
102 #[test]
103 fn round_trip_implicit_deny_with_no_extras() {
104 let token = encode_deny(false, None, None, Vec::new(), None);
105 let decoded = decode_message(&token).unwrap();
106 let parsed: Value = serde_json::from_str(&decoded).unwrap();
107 assert_eq!(parsed["allowed"], false);
108 assert_eq!(parsed["explicitDeny"], false);
109 assert!(parsed["matchedStatements"]["items"]
110 .as_array()
111 .unwrap()
112 .is_empty());
113 assert!(parsed.get("action").is_none());
114 }
115
116 #[test]
117 fn rejects_garbage_base64() {
118 assert!(decode_message("not!!!base64!!").is_err());
119 }
120
121 #[test]
122 fn rejects_base64_that_is_not_zlib() {
123 let token = base64::engine::general_purpose::STANDARD.encode(b"not zlib data");
124 assert!(decode_message(&token).is_err());
125 }
126}