Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/holger

server/lib/tests/the_console_is_seen_per_server.rs

//! ★ **A console is seen by the server it called, and only from its own proof**
//! (HOL-SYNC-SKEW, the review of HOL-FLOW-SMART).
//!
//! The flow draws a console box only when this installation has a console: one that called
//! this server lately. Two servers in one process each keep their own record, so a console
//! of the one never draws on the other. And only holger-ui's own calls count: the console
//! proves itself with its own bearer in `x-holger-proxy-authorization`; an administrator's
//! plain call (a CLI, a script) is no console.

use std::io::Write as _;

use holger_server_lib::{read_ron_config, wire_holger, Holger};
use traits::test_support::hold_port;

const CONSOLE: &str = "c0nsole-bearer-0123456789abcdefghij";
const CONSOLE_NODE: &str = "estate:console";

struct Running {
    _data: tempfile::TempDir,
    _cfg: tempfile::NamedTempFile,
    _holger: Holger,
    base: String,
}

fn start() -> Running {
    let mut last = String::new();
    for _ in 0..8 {
        match try_start(hold_port(), hold_port()) {
            Ok(r) => return r,
            Err(e) => last = e,
        }
    }
    panic!("server never started; last error: {last}");
}

fn try_start(grpc: u16, http: u16) -> Result<Running, String> {
    let data = tempfile::tempdir().unwrap();
    let d = data.path().display().to_string();
    std::fs::create_dir_all(data.path().join("audit")).unwrap();
    let ron = format!(
        r#"(
    repositories: [
        (ron_name: "crates", ron_repo_type: "raw", ron_upstreams: [], ron_in: None,
         ron_out: Some((ron_storage_endpoint: "storage", ron_exposed_endpoint: "door")),
         ron_data_dir: Some("{d}/crates")),
    ],
    exposed_endpoints: [
        (ron_name: "door", ron_url: "127.0.0.1:{grpc}", ron_http_url: Some("127.0.0.1:{http}")),
    ],
    storage_endpoints: [ (ron_name: "storage", ron_storage_type: "znippy", ron_path: "{d}") ],
    audit: (dir: "{d}/audit", enabled: true),
    auth: Some((
        methods: [ StaticToken(subject: "console", token: Some("{CONSOLE}")) ],
        roles: {{"console": Admin}},
    )),
)"#
    );
    let mut cfg = tempfile::NamedTempFile::new().unwrap();
    cfg.write_all(ron.as_bytes()).unwrap();
    let mut holger: Holger = read_ron_config(cfg.path()).map_err(|e| format!("config: {e}"))?;
    holger
        .instantiate_backends()
        .map_err(|e| format!("backends: {e}"))?;
    wire_holger(&mut holger).map_err(|e| format!("wire: {e}"))?;
    holger.start().map_err(|e| format!("start: {e}"))?;
    Ok(Running {
        _data: data,
        _cfg: cfg,
        _holger: holger,
        base: format!("http://127.0.0.1:{http}"),
    })
}

async fn wait_healthy(c: &reqwest::Client, base: &str) {
    for _ in 0..200 {
        if let Ok(r) = c.get(format!("{base}/healthz")).send().await {
            if r.status() == 200 {
                return;
            }
        }
        tokio::time::sleep(std::time::Duration::from_millis(20)).await;
    }
    panic!("gateway never became healthy at {base}");
}

/// Does this server's flow (level 1) draw a console?
async fn draws_a_console(c: &reqwest::Client, base: &str) -> bool {
    let r = c
        .get(format!("{base}/-/flow.json"))
        .bearer_auth(CONSOLE)
        .send()
        .await
        .expect("the flow door answers");
    assert_eq!(r.status(), 200, "an administrator reads the flow");
    let v: serde_json::Value = r.json().await.expect("the flow is JSON");
    v["nodes"]
        .as_array()
        .expect("nodes")
        .iter()
        .any(|n| n["id"] == CONSOLE_NODE)
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn a_console_is_seen_by_the_server_it_called_and_only_from_its_own_proof() {
    let (a, b) = (start(), start());
    let c = reqwest::Client::new();
    wait_healthy(&c, &a.base).await;
    wait_healthy(&c, &b.base).await;
    assert!(
        !draws_a_console(&c, &a.base).await,
        "no console has called a"
    );

    // An administrator's own call is no console's (a CLI, a script).
    let r = c
        .get(format!("{}/-/settings/console.a-switch", b.base))
        .bearer_auth(CONSOLE)
        .send()
        .await
        .unwrap();
    assert!(r.status().as_u16() < 500, "{}", r.status());
    assert!(
        !draws_a_console(&c, &b.base).await,
        "a plain administrator's call draws no console"
    );

    // holger-ui reads its switch on a with its own proof.
    let r = c
        .get(format!("{}/-/settings/console.a-switch", a.base))
        .header("x-holger-proxy-authorization", format!("Bearer {CONSOLE}"))
        .send()
        .await
        .unwrap();
    assert!(r.status().as_u16() < 500, "{}", r.status());
    assert!(
        draws_a_console(&c, &a.base).await,
        "the console that called a is drawn on a"
    );
    assert!(
        !draws_a_console(&c, &b.base).await,
        "and never on b, the other server in this process"
    );
}