diff --git a/crates/keystone/src/middleware.rs b/crates/keystone/src/middleware.rs index f6c4eaefc..807dec096 100644 --- a/crates/keystone/src/middleware.rs +++ b/crates/keystone/src/middleware.rs @@ -15,14 +15,14 @@ //! Keystone authentication middleware //! //! This middleware intercepts HTTP requests and checks for OpenStack Keystone -//! authentication headers (X-Auth-Token). If found, it validates the token +//! authentication headers (X-Auth-Token or Swift X-Storage-Token). If found, it validates the token //! with Keystone and stores the authenticated credentials in task-local storage //! for use by downstream authentication handlers. //! //! ## Authentication Flow //! //! 1. Check if Keystone is enabled (via global provider) -//! 2. Extract X-Auth-Token header from request +//! 2. Extract X-Auth-Token or X-Storage-Token header from request //! 3. If token present: //! - Validate with Keystone service //! - On success: Store credentials in task-local, continue processing @@ -50,6 +50,9 @@ use tracing::{debug, info, warn}; use crate::KeystoneAuthProvider; +const HEADER_X_AUTH_TOKEN: &str = "X-Auth-Token"; +const HEADER_X_STORAGE_TOKEN: &str = "X-Storage-Token"; + // Task-local storage for Keystone credentials // This allows passing credentials from middleware to auth handlers // without modifying the request/response types @@ -140,11 +143,10 @@ where } }; - // Extract X-Auth-Token header let token = extract_keystone_token(req.headers()); if let Some(token) = token { - debug!("Keystone middleware: Found X-Auth-Token header, validating"); + debug!("Keystone middleware: Found Keystone token header, validating"); // Validate token with Keystone match keystone_auth.authenticate_with_token(token).await { @@ -191,7 +193,7 @@ where } // No Keystone token header present, pass through to normal S3 authentication - debug!("Keystone middleware: No X-Auth-Token header, passing through to S3 auth"); + debug!("Keystone middleware: No Keystone token header, passing through to S3 auth"); let resp = inner.call(req).await?; let (parts, body) = resp.into_parts(); let body: BoxBody = body.map_err(Into::into).boxed_unsync(); @@ -200,14 +202,11 @@ where } } -/// Extract Keystone token from request headers -/// -/// Checks for X-Auth-Token header (Keystone v3 standard). -/// Note: X-Storage-Token (Swift) support deferred to future PR per Q4.C fn extract_keystone_token(headers: &HeaderMap) -> Option<&str> { - headers.get("X-Auth-Token").and_then(|v| v.to_str().ok()) - // TODO: Add X-Storage-Token support in Phase 2 (Swift API) - // .or_else(|| headers.get("X-Storage-Token").and_then(|v| v.to_str().ok())) + headers + .get(HEADER_X_AUTH_TOKEN) + .and_then(|v| v.to_str().ok()) + .or_else(|| headers.get(HEADER_X_STORAGE_TOKEN).and_then(|v| v.to_str().ok())) } /// Escape XML special characters to prevent injection @@ -254,8 +253,18 @@ mod tests { let mut headers = HeaderMap::new(); assert!(extract_keystone_token(&headers).is_none()); - headers.insert("X-Auth-Token", "test-token-123".parse().unwrap()); + headers.insert(HEADER_X_AUTH_TOKEN, "test-token-123".parse().expect("auth token header should parse")); assert_eq!(extract_keystone_token(&headers), Some("test-token-123")); + + headers.clear(); + headers.insert( + HEADER_X_STORAGE_TOKEN, + "swift-token-456".parse().expect("storage token header should parse"), + ); + assert_eq!(extract_keystone_token(&headers), Some("swift-token-456")); + + headers.insert(HEADER_X_AUTH_TOKEN, "auth-token-789".parse().expect("auth token header should parse")); + assert_eq!(extract_keystone_token(&headers), Some("auth-token-789")); } #[tokio::test]