//! The log and metric read grants: where swarm-controller mirrors an agent's //! `logs` and `metrics` permissions in the store, and what the object holds. //! //! The permissions themselves live in the queue's `agent-permissions` bucket. //! The store gateways' `swarm-logs-auth` has no queue access, so it reads this //! mirror instead. A leaf that is absent or unreadable is no grant. //! //! Under the agent's own prefix, so the agent can read its own grant and no //! agent can write it: the shared agent policy grants `create` on one other //! leaf only. use serde::{Deserialize, Serialize}; use crate::{ Error, path::{Kind, principal_prefix}, }; const LEAF: &str = "observability"; /// The path holding `agent`'s [`Grant`]. /// /// # Errors /// [`Error::PathSegment`] when `agent` contains anything but `[A-Za-z0-9_-]`, /// which is what keeps one agent's name from addressing another agent's grant. pub fn grant_path(agent: &str) -> Result { let prefix = principal_prefix(Kind::Agent, agent)?; Ok(format!("{prefix}/{LEAF}")) } /// What a [`grant_path`] holds: which stores the agent may read. #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct Grant { /// The agent holds the `logs` permission. #[serde(default)] pub logs: bool, /// The agent holds the `metrics` permission. #[serde(default)] pub metrics: bool, } #[cfg(test)] mod tests { use super::*; #[test] fn the_path_is_under_the_agents_own_prefix() { assert_eq!( grant_path("atlas").unwrap(), "swarm/agents/atlas/observability" ); assert!(grant_path("../argus").is_err()); } #[test] fn a_field_left_out_is_not_granted() { let grant: Grant = serde_json::from_str("{}").unwrap(); assert_eq!(grant, Grant::default()); let grant: Grant = serde_json::from_str(r#"{"logs":true}"#).unwrap(); assert_eq!((grant.logs, grant.metrics), (true, false)); } }