aboutsummaryrefslogtreecommitdiffstats
path: root/src/document
diff options
context:
space:
mode:
Diffstat (limited to 'src/document')
-rw-r--r--src/document/signatures.rs2
-rw-r--r--src/document/store.rs53
2 files changed, 54 insertions, 1 deletions
diff --git a/src/document/signatures.rs b/src/document/signatures.rs
index 63b19a5..5d8731b 100644
--- a/src/document/signatures.rs
+++ b/src/document/signatures.rs
@@ -67,7 +67,7 @@ fn related(signed: &[String], path: &[String]) -> bool {
/// strings is left out.
pub(crate) fn listed(doc: &Document) -> Vec<Vec<String>> {
let at: Vec<String> = SIGNED_PATHS.iter().map(|s| (*s).to_owned()).collect();
- let Ok(Some(serde_json::Value::Array(entries))) = super::store::read_path(doc, &at) else {
+ let Ok(Some(serde_json::Value::Array(entries))) = super::store::read_plain(doc, &at) else {
return Vec::new();
};
entries
diff --git a/src/document/store.rs b/src/document/store.rs
index 778307f..c946fef 100644
--- a/src/document/store.rs
+++ b/src/document/store.rs
@@ -454,6 +454,30 @@ fn read_path_inner(
Ok(Some(resolved_to_json(doc, &current, form, log)?))
}
+/// [`read_path`] of a value its user may have signed (`access::envelope`): an envelope
+/// read as the value it holds, unchecked (the policy checked it before it went in), a
+/// signed `null` as nothing.
+pub(crate) fn read_plain(
+ doc: &impl ReadDoc,
+ path: &[String],
+) -> Result<Option<JsonValue>, StoreError> {
+ Ok(match read_path(doc, path)? {
+ Some(JsonValue::String(text)) if text.starts_with("cv1.") => {
+ enveloped(&text).filter(|value| !value.is_null())
+ }
+ other => other,
+ })
+}
+
+/// The value an envelope (`cv1.<payload>.<signature>`) holds; None if it isn't one.
+fn enveloped(text: &str) -> Option<JsonValue> {
+ use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
+ let payload = text.split('.').nth(1)?;
+ let bytes = URL_SAFE_NO_PAD.decode(payload).ok()?;
+ let mut payload: JsonValue = serde_json::from_slice(&bytes).ok()?;
+ Some(payload.get_mut("value")?.take())
+}
+
/// Reads a path; the empty path is the whole store.
pub(crate) fn read_path(
doc: &impl ReadDoc,
@@ -1673,6 +1697,35 @@ mod tests {
at.split('/').map(str::to_owned).collect()
}
+ /// An envelope reads as the value it holds, a signed `null` as nothing, anything
+ /// else as it is.
+ #[test]
+ fn plain_reads_open_envelopes() {
+ use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
+ let sealed = |value: JsonValue| {
+ let payload =
+ json!({"path": ["a"], "value": value, "user": "alice", "at": 1, "prev": ""});
+ format!("cv1.{}.c2ln", URL_SAFE_NO_PAD.encode(payload.to_string()))
+ };
+ let mut doc = crate::document::Document::new();
+ try_change(&mut doc, |doc| {
+ write_path(doc, &path("a"), &json!(sealed(json!({"mode": "fast"}))))?;
+ write_path(doc, &path("gone"), &json!(sealed(JsonValue::Null)))?;
+ write_path(doc, &path("plain"), &json!("cv1-ish"))
+ })
+ .unwrap();
+ assert_eq!(
+ read_plain(&doc, &path("a")).unwrap(),
+ Some(json!({"mode": "fast"}))
+ );
+ assert_eq!(read_plain(&doc, &path("gone")).unwrap(), None);
+ assert_eq!(
+ read_plain(&doc, &path("plain")).unwrap(),
+ Some(json!("cv1-ish"))
+ );
+ assert_eq!(read_plain(&doc, &path("none")).unwrap(), None);
+ }
+
#[test]
fn changes_name_the_paths_their_operations_touch() {
fn touched(