//! Policy decisions. `Decision` can only be constructed in this module. //! //! Code outside this module cannot build a `Decision` with a struct literal, because its fields //! are private: //! //! ```compile_fail //! let request = proto::ToolRequest { //! session: proto::SessionId::new("s1").unwrap(), //! call: proto::CallId(1), //! tool: "read_file".to_string(), //! arguments: "{}".to_string(), //! }; //! let _ = brokerd::policy::Decision { request, grant: "g".to_string() }; //! ``` //! //! Nor with the constructor, because it is private to this module: //! //! ```compile_fail //! let request = proto::ToolRequest { //! session: proto::SessionId::new("s1").unwrap(), //! call: proto::CallId(1), //! tool: "read_file".to_string(), //! arguments: "{}".to_string(), //! }; //! let _ = brokerd::policy::Decision::new(request, "g".to_string()); //! ``` //! //! The same setup compiles when it goes through `decide`, which proves the two examples above //! fail because of `Decision` and not because of a mistake in the setup: //! //! ``` //! let request = proto::ToolRequest { //! session: proto::SessionId::new("s1").unwrap(), //! call: proto::CallId(1), //! tool: "read_file".to_string(), //! arguments: "{}".to_string(), //! }; //! assert_eq!(brokerd::policy::decide(request).unwrap_err(), proto::DenyReason::NoGrant); //! ``` use proto::{DenyReason, ToolRequest}; #[derive(Debug)] pub struct Decision { request: ToolRequest, grant: String, } impl Decision { #[cfg_attr(not(test), expect(dead_code, reason = "grant matching arrives in M3"))] fn new(request: ToolRequest, grant: String) -> Self { Decision { request, grant } } pub fn request(&self) -> &ToolRequest { &self.request } pub fn grant(&self) -> &str { &self.grant } } /// Until M3 there are no grants, so every request is denied with DenyReason::NoGrant. pub fn decide(request: ToolRequest) -> Result { let _ = request; Err(DenyReason::NoGrant) } #[cfg(test)] mod tests { use super::*; use proto::{CallId, SessionId}; fn request() -> ToolRequest { ToolRequest { session: SessionId::new("s1").unwrap(), call: CallId(1), tool: "read_file".to_string(), arguments: "{}".to_string(), } } #[test] fn no_grants_means_deny() { assert_eq!(decide(request()).unwrap_err(), DenyReason::NoGrant); } #[test] fn decision_exposes_request_and_grant() { let d = Decision::new(request(), "g1".to_string()); assert_eq!(d.request().tool, "read_file"); assert_eq!(d.grant(), "g1"); } }