mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-20 19:42:17 +00:00
fix(site-replication): translate policy mapping userType at MinIO wire boundary (#5751)
* test(site-replication): pin MinIO IAMUserType wire semantics for policy mappings Red tests for P0-4: MinIO peers send SRPolicyMapping.UserType using the madmin IAMUserType table (unknown=-1, regUser=0, stsUser=1, svcUser=2), while RustFS deserializes the field as u64 and decodes it with the internal RPC table (None=0, Svc=1, Sts=2, Reg=3). - userType -1 (MinIO group mappings) fails to deserialize, rejecting the whole IAM item: group mappings never sync from MinIO. - stsUser=1 decodes as Svc, landing federated STS mappings under the wrong prefix and silently dropping their effect. * fix(site-replication): translate policy mapping userType at MinIO wire boundary SRPolicyMapping.userType travels on the wire using MinIO's IAMUserType table (unknown=-1, regUser=0, stsUser=1, svcUser=2), but RustFS stored the field as u64 and reused the internal RPC encoding UserType::to_u64/from_u64 (None=0, Svc=1, Sts=2, Reg=3) at the site replication boundary. Consequences: MinIO group mappings (userType -1) failed to deserialize and the whole IAM item was rejected, and MinIO STS mappings (1) were stored as service-account mappings, silently dropping federated users' policies. - Widen SRPolicyMapping.user_type and SRCredInfo.iam_user_type to i64 so MinIO's -1 deserializes. - Add sr_wire_user_type / user_type_from_sr_wire in rustfs-iam as the dedicated SR wire codec: MinIO table on both directions, groups always encoded as 0, and wire value 3 kept forever as an alias for Reg so mappings from pre-fix RustFS peers still decode; unknown values fail closed. - Route the SR inbound (apply_iam_item) and outbound (mapped_policy_to_sr_mapping, policy-mapping change hooks) paths through the codec. The internal UserType::to_u64/from_u64 encoding is untouched: it is the intra-cluster node RPC contract and changing it would break rolling restarts. Outbound compatibility with old RustFS peers is preserved because UserType::None and Reg share the users prefix in get_mapped_policy_path, so wire 0 lands in the same location Reg=3 did.
This commit is contained in:
@@ -571,7 +571,7 @@ impl Operation for SetPolicyForUserOrGroup {
|
||||
r#type: "policy-mapping".to_string(),
|
||||
policy_mapping: Some(SRPolicyMapping {
|
||||
user_or_group: query.user_or_group.clone(),
|
||||
user_type: rustfs_iam::store::UserType::Reg.to_u64(),
|
||||
user_type: rustfs_iam::store::sr_wire_user_type(rustfs_iam::store::UserType::Reg, query.is_group),
|
||||
is_group: query.is_group,
|
||||
policy: query.policy_name.clone(),
|
||||
updated_at: Some(updated_at),
|
||||
@@ -1060,7 +1060,7 @@ pub(crate) async fn handle_builtin_policy_association(
|
||||
r#type: "policy-mapping".to_string(),
|
||||
policy_mapping: Some(SRPolicyMapping {
|
||||
user_or_group: target_name.clone(),
|
||||
user_type: rustfs_iam::store::UserType::Reg.to_u64(),
|
||||
user_type: rustfs_iam::store::sr_wire_user_type(rustfs_iam::store::UserType::Reg, is_group),
|
||||
is_group,
|
||||
policy: updated_policies.join(","),
|
||||
updated_at: Some(updated_at),
|
||||
|
||||
@@ -63,7 +63,7 @@ use rustfs_config::{
|
||||
};
|
||||
use rustfs_iam::error::is_err_no_such_service_account;
|
||||
use rustfs_iam::federation::OIDC_VIRTUAL_PARENT_CLAIM;
|
||||
use rustfs_iam::store::{MappedPolicy, UserType};
|
||||
use rustfs_iam::store::{MappedPolicy, UserType, sr_wire_user_type, user_type_from_sr_wire};
|
||||
use rustfs_iam::sys::{
|
||||
NewServiceAccountOpts, SITE_REPLICATOR_SERVICE_ACCOUNT, UpdateServiceAccountOpts, get_claims_from_token_with_secret,
|
||||
};
|
||||
@@ -4303,7 +4303,7 @@ fn local_idp_settings() -> IDPSettings {
|
||||
fn mapped_policy_to_sr_mapping(name: String, is_group: bool, user_type: UserType, mapping: MappedPolicy) -> SRPolicyMapping {
|
||||
SRPolicyMapping {
|
||||
user_or_group: name,
|
||||
user_type: user_type.to_u64(),
|
||||
user_type: sr_wire_user_type(user_type, is_group),
|
||||
is_group,
|
||||
policy: mapping.policies,
|
||||
updated_at: Some(mapping.update_at),
|
||||
@@ -7824,7 +7824,8 @@ async fn apply_iam_item(item: SRIAMItem) -> S3Result<()> {
|
||||
let Some(mapping) = item.policy_mapping else {
|
||||
return Err(s3_error!(InvalidRequest, "policyMapping is required"));
|
||||
};
|
||||
let user_type = UserType::from_u64(mapping.user_type).ok_or_else(|| s3_error!(InvalidRequest, "invalid userType"))?;
|
||||
let user_type =
|
||||
user_type_from_sr_wire(mapping.user_type).ok_or_else(|| s3_error!(InvalidRequest, "invalid userType"))?;
|
||||
iam_sys
|
||||
.policy_db_set(&mapping.user_or_group, user_type, mapping.is_group, &mapping.policy)
|
||||
.await
|
||||
@@ -11910,7 +11911,7 @@ mod tests {
|
||||
"alice".to_string(),
|
||||
SRPolicyMapping {
|
||||
user_or_group: "alice".to_string(),
|
||||
user_type: UserType::Reg.to_u64(),
|
||||
user_type: sr_wire_user_type(UserType::Reg, false),
|
||||
policy: "readwrite".to_string(),
|
||||
updated_at: Some(OffsetDateTime::UNIX_EPOCH),
|
||||
..Default::default()
|
||||
|
||||
Reference in New Issue
Block a user