use axum::extract::Request;
use axum::http::header;
#[must_use]
pub fn gate_enabled() -> bool {
crate::security::metrics_auth_required()
}
#[must_use]
pub fn bearer_authorized(req: &Request) -> bool {
if !gate_enabled() {
return true;
}
let Some(expected) = crate::security::admin_token() else {
return false;
};
let Some(provided) = extract_bearer(req) else {
return false;
};
constant_time_eq(expected.as_bytes(), provided.as_bytes())
}
fn extract_bearer(req: &Request) -> Option<&str> {
let auth = req.headers().get(header::AUTHORIZATION)?.to_str().ok()?;
let rest = auth.strip_prefix("Bearer ")?.trim();
(!rest.is_empty()).then_some(rest)
}
fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
let mut diff = 0u8;
for (x, y) in a.iter().zip(b.iter()) {
diff |= x ^ y;
}
diff == 0
}
#[cfg(test)]
mod tests {
use super::*;
use axum::body::Body;
#[test]
fn rejects_wrong_bearer_length() {
assert!(!constant_time_eq(b"short", b"longer"));
}
#[test]
fn open_when_gate_disabled() {
let req = Request::get("/metrics").body(Body::empty()).unwrap();
if !gate_enabled() {
assert!(bearer_authorized(&req));
}
}
#[test]
fn bearer_matches_valid_authorization_header() {
let mut req = Request::get("/metrics").body(Body::empty()).unwrap();
req.headers_mut().insert(
header::AUTHORIZATION,
"Bearer metrics-secret".parse().expect("header"),
);
assert_eq!(extract_bearer(&req), Some("metrics-secret"));
assert!(constant_time_eq(
b"metrics-secret",
extract_bearer(&req).unwrap().as_bytes()
));
assert!(!constant_time_eq(b"metrics-secret", b"wrong-secret"));
}
}