nautalid 0.1.0

Scratch container substrate — TLS 1.3 HTTP/2+3 kernel, LID/AetherDB, optional filter bus (GPL-3.0-or-later).
//! Optional admin bearer token for sensitive HTTP surfaces (`/metrics`).

use axum::extract::Request;
use axum::http::header;

/// When admin token is configured or policy requires it, metrics requires Bearer auth.
#[must_use]
pub fn gate_enabled() -> bool {
    crate::security::metrics_auth_required()
}

/// When admin token is configured, require **`Authorization: Bearer …`** (constant-time compare).
#[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"));
    }
}