diff options
Diffstat (limited to 'src/access')
| -rw-r--r-- | src/access/strict.rs | 53 |
1 files changed, 50 insertions, 3 deletions
diff --git a/src/access/strict.rs b/src/access/strict.rs index 0fa6577..0b294f8 100644 --- a/src/access/strict.rs +++ b/src/access/strict.rs @@ -27,6 +27,10 @@ pub(crate) const NAMESPACE: &str = "continuum-strict"; pub(crate) struct Manifest { /// The swarm it is for. pub(crate) swarm: String, + /// The epoch it is for, and the heads it was made at: the base a remake taking it + /// must have, so it serves once, for the values it names as they were then. + pub(crate) epoch: crate::document::replica::EpochRecord, + pub(crate) base: Vec<String>, /// The policy the next epoch has (`"values": "signed"`). pub(crate) policy: JsonValue, /// The values kept at the places values are signed (`envelope::signed_paths`), each @@ -46,6 +50,29 @@ pub(crate) struct Kept { } impl Manifest { + /// Whether it is for a remake of `epoch` at `base` (hex heads): why not, if not. + pub(crate) fn for_base( + &self, + epoch: &crate::document::replica::EpochRecord, + base: Option<&[String]>, + ) -> Result<(), String> { + if &self.epoch != epoch { + return Err(format!( + "made for epoch {}:{}, not this one", + self.epoch.generation, self.epoch.id + )); + } + let sorted = |heads: &[String]| { + let mut heads = heads.to_vec(); + heads.sort(); + heads + }; + match base { + Some(base) if sorted(base) == sorted(&self.base) => Ok(()), + _ => Err("made at other heads than the remake's base".to_owned()), + } + } + /// Whether it keeps `value` at `path`. pub(crate) fn keeps(&self, path: &[String], value: &JsonValue) -> bool { let digest = digest(value); @@ -104,17 +131,24 @@ pub(crate) fn held(doc: &impl ReadDoc) -> Vec<(Vec<String>, JsonValue)> { out } -/// A manifest for `doc` of `swarm`: keeping every value held at a signed place now, the -/// policy `policy`, for `user` to look over and sign at `at`. Its exact line. +/// A manifest for `doc` (at heads `base`, of `epoch` of `swarm`): keeping every value +/// held at a signed place now, the policy `policy`, for `user` to look over and sign at +/// `at`. Its exact line. pub(crate) fn manifest_of( doc: &impl ReadDoc, swarm: &str, + epoch: crate::document::replica::EpochRecord, + base: &[crate::document::ChangeHash], policy: JsonValue, user: &str, at: u64, ) -> String { + let mut base: Vec<String> = base.iter().map(ToString::to_string).collect(); + base.sort(); let manifest = Manifest { swarm: swarm.to_owned(), + epoch, + base, policy, keep: held(doc) .into_iter() @@ -202,7 +236,10 @@ mod tests { store::write_path(doc, &path("notes"), &json!(1)).unwrap(); }); let policy = json!({"version": 1, "values": "signed"}); - let line = manifest_of(&doc, "s", policy, "alice", 1); + let epoch = + crate::document::replica::EpochRecord::from(crate::document::replica::Epoch::GENESIS); + let heads = doc.get_heads(); + let line = manifest_of(&doc, "s", epoch.clone(), &heads, policy, "alice", 1); let key = SigningKey::from_bytes(&[3; 32]); let public = sshsig::PublicKey(key.verifying_key().to_bytes()); let text = format!("{line}\n{}", sshsig::sign(&key, NAMESPACE, line.as_bytes())); @@ -229,5 +266,15 @@ mod tests { assert!(verify(&elsewhere, std::slice::from_ref(&public), "s").is_err()); let tampered = text.replacen("alice", "mallory", 1); assert!(verify(&tampered, std::slice::from_ref(&public), "s").is_err()); + // For its epoch and base only. + let base: Vec<String> = heads.iter().map(ToString::to_string).collect(); + assert!(manifest.for_base(&epoch, Some(&base)).is_ok()); + assert!(manifest.for_base(&epoch, None).is_err()); + assert!(manifest.for_base(&epoch, Some(&[])).is_err()); + let later = crate::document::replica::EpochRecord { + generation: 1, + id: "ab".to_owned(), + }; + assert!(manifest.for_base(&later, Some(&base)).is_err()); } } |
