liminal-server 0.23.1

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

/// This liminal's own end of a crossing, as a table (ACCESS-002 R1).
#[derive(Debug)]
struct HostRights {
    import: &'static [&'static str],
}

impl LinkRights for HostRights {
    fn holds(
        &self,
        action: LinkAction,
        subject: &SubjectBinding,
        _now: u64,
    ) -> Result<GrantReceipt, AccessDecisionError> {
        let reached =
            action == LinkAction::Import && self.import.contains(&subject.resource().id.as_str());
        if reached {
            return Ok(GrantReceipt {
                grant: format!("host-import-{}", subject.resource().id),
            });
        }
        Err(AccessDecisionError {
            cause: RefusalCause::MissingGrant,
            refusal: AccessRefusal::new(
                subject.resource().clone(),
                action.access_action(),
                "https://lys.example/ask/host".to_owned(),
            ),
        })
    }
}

const LINK_STREAM: u32 = 4;

fn machine(seed: u8) -> LocalMachine {
    LocalMachine::new(Ed25519Identity::from_seed(&Zeroizing::new([seed; 32])))
}

/// The remote machine's offer to export over `link_id`, with its first
/// handshake message; the initiator is returned to finish the handshake.
fn export_offer(
    remote: &LocalMachine,
    host: &LocalMachine,
    link_id: &str,
) -> Result<(LinkHandshake, LinkOffer), Failure> {
    let mut initiator = LinkHandshake::initiator(remote, host.machine())?;
    let mut first = vec![0_u8; NOISE_OVERHEAD];
    let written = initiator.write(&mut first)?;
    first.truncate(written);
    Ok((
        initiator,
        LinkOffer {
            link_id: link_id.to_owned(),
            end: LinkEnd::Export,
            machine: *remote.machine().ed25519_public(),
            handshake: first,
        },
    ))
}

fn link_open(
    runtime: &ConnectionRuntime,
    state: &mut ConnectionProcessState,
    offer: LinkOffer,
) -> Result<LinkOpenOutcome, Failure> {
    match apply_frame(
        PID,
        runtime,
        state,
        Frame::LinkOpen {
            flags: 0,
            stream_id: LINK_STREAM,
            offer,
        },
    ) {
        FrameAction::Respond(Frame::LinkOpenAck {
            stream_id: LINK_STREAM,
            outcome,
            ..
        }) => Ok(outcome),
        other => Err(format!("the open was not acknowledged on its stream: {other:?}").into()),
    }
}

fn link_message(
    runtime: &ConnectionRuntime,
    state: &mut ConnectionProcessState,
    subject: &str,
    sealed: Vec<u8>,
) -> FrameAction {
    apply_frame(
        PID,
        runtime,
        state,
        Frame::LinkMessage {
            flags: 0,
            stream_id: LINK_STREAM,
            subject: subject.to_owned(),
            sealed,
        },
    )
}

/// The refusal a crossing answered under the link's own reason code.
fn crossing_refusal(action: FrameAction) -> Result<String, Failure> {
    match action {
        FrameAction::Respond(Frame::PublishError {
            stream_id: LINK_STREAM,
            reason_code: crate::server::connection::link_route::LINK_CROSSING_REFUSED_CODE,
            message: Some(message),
            ..
        }) => Ok(message),
        other => Err(format!("the crossing was not refused by name: {other:?}").into()),
    }
}

/// ACCESS-002 R1 on the wire: without `[auth.lys.machine]` no link opens
/// here, by name; with it, an offer is refused before Lys is asked when the
/// offered key is not the holder's recorded key, refused by Lys's own answer
/// when the end's grant is missing, and keyed when Lys allows it outright.
#[test]
fn a_link_opens_on_the_holders_recorded_key_when_lys_allows_the_end() -> Result<(), Failure> {
    let grants = Arc::new(Grants::default());
    let door = Arc::new(Door::default());
    let (runtime, mut state) = admitted(&grants, &door)?;
    let remote = machine(1);
    let host = machine(2);
    let (_, offer) = export_offer(&remote, &host, "link-7")?;
    let LinkOpenOutcome::Refused { reason } = link_open(&runtime, &mut state, offer.clone())?
    else {
        return Err("a liminal without a machine identity opened a link".into());
    };
    assert!(reason.contains("[auth.lys.machine]"), "{reason}");

    runtime
        .install_link_host(Arc::new(LinkHost::new(
            machine(2),
            Box::new(HostRights {
                import: &["link-7"],
            }),
            [("fixture-holder".to_owned(), remote.machine())].into(),
        )))
        .map_err(|_| "a link host was already installed")?;
    // A key that is not the recorded one is refused before Lys is asked.
    let (_, stranger) = export_offer(&machine(3), &host, "link-7")?;
    let LinkOpenOutcome::Refused { reason } = link_open(&runtime, &mut state, stranger)? else {
        return Err("a stranger's key opened a link".into());
    };
    assert!(reason.contains("recorded key"), "{reason}");
    // The recorded key, but Lys refuses the end: the four-field refusal.
    let LinkOpenOutcome::Refused { reason } = link_open(&runtime, &mut state, offer)? else {
        return Err("an end Lys refused opened a link".into());
    };
    assert!(reason.contains("\"link_export\""), "{reason}");
    assert!(reason.contains("liminal.link:link-7"), "{reason}");
    // Lys allows the end outright: the handshake completes on both sides.
    grants.allow_open("link-7", AccessAction::LinkExport)?;
    let (mut initiator, offer) = export_offer(&remote, &host, "link-7")?;
    let LinkOpenOutcome::Opened { handshake } = link_open(&runtime, &mut state, offer)? else {
        return Err("an allowed end did not open".into());
    };
    initiator.read(&handshake)?;
    assert!(initiator.is_finished());
    assert!(state.links.contains_key(LINK_STREAM)?);
    Ok(())
}

