chore(rustfs): remove dead keystone shadow auth path (#5988)

This commit is contained in:
Zhengchao An
2026-08-12 20:46:48 +08:00
committed by GitHub
parent 7a4a3d27c6
commit 2f83d6789b
2 changed files with 1 additions and 270 deletions
-33
View File
@@ -516,39 +516,6 @@ fn check_claims_from_token_with_context(
Ok(HashMap::new())
}
/// Check for Keystone authentication headers and authenticate if present
/// Returns Some((Credentials, is_owner)) if Keystone authentication succeeds
/// Returns None if no Keystone headers present (fall back to standard auth)
///
/// Reserved for future use (alternative Keystone auth path)
#[allow(dead_code)]
pub async fn try_keystone_auth(headers: &HeaderMap) -> S3Result<Option<(Credentials, bool)>> {
use crate::auth_keystone;
if !auth_keystone::is_keystone_enabled() {
return Ok(None);
}
match auth_keystone::authenticate_keystone(headers).await? {
Some(cred) => {
// Keystone credentials are never "owner" in the traditional sense
// unless they have admin role
let is_owner = cred
.groups
.as_ref()
.map(|groups| {
groups
.iter()
.any(|g| g.eq_ignore_ascii_case("admin") || g.eq_ignore_ascii_case("reseller_admin"))
})
.unwrap_or(false);
Ok(Some((cred, is_owner)))
}
None => Ok(None),
}
}
pub fn get_session_token<'a>(uri: &'a Uri, hds: &'a HeaderMap) -> Option<&'a str> {
let token = hds
.get("x-amz-security-token")
+1 -237
View File
@@ -14,13 +14,9 @@
//! OpenStack Keystone authentication integration for RustFS
use http::HeaderMap;
use rustfs_credentials::Credentials;
use rustfs_keystone::{KeystoneAuthProvider, KeystoneClient, KeystoneConfig, KeystoneIdentityMapper};
use rustfs_utils::MaskedAccessKey;
use s3s::{S3Result, s3_error};
use std::sync::{Arc, OnceLock};
use tracing::{error, info};
use tracing::info;
static KEYSTONE_AUTH: OnceLock<Arc<KeystoneAuthProvider>> = OnceLock::new();
static KEYSTONE_MAPPER: OnceLock<Arc<KeystoneIdentityMapper>> = OnceLock::new();
@@ -112,239 +108,7 @@ pub fn get_keystone_auth() -> Option<Arc<KeystoneAuthProvider>> {
KEYSTONE_AUTH.get().cloned()
}
/// Get Keystone identity mapper
///
/// Reserved for future use (Swift API, tenant prefixing)
#[allow(dead_code)]
pub fn get_keystone_mapper() -> Option<Arc<KeystoneIdentityMapper>> {
KEYSTONE_MAPPER.get().cloned()
}
/// Get Keystone configuration
///
/// Reserved for future use (dynamic configuration updates)
#[allow(dead_code)]
pub fn get_keystone_config() -> Option<&'static KeystoneConfig> {
KEYSTONE_CONFIG.get()
}
/// Check if Keystone is enabled
pub fn is_keystone_enabled() -> bool {
KEYSTONE_CONFIG.get().map(|c| c.enable).unwrap_or(false)
}
/// Authenticate request with Keystone
///
/// Checks for:
/// 1. X-Auth-Token header (Keystone token)
/// 2. X-Storage-Token header (Swift compatibility)
///
/// Returns Some(Credentials) if authenticated via Keystone,
/// None if Keystone is disabled or no Keystone headers present
///
/// Reserved for future use (alternative auth path, Swift API)
#[allow(dead_code)]
pub async fn authenticate_keystone(headers: &HeaderMap) -> S3Result<Option<Credentials>> {
let auth_provider = match get_keystone_auth() {
Some(provider) => provider,
None => return Ok(None), // Keystone not enabled
};
// Check for X-Auth-Token header (Keystone v3)
if let Some(token) = headers.get("X-Auth-Token").and_then(|v| v.to_str().ok()) {
return match auth_provider.authenticate_with_token(token).await {
Ok(cred) => {
info!(
component = LOG_COMPONENT_AUTH,
subsystem = LOG_SUBSYSTEM_KEYSTONE,
event = "keystone_token_auth",
token_type = "x_auth_token",
principal = %MaskedAccessKey(&cred.parent_user),
result = "success",
"Keystone token authentication completed"
);
Ok(Some(cred))
}
Err(e) => {
error!(
component = LOG_COMPONENT_AUTH,
subsystem = LOG_SUBSYSTEM_KEYSTONE,
event = "keystone_token_auth",
token_type = "x_auth_token",
result = "failed",
error = %e,
"Keystone token authentication completed"
);
Err(s3_error!(InvalidToken, "Invalid Keystone token: {}", e))
}
};
}
// Check for X-Storage-Token header (Swift compatibility)
if let Some(token) = headers.get("X-Storage-Token").and_then(|v| v.to_str().ok()) {
return match auth_provider.authenticate_with_token(token).await {
Ok(cred) => {
info!(
component = LOG_COMPONENT_AUTH,
subsystem = LOG_SUBSYSTEM_KEYSTONE,
event = "keystone_token_auth",
token_type = "x_storage_token",
principal = %MaskedAccessKey(&cred.parent_user),
result = "success",
"Keystone token authentication completed"
);
Ok(Some(cred))
}
Err(e) => {
error!(
component = LOG_COMPONENT_AUTH,
subsystem = LOG_SUBSYSTEM_KEYSTONE,
event = "keystone_token_auth",
token_type = "x_storage_token",
result = "failed",
error = %e,
"Keystone token authentication completed"
);
Err(s3_error!(InvalidToken, "Invalid Keystone token: {}", e))
}
};
}
// No Keystone headers found
Ok(None)
}
/// Apply tenant prefix to bucket name
///
/// Reserved for future use (multi-tenancy feature)
#[allow(dead_code)]
pub fn apply_tenant_prefix(bucket: &str, cred: &Credentials) -> String {
let mapper = match get_keystone_mapper() {
Some(m) => m,
None => return bucket.to_string(),
};
// Extract project_id from claims
let project_id = cred
.claims
.as_ref()
.and_then(|claims| claims.get("keystone_project_id"))
.and_then(|v| v.as_str());
mapper.apply_tenant_prefix(bucket, project_id)
}
/// Remove tenant prefix from bucket name
///
/// Reserved for future use (multi-tenancy feature)
#[allow(dead_code)]
pub fn remove_tenant_prefix(prefixed_bucket: &str, cred: &Credentials) -> String {
let mapper = match get_keystone_mapper() {
Some(m) => m,
None => return prefixed_bucket.to_string(),
};
let project_id = cred
.claims
.as_ref()
.and_then(|claims| claims.get("keystone_project_id"))
.and_then(|v| v.as_str());
mapper.remove_tenant_prefix(prefixed_bucket, project_id)
}
/// Check if bucket belongs to user's project
///
/// Reserved for future use (multi-tenancy feature)
#[allow(dead_code)]
pub fn is_user_bucket(bucket: &str, cred: &Credentials) -> bool {
let mapper = match get_keystone_mapper() {
Some(m) => m,
None => return true,
};
let project_id = cred
.claims
.as_ref()
.and_then(|claims| claims.get("keystone_project_id"))
.and_then(|v| v.as_str());
mapper.is_project_bucket(bucket, project_id)
}
/// Filter bucket list to only show user's project buckets
///
/// Reserved for future use (multi-tenancy feature)
#[allow(dead_code)]
pub fn filter_bucket_list(buckets: Vec<String>, cred: &Credentials) -> Vec<String> {
let mapper = match get_keystone_mapper() {
Some(m) => m,
None => return buckets,
};
if !mapper.is_tenant_prefix_enabled() {
return buckets;
}
let project_id = cred
.claims
.as_ref()
.and_then(|claims| claims.get("keystone_project_id"))
.and_then(|v| v.as_str());
if let Some(proj_id) = project_id {
let prefix = format!("{}:", proj_id);
buckets
.into_iter()
.filter(|b| b.starts_with(&prefix))
.map(|b| b[prefix.len()..].to_string())
.collect()
} else {
// No project ID, return unprefixed buckets only
buckets.into_iter().filter(|b| !b.contains(':')).collect()
}
}
/// Check if credential is from Keystone
///
/// Reserved for future use (credential type detection)
#[allow(dead_code)]
pub fn is_keystone_credential(cred: &Credentials) -> bool {
cred.claims
.as_ref()
.and_then(|claims| claims.get("auth_source"))
.and_then(|v| v.as_str())
.map(|s| s == "keystone")
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
use std::collections::HashMap;
fn create_test_credentials(project_id: Option<&str>) -> Credentials {
let mut claims = HashMap::new();
claims.insert("auth_source".to_string(), json!("keystone"));
if let Some(proj_id) = project_id {
claims.insert("keystone_project_id".to_string(), json!(proj_id));
}
Credentials {
access_key: "test-access".to_string(),
secret_key: "test-secret".to_string(),
claims: Some(claims),
..Default::default()
}
}
#[test]
fn test_is_keystone_credential() {
let cred = create_test_credentials(Some("proj123"));
assert!(is_keystone_credential(&cred));
let non_keystone_cred = Credentials::default();
assert!(!is_keystone_credential(&non_keystone_cred));
}
}