diff options
Diffstat (limited to 'src/access/policy.rs')
| -rw-r--r-- | src/access/policy.rs | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/src/access/policy.rs b/src/access/policy.rs new file mode 100644 index 0000000..7ddef0e --- /dev/null +++ b/src/access/policy.rs @@ -0,0 +1,93 @@ +//! 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`). 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<String> { + 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. + Enforced, + /// One this node doesn't know, and why. + Unknown(String), +} + +impl Policy { + pub(crate) fn enforced(&self) -> bool { + matches!(self, Policy::Enforced) + } +} + +/// 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, + 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); + assert_eq!(with(json!({"version": 1})), Policy::Enforced); + assert_eq!( + with(json!({"version": 1, "signatures": "optional"})), + Policy::Enforced + ); + 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(_))); + } +} |
