diff options
| author | tv <tv@krebsco.de> | 2026-11-09 15:26:21 +0000 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-09 18:13:48 +0100 |
| commit | 643aebbbac3089d90a8bb1b015701b6df83bdbba (patch) | |
| tree | 7bf2f9559f9f07dc81482802cd7a14a40480920b | |
| parent | dfea49e0871b78d2b6856eec80c5cd5a7f501dff (diff) | |
access: a shallow read shows only the way through where the user reads partly
Review 4, F4: `?shallow=true` kept every entry the user may at least pass through, and
a shallow read inlines short scalars' values with their types and sizes. A scalar at a
level a `*` passes through (a capability on `data/*/public`, an entry `data/count:
42`) showed its value though no capability covers it; before `*`, only a scalar where
a capability expected a map did.
Now an entry the user may read only partly is shown only as the way through it is: a
map, with no more than its type (not its size, the number of its keys); anything else
there (a scalar, a list, which a deep read leaves out too) isn't shown. Entries it may
read whole are as they were.
| -rw-r--r-- | docs/access.md | 4 | ||||
| -rw-r--r-- | src/api/access.rs | 42 |
2 files changed, 39 insertions, 7 deletions
diff --git a/docs/access.md b/docs/access.md index 1385950..3877dff 100644 --- a/docs/access.md +++ b/docs/access.md @@ -70,7 +70,9 @@ change to it counts on a node as soon as it has reached it. sees the keys on the way to what it may read, as a directory one may pass through but not list (a capability on `servers/eu` shows `servers` with `eu` alone in it), and nothing else; a list on the way is left out whole, as its items have no paths of - their own. Native forms (`text/plain` of a map, a counter as its own type) only of + their own. A shallow read (`?shallow=true`) shows such a map as a map and no more (no + size), and nothing beside the way: a scalar at a `*` no capability covers stays + hidden. Native forms (`text/plain` of a map, a counter as its own type) only of what it may read whole. What a write answers with is what was written. A stream shows what the user could read when it opened. * **Protocol data** is in the store too, under `_continuum`: `read` and `write` on it diff --git a/src/api/access.rs b/src/api/access.rs index 11daf83..6d26139 100644 --- a/src/api/access.rs +++ b/src/api/access.rs @@ -146,18 +146,34 @@ pub(crate) fn prune( } /// A shallow read of `path` (`store::read_shallow`), its `entries` only those `access` -/// lets its user read at least partly. +/// lets its user read at least partly. One it may read only partly is only a way +/// through to what it may: a map, shown as no more than that (no size); anything else +/// there (a scalar no capability covers, a list, as a deep read leaves it out) isn't +/// shown at all. pub(crate) fn prune_shallow(mut value: JsonValue, path: &[String], access: &Access) -> JsonValue { if access.visible(path) == Visible::All { return value; } if let Some(entries) = value.get_mut("entries").and_then(JsonValue::as_object_mut) { let mut child = path.to_vec(); - entries.retain(|key, _| { + entries.retain(|key, entry| { child.push(key.clone()); - let visible = access.visible(&child) != Visible::Nothing; + let visible = access.visible(&child); child.pop(); - visible + match visible { + Visible::All => true, + Visible::Nothing => false, + Visible::Partly => { + let way = entry + .get("type") + .and_then(JsonValue::as_str) + .is_some_and(|kind| kind == "map"); + if way { + *entry = serde_json::json!({ "type": "map" }); + } + way + } + } }); let size = entries.len(); value["size"] = size.into(); @@ -558,12 +574,26 @@ mod tests { #[test] fn a_shallow_read_lists_the_way() { let read = json!({ "type": "map", "size": 3, - "entries": { "a": {}, "b": {}, "_continuum": {} } }); + "entries": { "a": { "type": "map", "size": 4 }, "b": {}, + "_continuum": { "type": "map", "size": 9 } } }); let reader = caps(&[("a/x", &["read"])]); assert_eq!( prune_shallow(read.clone(), &[], &reader), - json!({ "type": "map", "size": 1, "entries": { "a": {} } }) + json!({ "type": "map", "size": 1, "entries": { "a": { "type": "map" } } }) ); assert_eq!(prune_shallow(read.clone(), &[], &Access::Everything), read); + // At a `*`: a scalar beside the way, which no capability covers, isn't shown; + // what the user may read is, whole. + let data = json!({ "type": "map", "size": 3, "entries": { + "count": { "type": "number", "size": 8, "value": 42 }, + "x": { "type": "map", "size": 2 }, + "public": { "type": "string", "size": 2, "value": "hi" } } }); + let wildcard = caps(&[("data/*/public", &["read"]), ("data/public", &["read"])]); + assert_eq!( + prune_shallow(data, &path("data"), &wildcard), + json!({ "type": "map", "size": 2, "entries": { + "x": { "type": "map" }, + "public": { "type": "string", "size": 2, "value": "hi" } } }) + ); } } |
