Phase 5: hardening — gRPC, observability, auth, config
- proto: lakehouse.proto with CatalogService, QueryService, StorageService, AiService - proto crate: tonic-build codegen from proto definitions - catalogd: gRPC CatalogService implementation - gateway: dual HTTP (:3100) + gRPC (:3101) servers - gateway: OpenTelemetry tracing with stdout exporter - gateway: API key auth middleware (toggleable) - shared: TOML config system with typed structs and defaults - lakehouse.toml config file - ADR-006 and ADR-007 documented Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
use axum::{
|
||||
extract::Request,
|
||||
http::StatusCode,
|
||||
middleware::Next,
|
||||
response::Response,
|
||||
};
|
||||
|
||||
/// API key auth middleware. Checks X-API-Key header against configured key.
|
||||
pub async fn api_key_auth(
|
||||
request: Request,
|
||||
next: Next,
|
||||
) -> Result<Response, StatusCode> {
|
||||
// Get the expected key from the request extensions (set by the layer)
|
||||
let expected_key = request.extensions().get::<ApiKey>().cloned();
|
||||
|
||||
if let Some(expected) = expected_key {
|
||||
let provided = request
|
||||
.headers()
|
||||
.get("x-api-key")
|
||||
.and_then(|v| v.to_str().ok());
|
||||
|
||||
match provided {
|
||||
Some(key) if key == expected.0 => {}
|
||||
_ => {
|
||||
tracing::warn!("unauthorized request: missing or invalid API key");
|
||||
return Err(StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(next.run(request).await)
|
||||
}
|
||||
|
||||
/// Wrapper type for the API key, stored in request extensions.
|
||||
#[derive(Clone)]
|
||||
pub struct ApiKey(pub String);
|
||||
Reference in New Issue
Block a user