liminal-server 0.25.4

Standalone server for the liminal messaging bus
Documentation
use super::*;

// ---- ACCESS-010 R1: an exchange with no [auth] is open mode, loopback only ----

fn open_mode_refusal(field: &str, address: &str) -> String {
    format!(
        "open mode (no [auth]): {field} {address} is not loopback; an exchange with no [auth] \
         listens on loopback only, give [auth] to listen elsewhere"
    )
}

fn websocket_on(
    address: &str,
) -> Result<crate::config::types::WebSocketConfig, Box<dyn std::error::Error>> {
    Ok(crate::config::types::WebSocketConfig {
        listen_address: socket(address)?,
        path: "/ws".to_owned(),
        allowed_origins: Vec::new(),
        allowed_origin_ports: Vec::new(),
        ping_interval_ms: None,
    })
}

#[test]
fn an_open_exchange_off_loopback_is_refused_on_every_listener_by_name()
-> Result<(), Box<dyn std::error::Error>> {
    let mut config = sample_config()?;
    config.auth = None;
    config.listen_address = socket("0.0.0.0:8080")?;
    config.extra_listen_addresses = vec![socket("192.168.50.40:8082")?];
    config.websocket = Some(websocket_on("0.0.0.0:8083")?);
    let message = config_validation_message(validate(&mut config, None));
    for expected in [
        open_mode_refusal("listen_address", "0.0.0.0:8080"),
        open_mode_refusal("extra_listen_addresses[0]", "192.168.50.40:8082"),
        open_mode_refusal("websocket.listen_address", "0.0.0.0:8083"),
    ] {
        assert!(
            message.lines().any(|line| line == expected),
            "missing {expected:?} in {message}"
        );
    }
    Ok(())
}

#[test]
fn an_open_exchange_on_loopback_validates() -> Result<(), Box<dyn std::error::Error>> {
    let mut config = sample_config()?;
    config.auth = None;
    config.listen_address = socket("127.0.0.1:8080")?;
    config.extra_listen_addresses = vec![socket("[::1]:8082")?];
    config.websocket = Some(websocket_on("127.0.0.1:8083")?);
    validate(&mut config, None)?;
    assert_eq!(config.access_mode(), crate::config::types::AccessMode::Open);
    Ok(())
}

#[test]
fn a_gated_exchange_may_listen_off_loopback() -> Result<(), Box<dyn std::error::Error>> {
    let mut config = sample_config()?;
    config.auth = Some(AuthConfig {
        token: "bearer-under-test".to_owned(),
        pass: None,
        lys: None,
        require_verified_worker: false,
    });
    config.listen_address = socket("0.0.0.0:8080")?;
    let message = config_validation_message(validate(&mut config, None));
    assert!(!message.contains("open mode"), "{message}");
    assert_eq!(
        config.access_mode(),
        crate::config::types::AccessMode::Gated
    );
    Ok(())
}