mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-23 12:49:04 +00:00
fix(policy): accept legacy bucket policy ID field (#6362)
This commit is contained in:
@@ -195,7 +195,8 @@ pub struct BucketPolicyArgs<'a> {
|
|||||||
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct BucketPolicy {
|
pub struct BucketPolicy {
|
||||||
#[serde(default, rename = "Id", skip_serializing_if = "ID::is_empty")]
|
// RUSTFS_COMPAT_TODO(rustfs-6339): accept bucket policies persisted with the legacy "ID" key. Remove after migration tooling rewrites every retained legacy bucket policy.
|
||||||
|
#[serde(default, rename = "Id", alias = "ID", skip_serializing_if = "ID::is_empty")]
|
||||||
pub id: ID,
|
pub id: ID,
|
||||||
#[serde(rename = "Version")]
|
#[serde(rename = "Version")]
|
||||||
pub version: String,
|
pub version: String,
|
||||||
@@ -2786,7 +2787,7 @@ mod test {
|
|||||||
let parsed: serde_json::Value = serde_json::from_str(&json).expect("Should parse");
|
let parsed: serde_json::Value = serde_json::from_str(&json).expect("Should parse");
|
||||||
|
|
||||||
// Verify empty fields are omitted
|
// Verify empty fields are omitted
|
||||||
assert!(!parsed.as_object().unwrap().contains_key("ID"), "Empty ID should be omitted");
|
assert!(parsed.get("Id").is_none(), "Empty ID should be omitted");
|
||||||
|
|
||||||
let statement = &parsed["Statement"][0];
|
let statement = &parsed["Statement"][0];
|
||||||
assert!(!statement.as_object().unwrap().contains_key("Sid"), "Empty Sid should be omitted");
|
assert!(!statement.as_object().unwrap().contains_key("Sid"), "Empty Sid should be omitted");
|
||||||
@@ -2809,6 +2810,43 @@ mod test {
|
|||||||
assert_eq!(statement["Principal"]["AWS"], "*");
|
assert_eq!(statement["Principal"]["AWS"], "*");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bucket_policy_deserializes_legacy_id() {
|
||||||
|
let legacy_policy = br#"{"ID":"","Version":"2012-10-17","Statement":[{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetObject"],"NotAction":[],"Resource":["arn:aws:s3:::bucket/*"],"NotResource":[],"Condition":{}}]}"#;
|
||||||
|
|
||||||
|
let policy: BucketPolicy =
|
||||||
|
serde_json::from_slice(legacy_policy).expect("bucket policy with legacy ID should deserialize");
|
||||||
|
assert!(policy.id.is_empty());
|
||||||
|
policy.is_valid().expect("legacy bucket policy should remain valid");
|
||||||
|
|
||||||
|
let policy: BucketPolicy = serde_json::from_str(r#"{"ID":"legacy-policy","Version":"2012-10-17","Statement":[]}"#)
|
||||||
|
.expect("non-empty legacy ID should deserialize");
|
||||||
|
assert_eq!(policy.id.0, "legacy-policy");
|
||||||
|
|
||||||
|
let serialized = serde_json::to_value(&policy).expect("bucket policy should serialize");
|
||||||
|
assert_eq!(serialized["Id"], "legacy-policy");
|
||||||
|
assert!(serialized.get("ID").is_none(), "legacy ID spelling should not be serialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bucket_policy_legacy_id_alias_remains_strict() {
|
||||||
|
let unknown_field = r#"{"Version":"2012-10-17","Statement":[],"Unexpected":true}"#;
|
||||||
|
let error =
|
||||||
|
serde_json::from_str::<BucketPolicy>(unknown_field).expect_err("unrelated unknown fields should remain rejected");
|
||||||
|
assert!(
|
||||||
|
error.to_string().contains("unknown field `Unexpected`"),
|
||||||
|
"unexpected deserialization error: {error}"
|
||||||
|
);
|
||||||
|
|
||||||
|
let duplicate_id = r#"{"Id":"current-policy","ID":"legacy-policy","Version":"2012-10-17","Statement":[]}"#;
|
||||||
|
let error = serde_json::from_str::<BucketPolicy>(duplicate_id)
|
||||||
|
.expect_err("canonical and legacy ID fields should not be accepted together");
|
||||||
|
assert!(
|
||||||
|
error.to_string().contains("duplicate field `Id`"),
|
||||||
|
"unexpected deserialization error: {error}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_existing_object_tag_condition_helpers() {
|
fn test_existing_object_tag_condition_helpers() {
|
||||||
let identity_policy = Policy::parse_config(
|
let identity_policy = Policy::parse_config(
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ for later deletion.
|
|||||||
|
|
||||||
## Open Items
|
## Open Items
|
||||||
|
|
||||||
|
- `rustfs-6339` legacy bucket policy ID casing: earlier RustFS releases persisted the top-level policy identifier as "ID", while current writes use the S3-compatible "Id" spelling. Readers accept both spellings so retained bucket metadata remains usable after upgrade. Remove the legacy alias after migration tooling has rewritten every retained bucket policy using "ID".
|
||||||
- `table-publication-fence-v1` table publication fencing: nodes that predate table and table-bucket publication fences can mutate live files while a new node is publishing a catalog pointer. New nodes retain exact object guards until the operator confirms that every serving node uses the new fences. Fleet confirmation also requires non-overlapping active warehouse prefixes and lifecycle workers that exclude table buckets. Remove the exact live-file fallback and the fleet-confirmation gate after the minimum supported RustFS release acquires table fences for registered-table mutations and table-bucket fences for unresolved-prefix mutations.
|
- `table-publication-fence-v1` table publication fencing: nodes that predate table and table-bucket publication fences can mutate live files while a new node is publishing a catalog pointer. New nodes retain exact object guards until the operator confirms that every serving node uses the new fences. Fleet confirmation also requires non-overlapping active warehouse prefixes and lifecycle workers that exclude table buckets. Remove the exact live-file fallback and the fleet-confirmation gate after the minimum supported RustFS release acquires table fences for registered-table mutations and table-bucket fences for unresolved-prefix mutations.
|
||||||
- `table-catalog-strong-snapshot-v1` durable strong catalog snapshot compatibility: version 1 writes continue during mixed-version rollout until operators confirm that every serving node reads version 2, and version 1 table/view identifier collisions remain available only for cleanup. Remove version 1 writes and collision cleanup after the minimum supported RustFS release reads version 2 and every retained durable strong snapshot is collision-free and has been upgraded to version 2.
|
- `table-catalog-strong-snapshot-v1` durable strong catalog snapshot compatibility: version 1 writes continue during mixed-version rollout until operators confirm that every serving node reads version 2, and version 1 table/view identifier collisions remain available only for cleanup. Remove version 1 writes and collision cleanup after the minimum supported RustFS release reads version 2 and every retained durable strong snapshot is collision-free and has been upgraded to version 2.
|
||||||
- `table-catalog-migration-fence-v1` durable strong migration fence compatibility: version 1 "PREPARING" fences did not distinguish a known-absent global strong snapshot from an unknown baseline, so retries read them but fail closed if the global snapshot is missing. Version 2 preserves the same JSON shape and records the pre-migration global snapshot ETag in the existing target_snapshot_etag field while the fence is "PREPARING". Remove version 1 reads after every supported direct-upgrade source writes version 2 fences and operators have completed or cancelled every older in-progress backing migration.
|
- `table-catalog-migration-fence-v1` durable strong migration fence compatibility: version 1 "PREPARING" fences did not distinguish a known-absent global strong snapshot from an unknown baseline, so retries read them but fail closed if the global snapshot is missing. Version 2 preserves the same JSON shape and records the pre-migration global snapshot ETag in the existing target_snapshot_etag field while the fence is "PREPARING". Remove version 1 reads after every supported direct-upgrade source writes version 2 fences and operators have completed or cancelled every older in-progress backing migration.
|
||||||
|
|||||||
Reference in New Issue
Block a user