Skip to main content

isb_server/server/mcp/
origin.rs

1//! Where a request came from, and the answer to one that came from nobody.
2
3use super::{Caller, Peer, Request, Response, rest_error};
4
5/// The 401 for a caller who sent no credential where anonymous access is
6/// off. `WWW-Authenticate` tells an MCP client to ask for a token.
7pub(super) fn sign_in_required() -> Response {
8    rest_error(
9        401,
10        "unauthorized",
11        "sign in: send an API token as Authorization: Bearer (isb token create)",
12    )
13    .header("WWW-Authenticate", "Bearer")
14}
15
16/// Where a request came from, for the audit log: the surface (`cli` over
17/// the unix socket, `mcp`, `web` for a browser session, else `rest`), the
18/// client's address and agent, and a request id (`X-Request-Id` when it is
19/// sane, else `Cf-Ray`, else a fresh one).
20pub fn origin(req: &Request, caller: &Caller, mcp: bool) -> crate::audit::Origin {
21    let surface = match (&req.peer, mcp, caller) {
22        (Peer::Unix { .. }, _, _) => "cli",
23        (_, true, _) => "mcp",
24        (_, false, Caller::User { principal })
25            if matches!(principal.kind, crate::auth::PrincipalKind::Session { .. }) =>
26        {
27            "web"
28        }
29        _ => "rest",
30    };
31    let sane = |s: &&str| {
32        !s.is_empty()
33            && s.len() <= 64
34            && s.bytes()
35                .all(|b| b.is_ascii_alphanumeric() || matches!(b, b'-' | b'_' | b'.' | b':'))
36    };
37    let request_id = req
38        .header("x-request-id")
39        .filter(sane)
40        .or_else(|| req.header("cf-ray").filter(sane))
41        .map(String::from)
42        .unwrap_or_else(new_request_id);
43    crate::audit::Origin {
44        surface: surface.into(),
45        ip: crate::auth::http::client_ip(req),
46        user_agent: req.header("user-agent").map(String::from),
47        request_id: Some(request_id),
48    }
49}
50
51fn new_request_id() -> String {
52    use ring::rand::SecureRandom;
53    let mut b = [0u8; 8];
54    let _ = ring::rand::SystemRandom::new().fill(&mut b);
55    b.iter().map(|x| format!("{x:02x}")).collect()
56}