Skip to content

Commit 745c698

Browse files
fix(lambda-events): omit null IoT policy documents
1 parent 831d481 commit 745c698

2 files changed

Lines changed: 81 additions & 1 deletion

File tree

lambda-events/src/custom_serde/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use base64::Engine;
22
use serde::{
33
de::{Deserialize, Deserializer, Error as DeError},
44
ser::Serializer,
5+
Serialize,
56
};
67

78
#[cfg(feature = "codebuild")]
@@ -57,6 +58,14 @@ where
5758
serializer.serialize_str(&base64::engine::general_purpose::STANDARD.encode(value))
5859
}
5960

61+
pub(crate) fn serialize_non_null<S, T>(values: &[Option<T>], serializer: S) -> Result<S::Ok, S::Error>
62+
where
63+
S: Serializer,
64+
T: Serialize,
65+
{
66+
serializer.collect_seq(values.iter().flatten())
67+
}
68+
6069
/// Deserializes any `Default` type, mapping JSON `null` to `T::default()`.
6170
///
6271
/// **Note** null-to-empty semantics are usually clear for container types (Map, Vec, etc).

lambda-events/src/event/iot/mod.rs

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
use crate::{custom_serde::serialize_headers, encodings::Base64Data, iam::IamPolicyDocument};
1+
use crate::{
2+
custom_serde::{serialize_headers, serialize_non_null},
3+
encodings::Base64Data,
4+
iam::IamPolicyDocument,
5+
};
26
#[cfg(feature = "builders")]
37
use bon::Builder;
48
use http::HeaderMap;
@@ -141,6 +145,9 @@ pub struct IoTCoreCustomAuthorizerResponse {
141145
pub principal_id: Option<String>,
142146
pub disconnect_after_in_seconds: u32,
143147
pub refresh_after_in_seconds: u32,
148+
/// Policy documents returned to AWS IoT. `None` entries are omitted during serialization because AWS IoT rejects
149+
/// null policy documents.
150+
#[serde(serialize_with = "serialize_non_null")]
144151
pub policy_documents: Vec<Option<IamPolicyDocument>>,
145152
/// Catchall to catch any additional fields that were present but not explicitly defined by this struct.
146153
/// Enabled with Cargo feature `catch-all-fields`.
@@ -156,6 +163,28 @@ pub struct IoTCoreCustomAuthorizerResponse {
156163
mod test {
157164
use super::*;
158165

166+
fn custom_auth_response_with_policy_documents(
167+
policy_documents: Vec<Option<IamPolicyDocument>>,
168+
) -> IoTCoreCustomAuthorizerResponse {
169+
let data = include_bytes!("../../fixtures/example-iot-custom-auth-response.json");
170+
let mut response: IoTCoreCustomAuthorizerResponse = serde_json::from_slice(data).unwrap();
171+
response.policy_documents = policy_documents;
172+
response
173+
}
174+
175+
fn policy_document(version: &str) -> IamPolicyDocument {
176+
serde_json::from_value(serde_json::json!({
177+
"Version": version,
178+
"Statement": []
179+
}))
180+
.unwrap()
181+
}
182+
183+
fn serialized_policy_documents(policy_documents: Vec<Option<IamPolicyDocument>>) -> serde_json::Value {
184+
let response = custom_auth_response_with_policy_documents(policy_documents);
185+
serde_json::to_value(response).unwrap()["policyDocuments"].clone()
186+
}
187+
159188
#[test]
160189
#[cfg(feature = "iot")]
161190
fn example_iot_custom_auth_request() {
@@ -185,4 +214,46 @@ mod test {
185214
let reparsed: IoTCoreCustomAuthorizerResponse = serde_json::from_slice(output.as_bytes()).unwrap();
186215
assert_eq!(parsed, reparsed);
187216
}
217+
218+
#[test]
219+
fn iot_custom_auth_response_serializes_policy_document() {
220+
let policy = policy_document("2012-10-17");
221+
222+
assert_eq!(
223+
serde_json::json!([policy]),
224+
serialized_policy_documents(vec![Some(policy)])
225+
);
226+
}
227+
228+
#[test]
229+
fn iot_custom_auth_response_filters_none_policy_documents() {
230+
let first_policy = policy_document("2012-10-17");
231+
let second_policy = policy_document("2008-10-17");
232+
233+
assert_eq!(
234+
serde_json::json!([first_policy, second_policy]),
235+
serialized_policy_documents(vec![Some(first_policy), None, Some(second_policy)])
236+
);
237+
}
238+
239+
#[test]
240+
fn iot_custom_auth_response_serializes_only_none_as_empty_array() {
241+
assert_eq!(serde_json::json!([]), serialized_policy_documents(vec![None]));
242+
}
243+
244+
#[test]
245+
fn iot_custom_auth_response_serializes_empty_policy_documents_as_empty_array() {
246+
assert_eq!(serde_json::json!([]), serialized_policy_documents(vec![]));
247+
}
248+
249+
#[test]
250+
fn iot_custom_auth_response_deserializes_null_policy_document() {
251+
let response = custom_auth_response_with_policy_documents(vec![None]);
252+
let mut serialized = serde_json::to_value(response).unwrap();
253+
serialized["policyDocuments"] = serde_json::json!([null]);
254+
255+
let deserialized: IoTCoreCustomAuthorizerResponse = serde_json::from_value(serialized).unwrap();
256+
257+
assert_eq!(vec![None], deserialized.policy_documents);
258+
}
188259
}

0 commit comments

Comments
 (0)