From 15c2bade5f71c96918a4ed5fd1814e302c7f3941 Mon Sep 17 00:00:00 2001 From: Zhengchao An Date: Tue, 4 Aug 2026 23:21:36 +0800 Subject: [PATCH] fix(iam): disambiguate OIDC virtual parent IDs (#5700) --- crates/iam/src/federation/model.rs | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/crates/iam/src/federation/model.rs b/crates/iam/src/federation/model.rs index 97fe3cb69..26cebcc85 100644 --- a/crates/iam/src/federation/model.rs +++ b/crates/iam/src/federation/model.rs @@ -64,10 +64,13 @@ impl FederatedAuthorization { return None; } - let mut source = Vec::with_capacity(8 + subject.len() + issuer.len()); + let subject_len = u64::try_from(subject.len()).ok()?; + let issuer_len = u64::try_from(issuer.len()).ok()?; + let mut source = Vec::with_capacity(23 + subject.len() + issuer.len()); source.extend_from_slice(b"openid:"); + source.extend_from_slice(&subject_len.to_be_bytes()); source.extend_from_slice(subject.as_bytes()); - source.push(b':'); + source.extend_from_slice(&issuer_len.to_be_bytes()); source.extend_from_slice(issuer.as_bytes()); let digest = HashAlgorithm::SHA256.hash_encode(&source); Some(base64_simd::URL_SAFE_NO_PAD.encode_to_string(digest.as_ref())) @@ -145,7 +148,7 @@ mod tests { } #[test] - fn oidc_virtual_parent_matches_minio_and_is_issuer_scoped() { + fn oidc_virtual_parent_is_issuer_scoped() { let first = authorization(Vec::new(), Vec::new()); let mut second = first.clone(); second @@ -155,7 +158,7 @@ mod tests { assert_eq!( first.oidc_virtual_parent().as_deref(), - Some("TwyekekG2eMes0qk9Tgh7KXEitwGi1z2W1f2KccrXGA") + Some("HwDfWftzOy4jiuS3WjKytC_Sg_A2hKhrRAFtBDhoBr0") ); assert!(!rustfs_policy::auth::contains_reserved_chars( first.oidc_virtual_parent().as_deref().expect("virtual parent") @@ -163,6 +166,24 @@ mod tests { assert_ne!(first.oidc_virtual_parent(), second.oidc_virtual_parent()); } + #[test] + fn oidc_virtual_parent_length_delimits_identity_parts() { + let mut first = authorization(Vec::new(), Vec::new()); + first.claims.sub = "subject".to_string(); + first.claims.raw.insert( + "iss".to_string(), + Value::String("https://issuer.example/path:https://other.example".to_string()), + ); + let mut second = authorization(Vec::new(), Vec::new()); + second.claims.sub = "subject:https://issuer.example/path".to_string(); + second + .claims + .raw + .insert("iss".to_string(), Value::String("https://other.example".to_string())); + + assert_ne!(first.oidc_virtual_parent(), second.oidc_virtual_parent()); + } + #[test] fn oidc_virtual_parent_requires_verified_identity_parts() { let mut missing_issuer = authorization(Vec::new(), Vec::new());