aboutsummaryrefslogtreecommitdiffstats
path: root/src/access/envelope.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/envelope.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/envelope.rs')
-rw-r--r--src/access/envelope.rs58
1 files changed, 44 insertions, 14 deletions
diff --git a/src/access/envelope.rs b/src/access/envelope.rs
index a875dca..7244a3b 100644
--- a/src/access/envelope.rs
+++ b/src/access/envelope.rs
@@ -1,5 +1,6 @@
-//! Values signed by their user: at a path the swarm lists as user-signed
-//! (`_continuum/access/userSigned`, `listed`), the value is a signed envelope, one
+//! Values signed by their user: under a strict policy (`access::policy`), at what
+//! decides who may do what and at the paths the swarm lists as user-signed
+//! (`_continuum/access/userSigned`, `listed`; `signed_paths`), the value is a signed envelope, one
//! string, `cv1.<payload>.<signature>` (each base64url, unpadded). The payload is JSON:
//! the `path` it is for (segments), the `value` (`null`: deleted), the `user` who
//! signed it, when (`at`, Unix ms, by the signer's clock), and `prev`: the SHA-256 (hex)
@@ -34,7 +35,7 @@ pub(crate) fn listed_at() -> Vec<String> {
/// The paths `doc` lists as user-signed; an entry that isn't a list of strings is left
/// out.
pub(crate) fn listed(doc: &impl ReadDoc) -> Vec<Vec<String>> {
- let Ok(Some(JsonValue::Array(entries))) = store::read_path(doc, &listed_at()) else {
+ let Ok(Some(JsonValue::Array(entries))) = store::read_plain(doc, &listed_at()) else {
return Vec::new();
};
entries
@@ -49,12 +50,35 @@ pub(crate) fn listed(doc: &impl ReadDoc) -> Vec<Vec<String>> {
.collect()
}
-/// The user-signed path `path` is at or within, if any (`listed`).
-pub(crate) fn signed_path<'a>(listed: &'a [Vec<String>], path: &[String]) -> Option<&'a [String]> {
- listed
+/// What decides who may do what, whose values are always user-signed under a strict
+/// policy (`access::policy`): every user's entry, every entry of `_continuum/access`,
+/// every ban. A `*` segment stands for any one key.
+fn deciding() -> Vec<Vec<String>> {
+ [meta::USERS, meta::ACCESS, meta::BANNED]
.iter()
- .find(|signed| path.starts_with(signed))
- .map(Vec::as_slice)
+ .map(|part| meta::path(&[part, "*"]))
+ .collect()
+}
+
+/// The paths whose values their users sign under a strict policy: what decides who may
+/// do what (`deciding`), and those `doc` lists (`listed`).
+pub(crate) fn signed_paths(doc: &impl ReadDoc) -> Vec<Vec<String>> {
+ let mut paths = deciding();
+ paths.extend(listed(doc));
+ paths
+}
+
+/// The user-signed path `path` is at or within, if any, of `signed` (`signed_paths`):
+/// its `*` segments as `path` has them.
+pub(crate) fn signed_path(signed: &[Vec<String>], path: &[String]) -> Option<Vec<String>> {
+ signed.iter().find_map(|pattern| {
+ (path.len() >= pattern.len()
+ && pattern
+ .iter()
+ .zip(path)
+ .all(|(segment, key)| segment == "*" || segment == key))
+ .then(|| path[..pattern.len()].to_vec())
+ })
}
/// A signed value's payload.
@@ -242,13 +266,19 @@ mod tests {
change(&mut doc, |doc| {
store::write_path(doc, &listed_at(), &json!([["config"], ["a", "b"], "junk"])).unwrap();
});
- let listed = listed(&doc);
- assert_eq!(listed, vec![path("config"), path("a/b")]);
+ assert_eq!(listed(&doc), vec![path("config"), path("a/b")]);
+ let signed = signed_paths(&doc);
+ assert_eq!(signed_path(&signed, &path("config")), Some(path("config")));
+ assert_eq!(signed_path(&signed, &path("a/b/c")), Some(path("a/b")));
+ assert_eq!(signed_path(&signed, &path("a")), None);
+ assert_eq!(
+ signed_path(&signed, &path("_continuum/users/alice/caps")),
+ Some(path("_continuum/users/alice"))
+ );
+ assert_eq!(signed_path(&signed, &path("_continuum/users")), None);
assert_eq!(
- signed_path(&listed, &path("config")),
- Some(&path("config")[..])
+ signed_path(&signed, &path("_continuum/access/policy")),
+ Some(path("_continuum/access/policy"))
);
- assert_eq!(signed_path(&listed, &path("a/b/c")), Some(&path("a/b")[..]));
- assert_eq!(signed_path(&listed, &path("a")), None);
}
}