From e6fd85f2494c3a90ab4f1af14d917605a7b432fc Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Wed, 19 Oct 2022 15:20:44 +0200 Subject: [PATCH] Implement `public_references` helper function Signed-off-by: Alexis Sellier --- radicle/src/storage/git.rs | 81 +++++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/radicle/src/storage/git.rs b/radicle/src/storage/git.rs index a6360066..6b5b022c 100644 --- a/radicle/src/storage/git.rs +++ b/radicle/src/storage/git.rs @@ -233,32 +233,8 @@ impl Repository { }) .collect::>()?; - // TODO: We could create a higher level `references` method that skips - // canonical/unverifiable refs. - for r in self.backend.references()? { - let r = r?; - - let name = r.name().ok_or(VerifyError::InvalidRef)?; - let oid = if let Some(oid) = r.target() { - oid - } else { - // Ignore symbolic refs, eg. `HEAD`. - continue; - }; - - let (remote_id, refname) = git::parse_ref::(name)?; - let remote_id = if let Some(remote_id) = remote_id { - remote_id - } else { - // Ignore refs that aren't part of a namespace, eg. canonical refs. - continue; - }; - - if refname == *refs::SIGNATURE_REF { - // Ignore the signed-refs reference, as this is what we're verifying. - continue; - } - + for entry in self.public_references()? { + let (remote_id, refname, oid) = entry?; let remote = remotes .get_mut(&remote_id) .ok_or(VerifyError::InvalidRemote(remote_id))?; @@ -267,8 +243,8 @@ impl Repository { .remove(&refname) .ok_or_else(|| VerifyError::UnknownRef(remote_id, refname.clone()))?; - if Oid::from(oid) != signed_oid { - return Err(VerifyError::InvalidRefTarget(remote_id, refname, oid)); + if oid != signed_oid { + return Err(VerifyError::InvalidRefTarget(remote_id, refname, *oid)); } } @@ -391,6 +367,35 @@ impl Repository { ); Ok(remotes) } + + /// Return all references that are public, ie. that are replicated, verified and signed. + fn public_references( + &self, + ) -> Result>, git2::Error> + { + let refs = self.backend.references_glob("refs/namespaces/*")?; + let refs = refs + .map(|reference| { + let r = reference?; + let name = r.name().ok_or(refs::Error::InvalidRef)?; + let (namespace, refname) = git::parse_ref_namespaced::(name)?; + let oid = if let Some(oid) = r.target() { + oid + } else { + // Ignore symbolic refs, eg. `HEAD`. + return Ok(None); + }; + + if refname == *refs::SIGNATURE_REF { + // Ignore the signed-refs reference, as this is what we're verifying. + return Ok(None); + } + Ok(Some((namespace, refname.to_owned(), oid.into()))) + }) + .filter_map(Result::transpose); + + Ok(refs) + } } impl ReadRepository for Repository { @@ -852,6 +857,26 @@ mod tests { assert_eq!(bob_master.target().unwrap(), alice_head); } + #[test] + fn test_public_references() { + let tmp = tempfile::tempdir().unwrap(); + let signer = MockSigner::default(); + let storage = Storage::open(tmp.path().join("storage")).unwrap(); + let (id, _, _, _) = + fixtures::project(tmp.path().join("project"), &storage, &signer).unwrap(); + let proj = storage.repository(id).unwrap(); + + let mut refs = proj + .public_references() + .unwrap() + .map(|r| r.unwrap()) + .map(|(_, r, _)| r.to_string()) + .collect::>(); + refs.sort(); + + assert_eq!(refs, vec!["refs/heads/master", "refs/heads/radicle/id"]); + } + #[test] fn test_upload_pack() { let tmp = tempfile::tempdir().unwrap();