Skip to main content

sim_lib_doc_core/
caps.rs

1//! Capability profile for office document placements.
2
3use sim_kernel::{CapabilityName, Cx, GrantSeat};
4
5use crate::OfficeError;
6
7/// Capability name for live network access.
8pub const NET_CONNECT_CAPABILITY: &str = "net-connect";
9/// Capability name for spawning helper processes.
10pub const PROCESS_SPAWN_CAPABILITY: &str = "process-spawn";
11/// Capability name for reading wall-clock time.
12pub const WALL_CLOCK_CAPABILITY: &str = "wall-clock";
13/// Capability name for accessing credentials.
14pub const CREDENTIALS_CAPABILITY: &str = "credentials";
15
16/// Default capability posture for office placements.
17pub struct OfficeCapabilityProfile;
18
19macro_rules! grant_into_result {
20    ($grant:expr) => {{
21        #[allow(clippy::let_unit_value)]
22        let grant_result = $grant;
23        #[allow(clippy::unit_arg)]
24        grant_result.into_result()
25    }};
26}
27
28impl OfficeCapabilityProfile {
29    /// Capabilities granted by the default office profile.
30    #[must_use]
31    pub fn granted() -> Vec<CapabilityName> {
32        Vec::new()
33    }
34
35    /// Capabilities denied by default until a host deliberately grants them.
36    #[must_use]
37    pub fn denied() -> Vec<CapabilityName> {
38        [
39            NET_CONNECT_CAPABILITY,
40            PROCESS_SPAWN_CAPABILITY,
41            WALL_CLOCK_CAPABILITY,
42            CREDENTIALS_CAPABILITY,
43        ]
44        .into_iter()
45        .map(CapabilityName::new)
46        .collect()
47    }
48
49    /// Seats the default granted capabilities into a context.
50    pub fn seat(seat: &GrantSeat, cx: &mut Cx) -> Result<(), OfficeError> {
51        for capability in Self::granted() {
52            grant_into_result!(seat.grant(cx, capability))?;
53        }
54        Ok(())
55    }
56}
57
58trait GrantOutcome {
59    fn into_result(self) -> Result<(), OfficeError>;
60}
61
62impl GrantOutcome for () {
63    fn into_result(self) -> Result<(), OfficeError> {
64        Ok(())
65    }
66}
67
68impl GrantOutcome for sim_kernel::Result<()> {
69    fn into_result(self) -> Result<(), OfficeError> {
70        self.map_err(OfficeError::from)
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use std::sync::Arc;
77
78    use sim_kernel::{DefaultFactory, NoopEvalPolicy};
79
80    use super::*;
81
82    #[test]
83    fn default_profile_denies_live_capabilities() {
84        let denied: Vec<_> = OfficeCapabilityProfile::denied()
85            .into_iter()
86            .map(|capability| capability.as_str().to_owned())
87            .collect();
88
89        assert_eq!(
90            denied,
91            vec![
92                NET_CONNECT_CAPABILITY,
93                PROCESS_SPAWN_CAPABILITY,
94                WALL_CLOCK_CAPABILITY,
95                CREDENTIALS_CAPABILITY,
96            ]
97        );
98        assert!(OfficeCapabilityProfile::granted().is_empty());
99    }
100
101    #[test]
102    fn seating_default_profile_does_not_grant_live_network() {
103        let (mut cx, seat) = sim_kernel::Cx::new_seated(
104            Arc::new(NoopEvalPolicy),
105            Arc::new(DefaultFactory),
106            sim_kernel::HandleSeed::new(0xce27_937d_0a0a_e3ea),
107        );
108
109        OfficeCapabilityProfile::seat(&seat, &mut cx).unwrap();
110
111        let network = CapabilityName::new(NET_CONNECT_CAPABILITY);
112        assert!(cx.require(&network).is_err());
113    }
114}