From 41657a4d749b1f1d44663ff6504f2e784f091537 Mon Sep 17 00:00:00 2001 From: tv Date: Sat, 7 Nov 2026 22:35:21 +0100 Subject: access: strict policy, signed paths "values": "signed" (strict, requiring signatures too): the registry, _continuum/access, bans and the paths in _continuum/access/userSigned (* for any key) hold values their users signed. Envelopes read as the values they hold where the node reads protocol data. Auth plan M7. --- src/access/policy.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 8 deletions(-) (limited to 'src/access/policy.rs') diff --git a/src/access/policy.rs b/src/access/policy.rs index f8ccfc2..8efedff 100644 --- a/src/access/policy.rs +++ b/src/access/policy.rs @@ -6,7 +6,11 @@ //! `{"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 +//! and waits until then; every node signs every change it makes. `"values": "signed"` +//! (strict): signatures required, and what decides who may do what (the user registry, +//! the policy and the rest of `_continuum/access`, bans) and the paths listed in +//! `_continuum/access/userSigned` hold values their users signed (`access::envelope`), +//! so no member writes there but as a user signed it. 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. @@ -30,7 +34,7 @@ pub(crate) enum Policy { Open, /// Rules by path enforced; with signatures `required`, only on changes a signature /// proves. - Enforced { required: bool }, + Enforced { required: bool, strict: bool }, /// One this node doesn't know, and why. Unknown(String), } @@ -42,13 +46,20 @@ impl Policy { /// Whether changes need a signature (`signatures: "required"`). pub(crate) fn required(&self) -> bool { - matches!(self, Policy::Enforced { required: true }) + matches!(self, Policy::Enforced { required: true, .. }) + } + + /// Whether values at the user-signed paths need their users' signatures + /// (`values: "signed"`, `access::envelope::signed_paths`). + #[cfg_attr(not(test), allow(dead_code))] + pub(crate) fn strict(&self) -> bool { + matches!(self, Policy::Enforced { strict: true, .. }) } } /// The policy `doc` holds. pub(crate) fn of(doc: &impl ReadDoc) -> Policy { - let value = match store::read_path(doc, &path()) { + let value = match store::read_plain(doc, &path()) { Ok(Some(value)) => value, Ok(None) => return Policy::Open, Err(error) => return Policy::Unknown(format!("it doesn't read: {error}")), @@ -62,9 +73,21 @@ pub(crate) fn of(doc: &impl ReadDoc) -> Policy { .map_or("none".to_owned(), JsonValue::to_string) )); } + let strict = match value.get("values").and_then(JsonValue::as_str) { + None | Some("plain") => false, + Some("signed") => true, + Some(other) => return Policy::Unknown(format!("values {other:?}")), + }; match value.get("signatures").and_then(JsonValue::as_str) { - None | Some("optional") => Policy::Enforced { required: false }, - Some("required") => Policy::Enforced { required: true }, + // Signed values: signed changes too. + None | Some("optional") => Policy::Enforced { + required: strict, + strict, + }, + Some("required") => Policy::Enforced { + required: true, + strict, + }, Some(other) => Policy::Unknown(format!("signatures {other:?}")), } } @@ -87,7 +110,10 @@ mod tests { #[test] fn what_the_policy_says() { assert_eq!(of(&Document::new()), Policy::Open); - let optional = Policy::Enforced { required: false }; + let optional = Policy::Enforced { + required: false, + strict: false, + }; assert_eq!(with(json!({"version": 1})), optional); assert_eq!( with(json!({"version": 1, "signatures": "optional"})), @@ -95,8 +121,24 @@ mod tests { ); assert_eq!( with(json!({"version": 1, "signatures": "required"})), - Policy::Enforced { required: true } + Policy::Enforced { + required: true, + strict: false + } + ); + assert_eq!( + with(json!({"version": 1, "values": "signed"})), + Policy::Enforced { + required: true, + strict: true + } ); + assert!(with(json!({"version": 1, "values": "signed"})).strict()); + assert!(!with(json!({"version": 1, "signatures": "required"})).strict()); + assert!(matches!( + with(json!({"version": 1, "values": "sometimes"})), + Policy::Unknown(_) + )); assert!(matches!(with(json!({"version": 2})), Policy::Unknown(_))); assert!(matches!( with(json!({"version": 1, "signatures": "sometimes"})), -- cgit v1.3.1