mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-27 08:38:58 +00:00
feat(admin): add MinIO-compatible IAM and IDP endpoints (#4334)
feat(admin): add MinIO-compatible IAM/IDP admin endpoints
Register and implement MinIO admin API compatibility for IAM/IDP:
- PUT /v3/import-iam-v2 and POST /v3/revoke-tokens/{userProvider}
- generic /v3/idp-config/{type}[/{name}] CRUD mapped onto existing config
- LDAP/OpenID service-account, policy-entities, and list-access-keys flows
Adds IamSys::delete_temp_account primitive to back STS token revocation.
revoke-tokens requires the broader ListUsers admin capability for
cross-user revocation (self-revocation only needs RemoveServiceAccount),
mirroring the cross-user guard used by the service-account handlers.
Registers admin route-policy inventory entries for every new route.
Unsupported LDAP/OpenID backends return honest compatibility errors.
Refs rustfs/backlog#609 #610 #616
This commit is contained in:
@@ -330,6 +330,27 @@ impl<T: Store> IamSys<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete a single temporary/STS account by its access key.
|
||||
///
|
||||
/// Unlike [`Self::delete_user`], which operates on regular (`UserType::Reg`)
|
||||
/// identities, this removes an STS credential (`UserType::Sts`) from both the
|
||||
/// in-memory cache and the backing store, immediately invalidating the
|
||||
/// associated session token. This is the primitive used by the admin
|
||||
/// `revoke-tokens` endpoint to revoke STS credentials for a parent user.
|
||||
pub async fn delete_temp_account(&self, access_key: &str, notify: bool) -> Result<()> {
|
||||
self.store.delete_user(access_key, UserType::Sts).await?;
|
||||
|
||||
if notify && !self.has_watcher() {
|
||||
for r in notify_iam_delete_user(access_key).await {
|
||||
if let Some(err) = r.err {
|
||||
warn!("notify delete_temp_account failed: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn notify_for_user(&self, name: &str, is_temp: bool) {
|
||||
if self.has_watcher() {
|
||||
return;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,7 @@ pub mod group;
|
||||
pub mod heal;
|
||||
pub mod health;
|
||||
pub(crate) mod iam_error;
|
||||
pub mod idp_compat;
|
||||
pub mod is_admin;
|
||||
pub mod kms;
|
||||
pub mod kms_dynamic;
|
||||
|
||||
@@ -937,7 +937,10 @@ async fn handle_builtin_policy_entities(req: S3Request<Body>) -> S3Result<S3Resp
|
||||
Ok(S3Response::with_headers((StatusCode::OK, Body::from(body)), header))
|
||||
}
|
||||
|
||||
async fn handle_builtin_policy_association(req: S3Request<Body>, is_attach: bool) -> S3Result<S3Response<(StatusCode, Body)>> {
|
||||
pub(crate) async fn handle_builtin_policy_association(
|
||||
req: S3Request<Body>,
|
||||
is_attach: bool,
|
||||
) -> S3Result<S3Response<(StatusCode, Body)>> {
|
||||
let Some(input_cred) = req.credentials else {
|
||||
return Err(s3_error!(InvalidRequest, "get cred failed"));
|
||||
};
|
||||
|
||||
@@ -33,9 +33,9 @@ mod console_test;
|
||||
mod route_registration_test;
|
||||
|
||||
use handlers::{
|
||||
audit, batch_job, bucket_meta, cluster_snapshot, config_admin, diagnostics, extensions, heal, health, kms, module_switch,
|
||||
object_zip_download, oidc, plugins_catalog, plugins_instances, pools, profile_admin, quota as quota_handler, rebalance,
|
||||
replication as replication_handler, scanner, site_replication, sts, system, table_catalog, tier, tls_debug, user,
|
||||
audit, batch_job, bucket_meta, cluster_snapshot, config_admin, diagnostics, extensions, heal, health, idp_compat, kms,
|
||||
module_switch, object_zip_download, oidc, plugins_catalog, plugins_instances, pools, profile_admin, quota as quota_handler,
|
||||
rebalance, replication as replication_handler, scanner, site_replication, sts, system, table_catalog, tier, tls_debug, user,
|
||||
};
|
||||
use router::{AdminOperation, S3Router};
|
||||
use s3s::route::S3Route;
|
||||
@@ -86,6 +86,7 @@ fn register_admin_routes(r: &mut S3Router<AdminOperation>) -> std::io::Result<()
|
||||
tls_debug::register_tls_debug_route(r)?;
|
||||
kms::register_kms_route(r)?;
|
||||
oidc::register_oidc_route(r)?;
|
||||
idp_compat::register_idp_compat_route(r)?;
|
||||
table_catalog::register_table_catalog_route(r)?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -68,6 +68,7 @@ const LIST_USERS: AdminActionRef = AdminActionRef::new("ListUsersAdminAction");
|
||||
const PROFILING: AdminActionRef = AdminActionRef::new("ProfilingAdminAction");
|
||||
const REBALANCE: AdminActionRef = AdminActionRef::new("RebalanceAdminAction");
|
||||
const REGISTER_TABLE: AdminActionRef = AdminActionRef::new("RegisterTableAction");
|
||||
const REMOVE_SERVICE_ACCOUNT: AdminActionRef = AdminActionRef::new("RemoveServiceAccountAdminAction");
|
||||
const REMOVE_USER_FROM_GROUP: AdminActionRef = AdminActionRef::new("RemoveUserFromGroupAdminAction");
|
||||
const RUN_TABLE_MAINTENANCE: AdminActionRef = AdminActionRef::new("RunTableMaintenanceAction");
|
||||
const SERVER_INFO: AdminActionRef = AdminActionRef::new("ServerInfoAdminAction");
|
||||
@@ -176,6 +177,55 @@ pub const ADMIN_ROUTE_POLICY_SPECS: &[AdminRouteSpec] = &[
|
||||
),
|
||||
admin(HttpMethod::Get, "/rustfs/admin/v3/export-iam", EXPORT_IAM, RouteRiskLevel::High),
|
||||
admin(HttpMethod::Put, "/rustfs/admin/v3/import-iam", IMPORT_IAM, RouteRiskLevel::High),
|
||||
admin(HttpMethod::Put, "/rustfs/admin/v3/import-iam-v2", IMPORT_IAM, RouteRiskLevel::High),
|
||||
admin(
|
||||
HttpMethod::Post,
|
||||
"/rustfs/admin/v3/revoke-tokens/{user_provider}",
|
||||
REMOVE_SERVICE_ACCOUNT,
|
||||
RouteRiskLevel::High,
|
||||
),
|
||||
admin(
|
||||
HttpMethod::Get,
|
||||
"/rustfs/admin/v3/idp-config/{idp_type}",
|
||||
SERVER_INFO,
|
||||
RouteRiskLevel::Sensitive,
|
||||
),
|
||||
admin(
|
||||
HttpMethod::Get,
|
||||
"/rustfs/admin/v3/idp-config/{idp_type}/{name}",
|
||||
SERVER_INFO,
|
||||
RouteRiskLevel::Sensitive,
|
||||
),
|
||||
admin(
|
||||
HttpMethod::Put,
|
||||
"/rustfs/admin/v3/idp-config/{idp_type}/{name}",
|
||||
CONFIG_UPDATE,
|
||||
RouteRiskLevel::High,
|
||||
),
|
||||
admin(
|
||||
HttpMethod::Post,
|
||||
"/rustfs/admin/v3/idp-config/{idp_type}/{name}",
|
||||
CONFIG_UPDATE,
|
||||
RouteRiskLevel::High,
|
||||
),
|
||||
admin(
|
||||
HttpMethod::Delete,
|
||||
"/rustfs/admin/v3/idp-config/{idp_type}/{name}",
|
||||
CONFIG_UPDATE,
|
||||
RouteRiskLevel::High,
|
||||
),
|
||||
admin(
|
||||
HttpMethod::Put,
|
||||
"/rustfs/admin/v3/idp/ldap/add-service-account",
|
||||
CREATE_SERVICE_ACCOUNT,
|
||||
RouteRiskLevel::High,
|
||||
),
|
||||
admin(
|
||||
HttpMethod::Post,
|
||||
"/rustfs/admin/v3/idp/ldap/policy/{operation}",
|
||||
ATTACH_POLICY,
|
||||
RouteRiskLevel::High,
|
||||
),
|
||||
admin(
|
||||
HttpMethod::Get,
|
||||
"/rustfs/admin/v3/list-canned-policies",
|
||||
@@ -1297,6 +1347,26 @@ pub const DEFERRED_ADMIN_ROUTE_POLICIES: &[DeferredAdminRoutePolicy] = &[
|
||||
"/rustfs/admin/v3/idp/builtin/policy-entities",
|
||||
DeferredRoutePolicyReason::MultipleActions,
|
||||
),
|
||||
deferred(
|
||||
HttpMethod::Get,
|
||||
"/rustfs/admin/v3/idp/ldap/policy-entities",
|
||||
DeferredRoutePolicyReason::MultipleActions,
|
||||
),
|
||||
deferred(
|
||||
HttpMethod::Get,
|
||||
"/rustfs/admin/v3/idp/ldap/list-access-keys",
|
||||
DeferredRoutePolicyReason::ContextualAuthorization,
|
||||
),
|
||||
deferred(
|
||||
HttpMethod::Get,
|
||||
"/rustfs/admin/v3/idp/ldap/list-access-keys-bulk",
|
||||
DeferredRoutePolicyReason::MultipleActions,
|
||||
),
|
||||
deferred(
|
||||
HttpMethod::Get,
|
||||
"/rustfs/admin/v3/idp/openid/list-access-keys-bulk",
|
||||
DeferredRoutePolicyReason::MultipleActions,
|
||||
),
|
||||
deferred(HttpMethod::Post, "/rustfs/admin/v3/service", DeferredRoutePolicyReason::NotImplemented),
|
||||
deferred(
|
||||
HttpMethod::Get,
|
||||
|
||||
@@ -318,6 +318,20 @@ fn expected_admin_route_matrix() -> Vec<RouteMatrixEntry> {
|
||||
admin_route_sample(Method::PUT, "/v3/oidc/config/{provider_id}", "/v3/oidc/config/default"),
|
||||
admin_route_sample(Method::DELETE, "/v3/oidc/config/{provider_id}", "/v3/oidc/config/default"),
|
||||
admin_route(Method::POST, "/v3/oidc/validate"),
|
||||
// idp_compat (rustfs/backlog#609 #610 #616)
|
||||
admin_route(Method::PUT, "/v3/import-iam-v2"),
|
||||
admin_route_sample(Method::POST, "/v3/revoke-tokens/{user_provider}", "/v3/revoke-tokens/builtin"),
|
||||
admin_route_sample(Method::GET, "/v3/idp-config/{idp_type}", "/v3/idp-config/openid"),
|
||||
admin_route_sample(Method::GET, "/v3/idp-config/{idp_type}/{name}", "/v3/idp-config/openid/default"),
|
||||
admin_route_sample(Method::PUT, "/v3/idp-config/{idp_type}/{name}", "/v3/idp-config/openid/default"),
|
||||
admin_route_sample(Method::POST, "/v3/idp-config/{idp_type}/{name}", "/v3/idp-config/openid/default"),
|
||||
admin_route_sample(Method::DELETE, "/v3/idp-config/{idp_type}/{name}", "/v3/idp-config/openid/default"),
|
||||
admin_route(Method::PUT, "/v3/idp/ldap/add-service-account"),
|
||||
admin_route(Method::GET, "/v3/idp/ldap/list-access-keys"),
|
||||
admin_route(Method::GET, "/v3/idp/ldap/list-access-keys-bulk"),
|
||||
admin_route(Method::GET, "/v3/idp/ldap/policy-entities"),
|
||||
admin_route_sample(Method::POST, "/v3/idp/ldap/policy/{operation}", "/v3/idp/ldap/policy/attach"),
|
||||
admin_route(Method::GET, "/v3/idp/openid/list-access-keys-bulk"),
|
||||
table_route(Method::GET, "/config"),
|
||||
table_route_sample(Method::PUT, "/buckets/{warehouse}", "/buckets/analytics"),
|
||||
table_route_sample(Method::GET, "/buckets/{warehouse}", "/buckets/analytics"),
|
||||
|
||||
Reference in New Issue
Block a user