use axum::Router;
use axum::body::Body;
use axum::http::{Request, StatusCode};
use axum::middleware;
use axum::routing::get;
use micromegas::servers::axum_utils::{auth_observability_middleware, observability_middleware};
use micromegas_tracing::event::in_memory_sink::InMemorySink;
use micromegas_tracing::levels::{LevelFilter, set_max_level};
use micromegas_tracing::logs::LogMsgQueueAny;
use micromegas_tracing::test_utils::init_in_memory_tracing;
use micromegas_transit::HeterogeneousQueue;
use serial_test::serial;
use tower::ServiceExt;
async fn ok_handler() -> StatusCode {
StatusCode::OK
}
fn enable_info_logging() {
set_max_level(LevelFilter::Trace);
}
fn collect_log_messages(sink: &InMemorySink) -> Vec<String> {
let state = sink.state.lock().expect("sink lock");
let mut messages = Vec::new();
for block in &state.log_blocks {
for event in block.events.iter() {
if let LogMsgQueueAny::LogStringEvent(evt) = event {
messages.push(evt.msg.0.clone());
}
}
}
messages
}
#[tokio::test]
#[serial]
async fn auth_observability_middleware_never_logs_the_query_string() {
let guard = init_in_memory_tracing();
enable_info_logging();
let app = Router::new()
.route("/auth/callback", get(ok_handler))
.layer(middleware::from_fn(auth_observability_middleware));
let request = Request::builder()
.uri("/auth/callback?code=super-secret-auth-code&state=signed-state-with-pkce-verifier")
.body(Body::empty())
.expect("request");
let response = app.oneshot(request).await.expect("response");
assert_eq!(response.status(), StatusCode::OK);
micromegas_tracing::dispatch::flush_log_buffer();
let messages = collect_log_messages(&guard.sink);
let request_line = messages
.iter()
.find(|m| m.starts_with("request "))
.expect("a request= log line was captured");
let response_line = messages
.iter()
.find(|m| m.starts_with("response "))
.expect("a response= log line was captured");
for line in [request_line, response_line] {
assert!(
line.contains("uri=/auth/callback"),
"expected path-only uri=, got: {line}"
);
assert!(
!line.contains("code="),
"query string leaked into log line: {line}"
);
assert!(
!line.contains("super-secret-auth-code"),
"auth code leaked into log line: {line}"
);
assert!(
!line.contains("state="),
"query string leaked into log line: {line}"
);
assert!(
!line.contains("signed-state-with-pkce-verifier"),
"PKCE-carrying state leaked into log line: {line}"
);
}
}
#[tokio::test]
#[serial]
async fn auth_observability_middleware_logs_path_only_route_unchanged() {
let guard = init_in_memory_tracing();
enable_info_logging();
let app = Router::new()
.route("/auth/me", get(ok_handler))
.layer(middleware::from_fn(auth_observability_middleware));
let request = Request::builder()
.uri("/auth/me")
.body(Body::empty())
.expect("request");
let response = app.oneshot(request).await.expect("response");
assert_eq!(response.status(), StatusCode::OK);
micromegas_tracing::dispatch::flush_log_buffer();
let messages = collect_log_messages(&guard.sink);
assert!(
messages
.iter()
.any(|m| m.starts_with("request ") && m.contains("uri=/auth/me"))
);
assert!(
messages
.iter()
.any(|m| m.starts_with("response ") && m.contains("uri=/auth/me"))
);
}
#[tokio::test]
#[serial]
async fn observability_middleware_logs_the_query_string() {
let guard = init_in_memory_tracing();
enable_info_logging();
let app = Router::new()
.route("/some/path", get(ok_handler))
.layer(middleware::from_fn(observability_middleware));
let request = Request::builder()
.uri("/some/path?foo=bar")
.body(Body::empty())
.expect("request");
let response = app.oneshot(request).await.expect("response");
assert_eq!(response.status(), StatusCode::OK);
micromegas_tracing::dispatch::flush_log_buffer();
let messages = collect_log_messages(&guard.sink);
let request_line = messages
.iter()
.find(|m| m.starts_with("request "))
.expect("a request= log line was captured");
let response_line = messages
.iter()
.find(|m| m.starts_with("response "))
.expect("a response= log line was captured");
for line in [request_line, response_line] {
assert!(
line.contains("uri=/some/path?foo=bar"),
"expected query string to be logged, got: {line}"
);
}
}