mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-10 23:26:53 +00:00
fix(iam): align OIDC parent IDs with MinIO (#5290)
* fix(iam): align OIDC parent IDs with MinIO * test(iam): cover OIDC STS binding policy lookup * test(admin): use runtime facade for AppContext setup * test(ilm): wait for lifecycle backfill before manual failure * test(ilm): make overlapping admission check concurrent
This commit is contained in:
@@ -32,6 +32,7 @@ pub(crate) use crate::runtime_sources::{
|
||||
#[cfg(test)]
|
||||
pub(crate) use crate::runtime_sources::{
|
||||
IamInterface, KmsInterface, NotificationSystemInterface, ServerConfigInterface, StorageClassInterface,
|
||||
publish_test_app_context,
|
||||
};
|
||||
use rustfs_config::server_config::Config;
|
||||
use rustfs_kms::KmsServiceManager;
|
||||
|
||||
@@ -266,7 +266,7 @@ impl FederatedSessionBinding for DefaultFederatedSessionBinding {
|
||||
FederatedSessionBindingError::InvalidRequest("verified OIDC identity is missing issuer or subject".to_string())
|
||||
})?;
|
||||
let selected_policy_names = match iam_store
|
||||
.policy_db_get(&parent_user, &Some(authorization.groups.clone()))
|
||||
.sts_policy_db_get(&parent_user, &Some(authorization.groups.clone()))
|
||||
.await
|
||||
.map_err(|_| FederatedSessionBindingError::Internal("failed to resolve OIDC policy mapping".to_string()))?
|
||||
{
|
||||
@@ -310,7 +310,13 @@ impl FederatedSessionBinding for DefaultFederatedSessionBinding {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::admin::runtime_sources::{AppContext, publish_test_app_context};
|
||||
use rustfs_iam::federation::{FederatedAuthorization, FederatedClaims};
|
||||
use rustfs_iam::store::{Store, UserType, object::IAM_CONFIG_PREFIX};
|
||||
use rustfs_kms::KmsServiceManager;
|
||||
use rustfs_madmin::{AccountStatus, AddOrUpdateUserReq};
|
||||
use serial_test::serial;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn transaction() -> FederatedSessionTransaction {
|
||||
FederatedSessionTransaction {
|
||||
@@ -348,14 +354,14 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issued_credentials_and_replication_item_preserve_existing_shape() {
|
||||
fn issued_credentials_and_replication_item_use_minio_parent_shape() {
|
||||
let transaction = transaction();
|
||||
let secret = "federated-session-test-signing-secret";
|
||||
let selected_policy_names = vec!["readonly".to_string()];
|
||||
|
||||
let credentials =
|
||||
issue_credentials(&transaction, &selected_policy_names, Some(secret)).expect("credential issuance should succeed");
|
||||
assert_eq!(credentials.parent_user, "openid=pUmguI1petsjVfDFQppmmR9yqdmWnBAXGJhHV_s9W3I");
|
||||
assert_eq!(credentials.parent_user, "TwyekekG2eMes0qk9Tgh7KXEitwGi1z2W1f2KccrXGA");
|
||||
assert_eq!(credentials.groups, Some(vec!["devs".to_string()]));
|
||||
|
||||
let claims = rustfs_iam::sys::get_claims_from_token_with_secret(&credentials.session_token, secret)
|
||||
@@ -364,11 +370,11 @@ mod tests {
|
||||
assert_eq!(claims.get("oidc_provider"), Some(&serde_json::json!("default")));
|
||||
assert_eq!(
|
||||
claims.get("parent"),
|
||||
Some(&serde_json::json!("openid=pUmguI1petsjVfDFQppmmR9yqdmWnBAXGJhHV_s9W3I"))
|
||||
Some(&serde_json::json!("TwyekekG2eMes0qk9Tgh7KXEitwGi1z2W1f2KccrXGA"))
|
||||
);
|
||||
assert_eq!(
|
||||
claims.get(OIDC_VIRTUAL_PARENT_CLAIM),
|
||||
Some(&serde_json::json!("openid=pUmguI1petsjVfDFQppmmR9yqdmWnBAXGJhHV_s9W3I"))
|
||||
Some(&serde_json::json!("TwyekekG2eMes0qk9Tgh7KXEitwGi1z2W1f2KccrXGA"))
|
||||
);
|
||||
assert_eq!(claims.get("policy"), Some(&serde_json::json!("readonly")));
|
||||
assert_eq!(claims.get("groups"), Some(&serde_json::json!(["devs"])));
|
||||
@@ -384,13 +390,77 @@ mod tests {
|
||||
assert_eq!(replicated.access_key, credentials.access_key);
|
||||
assert_eq!(replicated.secret_key, credentials.secret_key);
|
||||
assert_eq!(replicated.session_token, credentials.session_token);
|
||||
assert_eq!(replicated.parent_user, "openid=pUmguI1petsjVfDFQppmmR9yqdmWnBAXGJhHV_s9W3I");
|
||||
assert_eq!(replicated.parent_user, "TwyekekG2eMes0qk9Tgh7KXEitwGi1z2W1f2KccrXGA");
|
||||
assert_eq!(replicated.parent_policy_mapping, OIDC_STS_REQUIRES_VIRTUAL_PARENT_RECEIVER_POLICY);
|
||||
assert!(replicated.parent_policy_mapping.trim().is_empty());
|
||||
assert!(MappedPolicy::new(&replicated.parent_policy_mapping).to_slice().is_empty());
|
||||
assert_eq!(replicated.api_version.as_deref(), Some(SITE_REPL_API_VERSION));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn binding_uses_sts_policy_when_regular_mapping_collides() {
|
||||
let _ = rustfs_credentials::init_global_action_credentials(
|
||||
Some("TESTROOTACCESSKEY".to_string()),
|
||||
Some("TESTROOTSECRET123".to_string()),
|
||||
);
|
||||
if current_ready_iam_handle().is_err() {
|
||||
let env = rustfs_test_utils::TestECStoreEnv::builder()
|
||||
.prefix("federated_binding_sts_policy")
|
||||
.disk_count(1)
|
||||
.init_bucket_metadata(false)
|
||||
.build()
|
||||
.await;
|
||||
ObjectStore::new(Arc::clone(&env.ecstore))
|
||||
.save_iam_config(serde_json::json!({"version": 1}), format!("{}/format.json", *IAM_CONFIG_PREFIX))
|
||||
.await
|
||||
.expect("seed IAM format");
|
||||
let iam = rustfs_iam::build_iam_sys(Arc::clone(&env.ecstore))
|
||||
.await
|
||||
.expect("build test IAM");
|
||||
publish_test_app_context(Arc::new(AppContext::with_default_interfaces(
|
||||
env.ecstore,
|
||||
iam,
|
||||
Arc::new(KmsServiceManager::new()),
|
||||
)));
|
||||
}
|
||||
|
||||
let iam = current_ready_iam_handle().expect("test IAM should be ready");
|
||||
let mut transaction = transaction();
|
||||
transaction.authorization.claims.sub = "binding-sts-policy-subject".to_string();
|
||||
transaction.authorization.policies = vec!["readwrite".to_string()];
|
||||
let parent = transaction
|
||||
.authorization
|
||||
.oidc_virtual_parent()
|
||||
.expect("test authorization should have a virtual parent");
|
||||
iam.create_user(
|
||||
&parent,
|
||||
&AddOrUpdateUserReq {
|
||||
secret_key: "regular-user-secret".to_string(),
|
||||
policy: None,
|
||||
status: AccountStatus::Enabled,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("create colliding regular user");
|
||||
iam.policy_db_set(&parent, UserType::Reg, false, "readonly")
|
||||
.await
|
||||
.expect("store colliding regular policy mapping");
|
||||
iam.policy_db_set(&parent, UserType::Sts, false, "writeonly")
|
||||
.await
|
||||
.expect("store STS policy mapping");
|
||||
|
||||
let credentials = DefaultFederatedSessionBinding
|
||||
.bind(&transaction)
|
||||
.await
|
||||
.expect("production binding should issue credentials");
|
||||
let signing_key = current_token_signing_key().expect("test signing key should be initialized");
|
||||
let claims = rustfs_iam::sys::get_claims_from_token_with_secret(&credentials.session_token, &signing_key)
|
||||
.expect("issued session token should verify");
|
||||
|
||||
assert_eq!(claims.get("policy"), Some(&serde_json::json!("writeonly")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_session_policy_precedes_missing_signing_key() {
|
||||
let mut transaction = transaction();
|
||||
|
||||
@@ -59,6 +59,8 @@ pub(crate) fn set_test_outbound_tls_generation(generation: u64) {
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) use context::install_test_app_context;
|
||||
#[cfg(test)]
|
||||
pub(crate) use context::publish_global_app_context as publish_test_app_context;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) use context::{IamInterface, KmsInterface, NotificationSystemInterface, ServerConfigInterface, StorageClassInterface};
|
||||
|
||||
Reference in New Issue
Block a user