aboutsummaryrefslogtreecommitdiffstats
path: root/src/access/policy.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-07 22:35:21 +0100
committertv <tv@krebsco.de>2026-11-07 22:35:21 +0100
commit41657a4d749b1f1d44663ff6504f2e784f091537 (patch)
tree3f4f866f60988696bf047fa718351457a3309b75 /src/access/policy.rs
parenta3e936154e07ba3b95157d26ffeae9364f3d5cef (diff)
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.
Diffstat (limited to 'src/access/policy.rs')
-rw-r--r--src/access/policy.rs58
1 files changed, 50 insertions, 8 deletions
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"})),