diff options
| author | tv <tv@krebsco.de> | 2026-11-07 22:35:21 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-07 22:35:21 +0100 |
| commit | 7fed3e4478ad28151fab288d569a1268c1f3e4cf (patch) | |
| tree | 1d406a10393b16b7f85295e14b855ca5ac7c741f /src/access/envelope.rs | |
| parent | 41657a4d749b1f1d44663ff6504f2e784f091537 (diff) | |
api: signed values at the store
PUT with application/continuum-signed writes an envelope where values
are signed (403/409 saying why); reads open envelopes and name the
signer (x-continuum-signed-by), ?raw=true shows them. Under strict,
unsigned writes reaching a signed value and the users API are 409.
Auth plan M7.
Diffstat (limited to 'src/access/envelope.rs')
| -rw-r--r-- | src/access/envelope.rs | 45 |
1 files changed, 39 insertions, 6 deletions
diff --git a/src/access/envelope.rs b/src/access/envelope.rs index 7244a3b..1804b98 100644 --- a/src/access/envelope.rs +++ b/src/access/envelope.rs @@ -72,15 +72,39 @@ pub(crate) fn signed_paths(doc: &impl ReadDoc) -> Vec<Vec<String>> { /// 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()) + (path.len() >= pattern.len() && agree(pattern, path)) + .then(|| path[..pattern.len()].to_vec()) }) } +/// Whether `path` is where a user-signed value goes, of `signed` (`signed_paths`). +pub(crate) fn is_signed(signed: &[Vec<String>], path: &[String]) -> bool { + signed + .iter() + .any(|pattern| pattern.len() == path.len() && agree(pattern, path)) +} + +/// Whether a user-signed value of `signed` may be below `path` (`signed_paths`). +pub(crate) fn above_signed(signed: &[Vec<String>], path: &[String]) -> bool { + signed + .iter() + .any(|pattern| pattern.len() > path.len() && agree(pattern, path)) +} + +/// Whether a write at `path` reaches a user-signed value of `signed` (`signed_paths`): +/// at one, within one, or above one. +pub(crate) fn reaches_signed(signed: &[Vec<String>], path: &[String]) -> bool { + signed.iter().any(|pattern| agree(pattern, path)) +} + +/// Whether `pattern` and `path` agree as far as both go (`*`: any key). +fn agree(pattern: &[String], path: &[String]) -> bool { + pattern + .iter() + .zip(path) + .all(|(segment, key)| segment == "*" || segment == key) +} + /// A signed value's payload. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub(crate) struct Payload { @@ -280,5 +304,14 @@ mod tests { signed_path(&signed, &path("_continuum/access/policy")), Some(path("_continuum/access/policy")) ); + assert!(is_signed(&signed, &path("_continuum/users/alice"))); + assert!(!is_signed(&signed, &path("_continuum/users/alice/caps"))); + assert!(above_signed(&signed, &path("_continuum/users"))); + assert!(above_signed(&signed, &path("a"))); + assert!(!above_signed(&signed, &path("a/b"))); + assert!(reaches_signed(&signed, &path("a"))); + assert!(reaches_signed(&signed, &path("a/b/c"))); + assert!(!reaches_signed(&signed, &path("a/c"))); + assert!(!reaches_signed(&signed, &path("_continuum/members/x"))); } } |
