refactor: route IAM reads through app context (#3786)

This commit is contained in:
Zhengchao An
2026-06-23 17:57:55 +08:00
committed by GitHub
parent 61cfd4fc13
commit fd2ab38581
13 changed files with 106 additions and 46 deletions
+41 -7
View File
@@ -5,15 +5,16 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
## Current Context ## Current Context
- Issue: [`rustfs/backlog#660`](https://github.com/rustfs/backlog/issues/660) - Issue: [`rustfs/backlog#660`](https://github.com/rustfs/backlog/issues/660)
- Branch: `overtrue/arch-runtime-context-consumer-batch` - Branch: `overtrue/arch-iam-global-read-batch`
- Baseline: completed `C-011/C-012/C-013/API-055/API-059/API-079/API-080/API-081/API-082/API-083/API-084/API-085/API-086/API-087/API-088/API-089/API-090/API-091/API-092/API-093/API-094/API-095/API-096/API-097/API-098/API-099/API-100/API-101/API-102/API-103/API-104/API-105/API-106/API-107/API-108/API-109/API-110/API-111/API-112/API-113/API-114/API-115/API-116/API-117/API-118/API-119/API-120/API-121/API-122/API-123/API-124/API-125/API-126/API-127/API-128/API-129/API-130/API-131/API-132/API-133/API-134/API-135/API-136/API-137/API-138/API-139/API-140/API-141/API-142/API-143/API-144/API-145/API-146/API-147/API-148/API-149/API-150/API-151/API-152/API-153/API-154/API-155/API-156/API-157/API-158/API-159/API-160/API-161/API-162/API-163/API-164/API-165/API-166/API-167/API-168/API-169/API-170/API-171/API-172/API-173/API-174/API-175/API-176/API-177`. - Baseline: completed `C-011/C-012/C-013/API-055/API-059/API-079/API-080/API-081/API-082/API-083/API-084/API-085/API-086/API-087/API-088/API-089/API-090/API-091/API-092/API-093/API-094/API-095/API-096/API-097/API-098/API-099/API-100/API-101/API-102/API-103/API-104/API-105/API-106/API-107/API-108/API-109/API-110/API-111/API-112/API-113/API-114/API-115/API-116/API-117/API-118/API-119/API-120/API-121/API-122/API-123/API-124/API-125/API-126/API-127/API-128/API-129/API-130/API-131/API-132/API-133/API-134/API-135/API-136/API-137/API-138/API-139/API-140/API-141/API-142/API-143/API-144/API-145/API-146/API-147/API-148/API-149/API-150/API-151/API-152/API-153/API-154/API-155/API-156/API-157/API-158/API-159/API-160/API-161/API-162/API-163/API-164/API-165/API-166/API-167/API-168/API-169/API-170/API-171/API-172/API-173/API-174/API-175/API-176/API-177/API-178`.
- Based on: API-170 merged in PR #3783; this branch batches API-171 through - Based on: API-171 through API-177 prepared in PR #3785; this branch batches
API-177 on top of latest `main`. the next IAM consumer migration on top of that branch.
- PR type for this branch: `consumer-migration` - PR type for this branch: `consumer-migration`
- Runtime behavior changes: none. - Runtime behavior changes: none.
- Rust code changes: route replication pool, outbound TLS generation, runtime - Rust code changes: route replication pool, outbound TLS generation, runtime
region, KMS encryption service, runtime support handles, S3 Select DB, and region, KMS encryption service, runtime support handles, S3 Select DB,
internode RPC metrics through AppContext-first resolvers. internode RPC metrics, and IAM authorization/handler reads through
AppContext-first resolvers.
- CI/script changes: lock completed owner and test/fuzz boundaries against - CI/script changes: lock completed owner and test/fuzz boundaries against
bare/glob imports, scattered raw ECStore facade subpaths, and startup bare/glob imports, scattered raw ECStore facade subpaths, and startup
runtime/root-server/table/S3/app shared/app bucket/app ECStore/admin facade runtime/root-server/table/S3/app shared/app bucket/app ECStore/admin facade
@@ -22,7 +23,7 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
and storage owner thin bridge regressions, plus app context and notify and storage owner thin bridge regressions, plus app context and notify
event-bridge thin module regressions; accept the reviewed AppContext resolver event-bridge thin module regressions; accept the reviewed AppContext resolver
reverse dependencies in the layer baseline. reverse dependencies in the layer baseline.
- Docs changes: record the API-136 through API-177 owner facade cleanup. - Docs changes: record the API-136 through API-178 owner facade cleanup.
## Phase 0 Tasks ## Phase 0 Tasks
@@ -4556,6 +4557,20 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
metrics scan, Rust risk scan, branch freshness check, and three-expert metrics scan, Rust risk scan, branch freshness check, and three-expert
review. review.
- [x] `API-178` Route IAM runtime reads through AppContext.
- Do: route auth, storage authorization, admin auth, admin IAM handlers, STS,
and table-catalog credential issuance through an AppContext-first ready IAM
resolver.
- Acceptance: production auth/admin/storage request paths no longer call the
IAM global getter directly, while the resolver preserves the legacy ready
check and global fallback.
- Must preserve: signature secret lookup, access-key validation, S3 policy
authorization, table data-plane authorization, admin IAM CRUD, STS temp-user
creation, service-account flows, and table credential issuance.
- Verification: RustFS compile coverage, targeted context resolver tests,
migration guard, layer guard, formatting, diff hygiene, residual IAM getter
scan, Rust risk scan, branch freshness check, and three-expert review.
## Next PRs ## Next PRs
1. `consumer-migration`: continue reducing direct global reads behind AppContext resolver boundaries. 1. `consumer-migration`: continue reducing direct global reads behind AppContext resolver boundaries.
@@ -4643,11 +4658,30 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
| Quality/architecture | pass | API-177 keeps internode RPC metrics behind an AppContext-first resolver across HTTP and gRPC RPC paths. | | Quality/architecture | pass | API-177 keeps internode RPC metrics behind an AppContext-first resolver across HTTP and gRPC RPC paths. |
| Migration preservation | pass | Request counters, byte accounting, transport backend labels, and error metrics preserve existing global fallback behavior. | | Migration preservation | pass | Request counters, byte accounting, transport backend labels, and error metrics preserve existing global fallback behavior. |
| Testing/verification | pass | RustFS focused compile, targeted context resolver test, formatting, migration/layer guards, diff hygiene, residual internode metrics scan, and Rust risk scan passed for API-177. | | Testing/verification | pass | RustFS focused compile, targeted context resolver test, formatting, migration/layer guards, diff hygiene, residual internode metrics scan, and Rust risk scan passed for API-177. |
| Quality/architecture | pass | API-178 keeps ready IAM access behind an AppContext-first resolver without widening handler semantics. |
| Migration preservation | pass | Auth, storage authorization, admin IAM handlers, STS, and table credential flows keep existing error mapping and ready-check fallback. |
| Testing/verification | pass | RustFS focused compile, targeted context resolver test, formatting, migration/layer guards, diff hygiene, residual IAM getter scan, Rust risk scan, and pre-commit passed for API-178. |
## Verification Notes ## Verification Notes
Passed before push: Passed before push:
- Issue #660 API-178 current slice:
- `cargo check --tests -p rustfs`: passed.
- `cargo test -p rustfs resolver_helpers_are_context_first_and_fallback_when_context_is_absent --lib`:
passed.
- `cargo fmt --all`: passed.
- `cargo fmt --all --check`: passed.
- `git diff --check`: passed.
- `bash -n scripts/check_architecture_migration_rules.sh`: passed.
- `./scripts/check_architecture_migration_rules.sh`: passed.
- `./scripts/check_layer_dependencies.sh`: passed.
- IAM getter scan: passed; production auth/admin/storage IAM reads now go
through the AppContext ready IAM resolver.
- Rust risk scan: no new production unwrap/expect, panic/todo/unsafe, or cast
risks added.
- `make pre-commit`: passed.
- Issue #660 API-176 current slice: - Issue #660 API-176 current slice:
- `cargo check --tests -p rustfs`: passed. - `cargo check --tests -p rustfs`: passed.
- `cargo test -p rustfs resolver_helpers_are_context_first_and_fallback_when_context_is_absent --lib`: - `cargo test -p rustfs resolver_helpers_are_context_first_and_fallback_when_context_is_absent --lib`:
+2 -2
View File
@@ -56,7 +56,7 @@ pub async fn validate_admin_request(
actions: Vec<Action>, actions: Vec<Action>,
remote_addr: Option<std::net::SocketAddr>, remote_addr: Option<std::net::SocketAddr>,
) -> S3Result<()> { ) -> S3Result<()> {
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam not init")); return Err(s3_error!(InternalError, "iam not init"));
}; };
let ctx = AuthContext { let ctx = AuthContext {
@@ -146,7 +146,7 @@ pub async fn validate_admin_request_with_bucket_object(
remote_addr: Option<std::net::SocketAddr>, remote_addr: Option<std::net::SocketAddr>,
resource: AdminResourceScope<'_>, resource: AdminResourceScope<'_>,
) -> S3Result<()> { ) -> S3Result<()> {
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam not init")); return Err(s3_error!(InternalError, "iam not init"));
}; };
let ctx = AuthContext { let ctx = AuthContext {
+1 -1
View File
@@ -69,7 +69,7 @@ impl Operation for AccountInfoHandler {
let (cred, owner) = authenticate_request(&req.headers, &req.uri, &input_cred).await?; let (cred, owner) = authenticate_request(&req.headers, &req.uri, &input_cred).await?;
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InvalidRequest, "iam not init")); return Err(s3_error!(InvalidRequest, "iam not init"));
}; };
+5 -5
View File
@@ -115,7 +115,7 @@ impl Operation for ListGroups {
) )
.await?; .await?;
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam is not initialized")); return Err(s3_error!(InternalError, "iam is not initialized"));
}; };
@@ -180,7 +180,7 @@ impl Operation for GetGroup {
GroupQuery::default() GroupQuery::default()
} }
}; };
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam is not initialized")); return Err(s3_error!(InternalError, "iam is not initialized"));
}; };
@@ -256,7 +256,7 @@ impl Operation for DeleteGroup {
let group = decode_delete_group_name(&params)?; let group = decode_delete_group_name(&params)?;
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam is not initialized")); return Err(s3_error!(InternalError, "iam is not initialized"));
}; };
@@ -394,7 +394,7 @@ impl Operation for SetGroupStatus {
return Err(s3_error!(InvalidArgument, "group is required")); return Err(s3_error!(InvalidArgument, "group is required"));
} }
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam is not initialized")); return Err(s3_error!(InternalError, "iam is not initialized"));
}; };
@@ -537,7 +537,7 @@ impl Operation for UpdateGroupMembers {
"admin group state" "admin group state"
); );
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam is not initialized")); return Err(s3_error!(InternalError, "iam is not initialized"));
}; };
+2 -1
View File
@@ -58,7 +58,8 @@ impl Operation for IsAdminHandler {
true true
} else { } else {
let empty_claims = HashMap::new(); let empty_claims = HashMap::new();
let iam_store = rustfs_iam::get().map_err(|_| s3_error!(InternalError, "iam not init"))?; let iam_store =
crate::app::context::resolve_ready_iam_handle().map_err(|_| s3_error!(InternalError, "iam not init"))?;
let conditions = get_condition_values(&req.headers, &cred, None, None, None); let conditions = get_condition_values(&req.headers, &cred, None, None, None);
iam_store iam_store
.is_allowed(&Args { .is_allowed(&Args {
+11 -7
View File
@@ -144,7 +144,7 @@ impl Operation for ListCannedPolicies {
} }
}; };
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam is not initialized")); return Err(s3_error!(InternalError, "iam is not initialized"));
}; };
@@ -252,7 +252,7 @@ impl Operation for AddCannedPolicy {
if policy.version.is_empty() { if policy.version.is_empty() {
return Err(s3_error!(InvalidArgument, "policy version is required")); return Err(s3_error!(InvalidArgument, "policy version is required"));
} }
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam is not initialized")); return Err(s3_error!(InternalError, "iam is not initialized"));
}; };
@@ -340,7 +340,7 @@ impl Operation for InfoCannedPolicy {
return Err(s3_error!(InvalidArgument, "too many policies")); return Err(s3_error!(InvalidArgument, "too many policies"));
} }
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam is not initialized")); return Err(s3_error!(InternalError, "iam is not initialized"));
}; };
@@ -401,7 +401,7 @@ impl Operation for RemoveCannedPolicy {
return Err(s3_error!(InvalidArgument, "policy name is required")); return Err(s3_error!(InvalidArgument, "policy name is required"));
} }
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam is not initialized")); return Err(s3_error!(InternalError, "iam is not initialized"));
}; };
@@ -492,7 +492,7 @@ impl Operation for SetPolicyForUserOrGroup {
return Err(s3_error!(InvalidArgument, "user or group is required")); return Err(s3_error!(InvalidArgument, "user or group is required"));
} }
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam is not initialized")); return Err(s3_error!(InternalError, "iam is not initialized"));
}; };
@@ -825,7 +825,9 @@ async fn handle_builtin_policy_entities(req: S3Request<Body>) -> S3Result<S3Resp
let query = parse_policy_entities_query(req.uri.query()); let query = parse_policy_entities_query(req.uri.query());
let Ok(iam_store) = rustfs_iam::get() else { return Err(s3_error!(InternalError, "iam not init")) }; let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam not init"));
};
let all_group_policy_mappings = collect_group_policy_mappings(&iam_store, &[]).await?; let all_group_policy_mappings = collect_group_policy_mappings(&iam_store, &[]).await?;
let users = iam_store.list_users().await.map_err(|e| { let users = iam_store.list_users().await.map_err(|e| {
@@ -959,7 +961,9 @@ async fn handle_builtin_policy_association(req: S3Request<Body>, is_attach: bool
.map_err(|e| s3_error!(InvalidRequest, "unmarshal policy association body failed, e: {:?}", e))?; .map_err(|e| s3_error!(InvalidRequest, "unmarshal policy association body failed, e: {:?}", e))?;
validate_policy_association_req(&assoc_req)?; validate_policy_association_req(&assoc_req)?;
let Ok(iam_store) = rustfs_iam::get() else { return Err(s3_error!(InternalError, "iam not init")) }; let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam not init"));
};
let (target_name, is_group, existing_policies) = if !assoc_req.user.is_empty() { let (target_name, is_group, existing_policies) = if !assoc_req.user.is_empty() {
match iam_store.is_temp_user(&assoc_req.user).await { match iam_store.is_temp_user(&assoc_req.user).await {
+8 -8
View File
@@ -292,7 +292,7 @@ impl Operation for AddServiceAccount {
req_is_derived_cred = true; req_is_derived_cred = true;
} }
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InvalidRequest, "iam not init")); return Err(s3_error!(InvalidRequest, "iam not init"));
}; };
@@ -513,7 +513,7 @@ impl Operation for UpdateServiceAccount {
return Err(s3_error!(InvalidRequest, "get cred failed")); return Err(s3_error!(InvalidRequest, "get cred failed"));
}; };
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InvalidRequest, "iam not init")); return Err(s3_error!(InvalidRequest, "iam not init"));
}; };
@@ -642,7 +642,7 @@ impl Operation for InfoServiceAccount {
let access_key = query.access_key; let access_key = query.access_key;
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InvalidRequest, "iam not init")); return Err(s3_error!(InvalidRequest, "iam not init"));
}; };
@@ -719,7 +719,7 @@ impl Operation for TemporaryAccountInfo {
let (cred, owner) = let (cred, owner) =
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?; check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InvalidRequest, "iam not init")); return Err(s3_error!(InvalidRequest, "iam not init"));
}; };
@@ -789,7 +789,7 @@ impl Operation for InfoAccessKey {
query.access_key query.access_key
}; };
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InvalidRequest, "iam not init")); return Err(s3_error!(InvalidRequest, "iam not init"));
}; };
@@ -912,7 +912,7 @@ impl Operation for ListServiceAccount {
// cred.parent_user // cred.parent_user
// }; // };
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InvalidRequest, "iam not init")); return Err(s3_error!(InvalidRequest, "iam not init"));
}; };
@@ -1049,7 +1049,7 @@ impl Operation for ListAccessKeysBulk {
let (cred, owner) = let (cred, owner) =
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?; check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InvalidRequest, "iam not init")); return Err(s3_error!(InvalidRequest, "iam not init"));
}; };
@@ -1252,7 +1252,7 @@ impl Operation for DeleteServiceAccount {
return Err(s3_error!(InvalidArgument, "access key is empty")); return Err(s3_error!(InvalidArgument, "access key is empty"));
} }
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InvalidRequest, "iam not init")); return Err(s3_error!(InvalidRequest, "iam not init"));
}; };
+3 -2
View File
@@ -187,7 +187,7 @@ async fn handle_assume_role(
return Err(s3_error!(InvalidRequest, "AccessDenied")); return Err(s3_error!(InvalidRequest, "AccessDenied"));
} }
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InvalidRequest, "iam not init")); return Err(s3_error!(InvalidRequest, "iam not init"));
}; };
let conditions = crate::auth::get_condition_values(&headers, &cred, None, None, remote_addr); let conditions = crate::auth::get_condition_values(&headers, &cred, None, None, remote_addr);
@@ -438,7 +438,8 @@ pub async fn create_oidc_sts_credentials(
new_cred.groups = Some(groups.to_vec()); new_cred.groups = Some(groups.to_vec());
// Store temp user in IAM // Store temp user in IAM
let iam_store = rustfs_iam::get().map_err(|_| s3_error!(InternalError, "IAM not initialized"))?; let iam_store =
crate::app::context::resolve_ready_iam_handle().map_err(|_| s3_error!(InternalError, "IAM not initialized"))?;
let updated_at = iam_store let updated_at = iam_store
.set_temp_user(&new_cred.access_key, &new_cred, None) .set_temp_user(&new_cred.access_key, &new_cred, None)
+1 -1
View File
@@ -701,7 +701,7 @@ impl TableCredentialIssuer for IamTableCredentialIssuer {
.map_err(|err| s3_error!(InternalError, "failed to generate table credentials: {}", err))?; .map_err(|err| s3_error!(InternalError, "failed to generate table credentials: {}", err))?;
bind_table_credential_parent(&mut credential, principal); bind_table_credential_parent(&mut credential, principal);
let iam_store = rustfs_iam::get().map_err(|_| s3_error!(InternalError, "iam not init"))?; let iam_store = crate::app::context::resolve_ready_iam_handle().map_err(|_| s3_error!(InternalError, "iam not init"))?;
iam_store iam_store
.set_temp_user(&credential.access_key, &credential, None) .set_temp_user(&credential.access_key, &credential, None)
.await .await
+7 -7
View File
@@ -227,7 +227,7 @@ impl Operation for AddUser {
return Err(s3_error!(InvalidArgument, "cannot create a user with the system access key")); return Err(s3_error!(InvalidArgument, "cannot create a user with the system access key"));
} }
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam is not initialized")); return Err(s3_error!(InternalError, "iam is not initialized"));
}; };
@@ -356,7 +356,7 @@ impl Operation for SetUserStatus {
let status = AccountStatus::try_from(query.status.as_deref().unwrap_or_default()) let status = AccountStatus::try_from(query.status.as_deref().unwrap_or_default())
.map_err(|e| S3Error::with_message(S3ErrorCode::InvalidArgument, e))?; .map_err(|e| S3Error::with_message(S3ErrorCode::InvalidArgument, e))?;
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam is not initialized")); return Err(s3_error!(InternalError, "iam is not initialized"));
}; };
@@ -408,7 +408,7 @@ impl Operation for ListUsers {
} }
}; };
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam is not initialized")); return Err(s3_error!(InternalError, "iam is not initialized"));
}; };
@@ -481,7 +481,7 @@ impl Operation for RemoveUser {
return Err(s3_error!(InvalidArgument, "cannot remove the current user")); return Err(s3_error!(InvalidArgument, "cannot remove the current user"));
} }
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam is not initialized")); return Err(s3_error!(InternalError, "iam is not initialized"));
}; };
@@ -558,7 +558,7 @@ impl Operation for GetUserInfo {
return Err(s3_error!(InvalidArgument, "access key is empty")); return Err(s3_error!(InvalidArgument, "access key is empty"));
} }
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam is not initialized")); return Err(s3_error!(InternalError, "iam is not initialized"));
}; };
@@ -654,7 +654,7 @@ impl Operation for ExportIam {
) )
.await?; .await?;
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InvalidRequest, "iam not init")); return Err(s3_error!(InvalidRequest, "iam not init"));
}; };
@@ -888,7 +888,7 @@ impl Operation for ImportIam {
let mut zip_reader = let mut zip_reader =
ZipArchive::new(Cursor::new(body)).map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, e.to_string()))?; ZipArchive::new(Cursor::new(body)).map_err(|e| S3Error::with_message(S3ErrorCode::InternalError, e.to_string()))?;
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InvalidRequest, "iam not init")); return Err(s3_error!(InvalidRequest, "iam not init"));
}; };
+21 -1
View File
@@ -37,7 +37,7 @@ use super::{BucketBandwidthMonitor, DynReplicationPool, NotificationSys, Replica
use crate::config::RustFSBufferConfig; use crate::config::RustFSBufferConfig;
use rustfs_config::server_config::Config; use rustfs_config::server_config::Config;
use rustfs_credentials::Credentials; use rustfs_credentials::Credentials;
use rustfs_iam::{store::object::ObjectStore, sys::IamSys}; use rustfs_iam::{error::Error as IamError, store::object::ObjectStore, sys::IamSys};
use rustfs_io_metrics::{PerformanceMetrics, internode_metrics::InternodeMetrics}; use rustfs_io_metrics::{PerformanceMetrics, internode_metrics::InternodeMetrics};
use rustfs_kms::{KmsServiceManager, ObjectEncryptionService, init_global_kms_service_manager}; use rustfs_kms::{KmsServiceManager, ObjectEncryptionService, init_global_kms_service_manager};
use rustfs_lock::LockClient; use rustfs_lock::LockClient;
@@ -84,6 +84,11 @@ pub fn resolve_iam_handle() -> Option<Arc<IamSys<ObjectStore>>> {
resolve_iam_handle_with(get_global_app_context(), rustfs_iam::get_global_iam_sys) resolve_iam_handle_with(get_global_app_context(), rustfs_iam::get_global_iam_sys)
} }
/// Resolve a ready IAM system handle using AppContext-first precedence.
pub fn resolve_ready_iam_handle() -> rustfs_iam::error::Result<Arc<IamSys<ObjectStore>>> {
resolve_ready_iam_handle_with(get_global_app_context(), rustfs_iam::get)
}
/// Resolve bucket metadata handle using AppContext-first precedence. /// Resolve bucket metadata handle using AppContext-first precedence.
pub fn resolve_bucket_metadata_handle() -> Option<Arc<RwLock<BucketMetadataSys>>> { pub fn resolve_bucket_metadata_handle() -> Option<Arc<RwLock<BucketMetadataSys>>> {
resolve_bucket_metadata_handle_with(get_global_app_context(), || default_bucket_metadata_interface().handle()) resolve_bucket_metadata_handle_with(get_global_app_context(), || default_bucket_metadata_interface().handle())
@@ -290,6 +295,21 @@ fn resolve_iam_handle_with(
context.map(|context| context.iam().handle()).or_else(fallback) context.map(|context| context.iam().handle()).or_else(fallback)
} }
fn resolve_ready_iam_handle_with(
context: Option<Arc<AppContext>>,
fallback: impl FnOnce() -> rustfs_iam::error::Result<Arc<IamSys<ObjectStore>>>,
) -> rustfs_iam::error::Result<Arc<IamSys<ObjectStore>>> {
if let Some(context) = context {
if context.iam().is_ready() {
return Ok(context.iam().handle());
}
return Err(IamError::IamSysNotInitialized);
}
fallback()
}
fn resolve_bucket_metadata_handle_with( fn resolve_bucket_metadata_handle_with(
context: Option<Arc<AppContext>>, context: Option<Arc<AppContext>>,
fallback: impl FnOnce() -> Option<Arc<RwLock<BucketMetadataSys>>>, fallback: impl FnOnce() -> Option<Arc<RwLock<BucketMetadataSys>>>,
+2 -2
View File
@@ -186,7 +186,7 @@ impl S3Auth for IAMAuth {
return Ok(key); return Ok(key);
} }
if let Ok(iam_store) = rustfs_iam::get() { if let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() {
// Use check_key instead of get_user to ensure user is loaded from disk if not in cache // Use check_key instead of get_user to ensure user is loaded from disk if not in cache
// This is important for newly created users that may not be in cache yet. // This is important for newly created users that may not be in cache yet.
// check_key will automatically attempt to load the user from disk if not found in cache. // check_key will automatically attempt to load the user from disk if not found in cache.
@@ -341,7 +341,7 @@ pub async fn check_key_valid(session_token: &str, access_key: &str) -> S3Result<
let sys_cred = cred.clone(); let sys_cred = cred.clone();
if !constant_time_eq(&cred.access_key, access_key) { if !constant_time_eq(&cred.access_key, access_key) {
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(S3Error::with_message( return Err(S3Error::with_message(
S3ErrorCode::InternalError, S3ErrorCode::InternalError,
format!("check_key_valid {:?}", IamError::IamSysNotInitialized), format!("check_key_valid {:?}", IamError::IamSysNotInitialized),
+2 -2
View File
@@ -328,7 +328,7 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
let version_id = req_info.version_id.clone(); let version_id = req_info.version_id.clone();
if let Some(cred) = &cred { if let Some(cred) = &cred {
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(S3Error::with_message( return Err(S3Error::with_message(
S3ErrorCode::InternalError, S3ErrorCode::InternalError,
format!("authorize_request {:?}", IamError::IamSysNotInitialized), format!("authorize_request {:?}", IamError::IamSysNotInitialized),
@@ -841,7 +841,7 @@ async fn authorize_table_data_plane_if_needed(
let Some(resource) = table_data_plane_resource_for_request(bucket, object).await? else { let Some(resource) = table_data_plane_resource_for_request(bucket, object).await? else {
return Ok(()); return Ok(());
}; };
let Ok(iam_store) = rustfs_iam::get() else { let Ok(iam_store) = crate::app::context::resolve_ready_iam_handle() else {
return Err(s3_error!(InternalError, "iam not init")); return Err(s3_error!(InternalError, "iam not init"));
}; };