~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/atlibs

0
0
main
73 lines · 3.9 KB Raw History
// Composition toys (javascript/src/compose).
// caller: calls `nsid` with `input` plus a `progress` callback (through `proxy` when given), answers each callback,
// returns the outcome.
// lookup: doubles `n`, calling the `progress` capability it was given first when there is one.
// even: a policy, allows calls whose input `n` is even.
// spy: a policy that tries to invoke the `host` capability in the call it checks; it allows the call
// only when that is refused (a policy run must not hold the caller's capabilities).
use abi::{Call, Event, Handle, Run, cap, cap_token, entries};
use serde_json::{Value, json};

fn caller_handler(run: Run<Value>) -> Result<Handle, String> {
    let mut state = if run.state.is_null() { json!({ "progress": [] }) } else { run.state };
    Ok(match run.event {
        Event::Start => {
            let nsid = run.args["nsid"].as_str().ok_or("nsid is required")?;
            let mut input = run.args.get("input").cloned().unwrap_or_else(|| json!({}));
            input["progress"] = cap("progress");
            let call = Call::new("q", nsid).input(input);
            let call = match run.args["proxy"].as_str() {
                Some(proxy) => call.proxy(proxy),
                None => call,
            };
            Handle::new().call(call).state(state)
        }
        Event::Callback { cap, input, reply, .. } if cap == "progress" => {
            let list = state["progress"].as_array_mut().ok_or("bad state")?;
            list.push(input.clone());
            let id = format!("reply-{}", list.len());
            Handle::new().call(Call::new(id, reply).input(json!({ "seen": input }))).state(state)
        }
        Event::Result { id, result, .. } if id == "q" => {
            let outcome = match result.output() {
                Ok(output) => json!({ "ok": true, "output": output }),
                Err(error) => json!({ "ok": false, "error": error }),
            };
            Handle::new().result(json!({ "q": outcome, "progress": state["progress"] }))
        }
        _ => Handle::new(),
    })
}

fn lookup_handler(run: Run<Value>) -> Result<Handle, String> {
    let n = run.args["n"].as_i64().ok_or("n is required")?;
    let answer = |ack: Value| Handle::new().result(json!({ "doubled": n * 2, "ack": ack, "caller": run.args["caller"] }));
    Ok(match (run.event, cap_token(&run.args["progress"])) {
        (Event::Start, Some(token)) => Handle::new().call(Call::new("p", token).input(json!({ "step": 1 }))),
        (Event::Start, None) => answer(Value::Null),
        (Event::Result { id, result, .. }, _) if id == "p" => answer(result.output().cloned().unwrap_or_else(|e| json!({ "error": e }))),
        _ => Handle::new(),
    })
}

fn even_handler(run: Run<Value>) -> Result<Handle, String> {
    let n = run.args["call"]["input"]["n"].as_i64();
    let allow = n.is_some_and(|n| n % 2 == 0);
    let violations = if allow { json!([]) } else { json!([{ "msg": format!("n {} is not even", n.map_or("missing".into(), |n| n.to_string())), "policyId": "even" }]) };
    Ok(Handle::new().result(json!({ "allow": allow, "violations": violations })).done())
}

fn spy_handler(run: Run<Value>) -> Result<Handle, String> {
    let verdict = |allow: bool, msg: String| Handle::new().result(json!({ "allow": allow, "violations": [{ "msg": msg, "policyId": "spy" }] })).done();
    Ok(match (run.event, cap_token(&run.args["call"]["input"]["host"])) {
        (Event::Start, Some(token)) => Handle::new().call(Call::new("h", token).input(json!({ "from": "policy" }))),
        (Event::Start, None) => verdict(true, "no host capability".into()),
        (Event::Result { id, result, .. }, _) if id == "h" => match result.output() {
            Ok(_) => verdict(false, "the policy invoked the caller's capability".into()),
            Err(error) => verdict(true, error),
        },
        _ => Handle::new(),
    })
}

entries! { caller => caller_handler, lookup => lookup_handler, even => even_handler, spy => spy_handler }