//! The swarm's write protection policy (`_continuum/access/policy`): whether every node //! judges received changes by rules by path before applying them (`access::screen`, //! `access::rules`). Absent, nobody does: what exists keeps working as it did. Written //! by a user who may write protocol data, as any of it. //! //! `{"version": 1, "signatures": "optional"}`: rules enforced; a change is its claimed //! committer's whether or not a signature proves it (`document::signatures`). //! `"required"`: a change is applied once a signature by its committer vouches for it, //! and waits until then; every node signs every change it makes. A policy //! this node doesn't know (another version, or a setting it lacks) stops it from taking //! writes through the API, and it receives without judging: it can't judge as the //! others do, so it writes nothing they might refuse, until it is upgraded. use serde_json::Value as JsonValue; use crate::document::{ReadDoc, meta, store}; /// The policy versions this node knows. pub(crate) const VERSION: u64 = 1; /// Where the policy is. pub(crate) fn path() -> Vec { meta::path(&[meta::ACCESS, "policy"]) } /// What the policy says. #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) enum Policy { /// None: changes aren't judged. Open, /// Rules by path enforced; with signatures `required`, only on changes a signature /// proves. Enforced { required: bool }, /// One this node doesn't know, and why. Unknown(String), } impl Policy { pub(crate) fn enforced(&self) -> bool { matches!(self, Policy::Enforced { .. }) } /// Whether changes need a signature (`signatures: "required"`). pub(crate) fn required(&self) -> bool { matches!(self, Policy::Enforced { required: true }) } } /// The policy `doc` holds. pub(crate) fn of(doc: &impl ReadDoc) -> Policy { let value = match store::read_path(doc, &path()) { Ok(Some(value)) => value, Ok(None) => return Policy::Open, Err(error) => return Policy::Unknown(format!("it doesn't read: {error}")), }; let version = value.get("version").and_then(JsonValue::as_u64); if version != Some(VERSION) { return Policy::Unknown(format!( "version {}, this node knows {VERSION}", value .get("version") .map_or("none".to_owned(), JsonValue::to_string) )); } match value.get("signatures").and_then(JsonValue::as_str) { None | Some("optional") => Policy::Enforced { required: false }, Some("required") => Policy::Enforced { required: true }, Some(other) => Policy::Unknown(format!("signatures {other:?}")), } } #[cfg(test)] mod tests { use super::*; use crate::document::Document; use crate::document::testing::change; use serde_json::json; fn with(policy: JsonValue) -> Policy { let mut doc = Document::new(); change(&mut doc, |doc| { store::write_path(doc, &path(), &policy).unwrap(); }); of(&doc) } #[test] fn what_the_policy_says() { assert_eq!(of(&Document::new()), Policy::Open); let optional = Policy::Enforced { required: false }; assert_eq!(with(json!({"version": 1})), optional); assert_eq!( with(json!({"version": 1, "signatures": "optional"})), optional ); assert_eq!( with(json!({"version": 1, "signatures": "required"})), Policy::Enforced { required: true } ); assert!(matches!(with(json!({"version": 2})), Policy::Unknown(_))); assert!(matches!( with(json!({"version": 1, "signatures": "sometimes"})), Policy::Unknown(_) )); assert!(matches!(with(json!("on")), Policy::Unknown(_))); } }