From bc0f5292f3624f53832a4a68a67b481fb7b78420 Mon Sep 17 00:00:00 2001 From: GatewayJ <835269233@qq.com> Date: Thu, 15 Jan 2026 15:33:22 +0800 Subject: [PATCH] fix: standart policy format (#1508) --- crates/policy/src/policy/action.rs | 31 +++++++++++++++--------------- crates/policy/src/policy/policy.rs | 10 ++++++---- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/crates/policy/src/policy/action.rs b/crates/policy/src/policy/action.rs index e6ac3f3d..7af21b49 100644 --- a/crates/policy/src/policy/action.rs +++ b/crates/policy/src/policy/action.rs @@ -22,8 +22,8 @@ use strum::{EnumString, IntoStaticStr}; use super::{Error as IamError, Validator, utils::wildcard}; -/// A set of policy actions that serializes as a single string when containing one item, -/// or as an array when containing multiple items (matching AWS S3 API format). +/// A set of policy actions that always serializes as an array of strings, +/// conforming to the S3 policy specification for consistency and compatibility. #[derive(Clone, Default, Debug)] pub struct ActionSet(pub HashSet); @@ -34,15 +34,8 @@ impl Serialize for ActionSet { { use serde::ser::SerializeSeq; - if self.0.len() == 1 { - // Serialize single action as string (not array) - if let Some(action) = self.0.iter().next() { - let action_str: &str = action.into(); - return serializer.serialize_str(action_str); - } - } - - // Serialize multiple actions as array + // Always serialize as array, even for single action, to match S3 specification + // and ensure compatibility with AWS SDK clients that expect array format let mut seq = serializer.serialize_seq(Some(self.0.len()))?; for action in &self.0 { let action_str: &str = action.into(); @@ -610,13 +603,17 @@ mod tests { #[test] fn test_actionset_serialize_single_element() { - // Single element should serialize as string + // Single element should serialize as array for S3 specification compliance let mut set = HashSet::new(); set.insert(Action::S3Action(S3Action::GetObjectAction)); let actionset = ActionSet(set); let json = serde_json::to_string(&actionset).expect("Should serialize"); - assert_eq!(json, "\"s3:GetObject\""); + let parsed: serde_json::Value = serde_json::from_str(&json).expect("Should parse"); + assert!(parsed.is_array(), "Should serialize as array"); + let arr = parsed.as_array().expect("Should be array"); + assert_eq!(arr.len(), 1); + assert_eq!(arr[0].as_str().unwrap(), "s3:GetObject"); } #[test] @@ -636,12 +633,16 @@ mod tests { #[test] fn test_actionset_wildcard_serialization() { - // Wildcard action should serialize correctly + // Wildcard action should serialize as array for S3 specification compliance let mut set = HashSet::new(); set.insert(Action::try_from("*").expect("Should parse wildcard")); let actionset = ActionSet(set); let json = serde_json::to_string(&actionset).expect("Should serialize"); - assert_eq!(json, "\"s3:*\""); + let parsed: serde_json::Value = serde_json::from_str(&json).expect("Should parse"); + assert!(parsed.is_array(), "Should serialize as array"); + let arr = parsed.as_array().expect("Should be array"); + assert_eq!(arr.len(), 1); + assert_eq!(arr[0].as_str().unwrap(), "s3:*"); } } diff --git a/crates/policy/src/policy/policy.rs b/crates/policy/src/policy/policy.rs index 7eb9e2a3..46ccf984 100644 --- a/crates/policy/src/policy/policy.rs +++ b/crates/policy/src/policy/policy.rs @@ -1119,7 +1119,7 @@ mod test { } #[test] - fn test_bucket_policy_serialize_single_action_as_string() { + fn test_bucket_policy_serialize_single_action_as_array() { use crate::policy::action::{Action, ActionSet, S3Action}; use crate::policy::resource::{Resource, ResourceSet}; use crate::policy::{Effect, Principal}; @@ -1153,8 +1153,10 @@ mod test { let parsed: serde_json::Value = serde_json::from_str(&json).expect("Should parse"); let action = &parsed["Statement"][0]["Action"]; - // Single action should be serialized as string - assert!(action.is_string(), "Single action should serialize as string"); - assert_eq!(action.as_str().unwrap(), "s3:ListBucket"); + // Single action should be serialized as array for S3 specification compliance + assert!(action.is_array(), "Single action should serialize as array"); + let arr = action.as_array().expect("Should be array"); + assert_eq!(arr.len(), 1); + assert_eq!(arr[0].as_str().unwrap(), "s3:ListBucket"); } }