mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-31 09:18:28 +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:
@@ -171,8 +171,12 @@ impl fmt::Debug for PeerInfo {
|
||||
pub struct SRPolicyMapping {
|
||||
#[serde(rename = "userOrGroup", default)]
|
||||
pub user_or_group: String,
|
||||
/// MinIO IAMUserType wire value (cmd/iam.go): unknown = -1, regUser = 0,
|
||||
/// stsUser = 1, svcUser = 2. Signed because MinIO sends -1 for group
|
||||
/// mappings. This is NOT the RustFS-internal `UserType` encoding; translate
|
||||
/// at the boundary with `rustfs_iam::store::{sr_wire_user_type, user_type_from_sr_wire}`.
|
||||
#[serde(rename = "userType", default)]
|
||||
pub user_type: u64,
|
||||
pub user_type: i64,
|
||||
#[serde(rename = "isGroup", default)]
|
||||
pub is_group: bool,
|
||||
#[serde(default)]
|
||||
@@ -330,8 +334,10 @@ pub struct SRSvcAccChange {
|
||||
pub struct SRCredInfo {
|
||||
#[serde(rename = "accessKey", default)]
|
||||
pub access_key: String,
|
||||
/// MinIO IAMUserType wire value (same table as `SRPolicyMapping::user_type`);
|
||||
/// signed because MinIO's unknown is -1.
|
||||
#[serde(rename = "iamUserType", default)]
|
||||
pub iam_user_type: u64,
|
||||
pub iam_user_type: i64,
|
||||
#[serde(rename = "isDeleteReq", default)]
|
||||
pub is_delete_req: bool,
|
||||
#[serde(rename = "userIdentityJSON", default, skip_serializing_if = "Option::is_none")]
|
||||
@@ -1399,7 +1405,7 @@ pub struct SiteNetPerfResult {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{PeerInfo, PeerSite, SRInfo, SRResyncOpStatus};
|
||||
use super::{PeerInfo, PeerSite, SRCredInfo, SRInfo, SRPolicyMapping, SRResyncOpStatus};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
const TEST_CA_CERT: &str = "-----BEGIN CERTIFICATE-----\ntest-ca\n-----END CERTIFICATE-----";
|
||||
@@ -1500,6 +1506,35 @@ mod tests {
|
||||
assert!(peer_debug.contains("has_custom_ca: true"));
|
||||
}
|
||||
|
||||
/// MinIO IAMUserType wire semantics (cmd/iam.go): unknown = -1,
|
||||
/// regUser = 0, stsUser = 1, svcUser = 2. MinIO group policy mappings
|
||||
/// arrive with `userType: -1`; the wire field must accept negatives.
|
||||
#[test]
|
||||
fn sr_policy_mapping_accepts_minio_negative_user_type() {
|
||||
let mapping: SRPolicyMapping = serde_json::from_value(json!({
|
||||
"userOrGroup": "devs",
|
||||
"userType": -1,
|
||||
"isGroup": true,
|
||||
"policy": "readwrite"
|
||||
}))
|
||||
.expect("MinIO group mapping with userType -1 must deserialize");
|
||||
assert_eq!(mapping.user_type, -1);
|
||||
assert!(mapping.is_group);
|
||||
assert_eq!(mapping.policy, "readwrite");
|
||||
}
|
||||
|
||||
/// Same IAMUserType family as SRPolicyMapping: MinIO may send -1 (unknown).
|
||||
#[test]
|
||||
fn sr_cred_info_accepts_minio_negative_iam_user_type() {
|
||||
let cred: SRCredInfo = serde_json::from_value(json!({
|
||||
"accessKey": "replicated-user",
|
||||
"iamUserType": -1
|
||||
}))
|
||||
.expect("SRCredInfo with iamUserType -1 must deserialize");
|
||||
assert_eq!(cred.iam_user_type, -1);
|
||||
assert_eq!(cred.access_key, "replicated-user");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resync_status_legacy_json_defaults_new_lifecycle_fields() {
|
||||
let legacy_json = json!({
|
||||
|
||||
Reference in New Issue
Block a user