/// A message crosses only when the exporting machine's pass holds
/// `link_export` and this machine's holds `link_import` on the link; a
/// frame the link's transport did not seal is refused before any decision.
#[test]
fn a_crossing_is_decided_on_both_passes_before_it_is_published() -> Result<(), Failure> {
    let grants = Arc::new(Grants::default());
    let door = Arc::new(Door::default());
    let (runtime, mut state) = admitted(&grants, &door)?;
    let remote = machine(1);
    runtime
        .install_link_host(Arc::new(LinkHost::new(
            machine(2),
            Box::new(HostRights {
                import: &["link-7"],
            }),
            [("fixture-holder".to_owned(), remote.machine())].into(),
        )))
        .map_err(|_| "a link host was already installed")?;

    // No stream holds a link yet: refused by name.
    let refused = crossing_refusal(link_message(&runtime, &mut state, "orders", vec![1, 2, 3]))?;
    assert!(refused.contains("no link is open on stream"), "{refused}");

    grants.allow_open("link-7", AccessAction::LinkExport)?;
    let (mut initiator, offer) = export_offer(&remote, &machine(2), "link-7")?;
    let LinkOpenOutcome::Opened { handshake } = link_open(&runtime, &mut state, offer)? else {
        return Err("the link did not open".into());
    };
    initiator.read(&handshake)?;
    let mut transport = initiator.finish()?;
    let envelope = MessageEnvelope::new(
        SchemaId::new([1; SchemaId::WIRE_LEN]),
        CausalContext::independent(),
        b"crossing".to_vec(),
    );
    let mut plain = 1_u64.to_be_bytes().to_vec();
    plain.extend(envelope.to_wire_bytes()?);
    let seal = |transport: &mut crate::link_handshake::LinkTransport| -> Result<Vec<u8>, Failure> {
        let mut sealed = vec![0_u8; plain.len() + NOISE_OVERHEAD];
        let written = transport.seal(&plain, &mut sealed)?;
        sealed.truncate(written);
        Ok(sealed)
    };

    // The exporting machine holds no link_export on the link: refused at export.
    let refused = crossing_refusal(link_message(
        &runtime,
        &mut state,
        "orders",
        seal(&mut transport)?,
    ))?;
    assert!(
        refused.starts_with("crossing refused at export:"),
        "{refused}"
    );
    assert!(refused.contains("\"link_export\""), "{refused}");

    // A separately opened link has export permission and lacks import permission.
    grants.grant_link("link-7", LinkAction::Export)?;
    grants.grant_link("invoices-link", LinkAction::Export)?;
    grants.allow_open("invoices-link", AccessAction::LinkExport)?;
    let (invoice_runtime, mut invoice_state) = admitted(&grants, &door)?;
    invoice_runtime
        .install_link_host(Arc::new(LinkHost::new(
            machine(2),
            Box::new(HostRights {
                import: &["link-7"],
            }),
            [("fixture-holder".to_owned(), remote.machine())].into(),
        )))
        .map_err(|_| "invoice host already installed")?;
    let (mut invoice_initiator, invoice_offer) =
        export_offer(&remote, &machine(2), "invoices-link")?;
    let LinkOpenOutcome::Opened { handshake } =
        link_open(&invoice_runtime, &mut invoice_state, invoice_offer)?
    else {
        return Err("invoice link did not open".into());
    };
    invoice_initiator.read(&handshake)?;
    let mut invoice_transport = invoice_initiator.finish()?;
    let refused = crossing_refusal(link_message(
        &invoice_runtime,
        &mut invoice_state,
        "invoices",
        seal(&mut invoice_transport)?,
    ))?;
    assert!(
        refused.starts_with("crossing refused at import:"),
        "{refused}"
    );

    // Both ends hold the opened link: the crossing reaches the door's publish.
    let crossed = link_message(&runtime, &mut state, "orders", seal(&mut transport)?);
    match crossed {
        FrameAction::Respond(Frame::PublishError {
            reason_code,
            message: Some(message),
            ..
        }) => {
            assert_ne!(
                reason_code,
                crate::server::connection::link_route::LINK_CROSSING_REFUSED_CODE
            );
            assert!(
                message.contains("publish is not a route under test"),
                "{message}"
            );
        }
        other => return Err(format!("the crossing did not reach publish: {other:?}").into()),
    }

    // Bytes the transport did not seal are refused before any decision.
    let refused = crossing_refusal(link_message(&runtime, &mut state, "orders", vec![0; 40]))?;
    assert!(refused.starts_with("link frame refused:"), "{refused}");
    Ok(())
}