Skip to main content

byteflow/scheduler/
link_admin.rs

1//! LINK / MONITOR / ADMIN as real capabilities, not ambient authority.
2//!
3//! Before: LINK/MONITOR took a bare FlowId (or a Cap resolved with empty
4//! rights) and succeeded if the target existed — knowing the id was enough.
5//! Now: the Cap must target that exact flow and carry the matching right.
6
7use crate::bytecode::{Cap, CapTarget, CapRights, RevocationCell};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum LinkError {
11    WrongTarget,
12    MissingRight,
13    CapRevoked,
14}
15
16impl std::fmt::Display for LinkError {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        match self {
19            LinkError::WrongTarget => f.write_str("cap does not target this flow"),
20            LinkError::MissingRight => f.write_str("cap lacks LINK/MONITOR right"),
21            LinkError::CapRevoked => f.write_str("capability revoked"),
22        }
23    }
24}
25
26impl std::error::Error for LinkError {}
27
28pub fn check_link(cap: &Cap, cell: &RevocationCell, target: u64) -> Result<(), LinkError> {
29    if !cap.is_valid(cell) {
30        return Err(LinkError::CapRevoked);
31    }
32    if cap.target != CapTarget::Flow(target) {
33        return Err(LinkError::WrongTarget);
34    }
35    if !cap.rights.contains(CapRights::LINK) {
36        return Err(LinkError::MissingRight);
37    }
38    Ok(())
39}
40
41pub fn check_monitor(cap: &Cap, cell: &RevocationCell, target: u64) -> Result<(), LinkError> {
42    if !cap.is_valid(cell) {
43        return Err(LinkError::CapRevoked);
44    }
45    if cap.target != CapTarget::Flow(target) {
46        return Err(LinkError::WrongTarget);
47    }
48    if !cap.rights.contains(CapRights::MONITOR) {
49        return Err(LinkError::MissingRight);
50    }
51    Ok(())
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum AdminError {
56    MissingRight,
57    CapRevoked,
58    WrongTarget,
59}
60
61impl std::fmt::Display for AdminError {
62    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63        match self {
64            AdminError::MissingRight => f.write_str("cap lacks ADMIN right"),
65            AdminError::CapRevoked => f.write_str("capability revoked"),
66            AdminError::WrongTarget => f.write_str("ADMIN requires CapTarget::Scheduler"),
67        }
68    }
69}
70
71impl std::error::Error for AdminError {}
72
73/// Scheduler-level ops (kill, inspect, quota top-up) all pass this check.
74pub fn check_admin(cap: &Cap, cell: &RevocationCell) -> Result<(), AdminError> {
75    if !cap.is_valid(cell) {
76        return Err(AdminError::CapRevoked);
77    }
78    if cap.target != CapTarget::Scheduler {
79        return Err(AdminError::WrongTarget);
80    }
81    if !cap.rights.contains(CapRights::ADMIN) {
82        return Err(AdminError::MissingRight);
83    }
84    Ok(())
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn knowing_flow_id_alone_is_not_enough_to_link() {
93        let cell = RevocationCell::new();
94        let cap = Cap::root(CapTarget::Flow(1), CapRights::SEND, None, &cell);
95        assert!(matches!(
96            check_link(&cap, &cell, 1),
97            Err(LinkError::MissingRight)
98        ));
99        let cap2 = Cap::root(CapTarget::Flow(2), CapRights::LINK, None, &cell);
100        assert!(matches!(
101            check_link(&cap2, &cell, 1),
102            Err(LinkError::WrongTarget)
103        ));
104    }
105
106    #[test]
107    fn admin_requires_scheduler_target_and_right() {
108        let cell = RevocationCell::new();
109        let cap = Cap::root(CapTarget::Scheduler, CapRights::ADMIN, None, &cell);
110        assert!(check_admin(&cap, &cell).is_ok());
111        let cap2 = Cap::root(CapTarget::Flow(1), CapRights::ADMIN, None, &cell);
112        assert!(matches!(
113            check_admin(&cap2, &cell),
114            Err(AdminError::WrongTarget)
115        ));
116    }
117}