| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
use super::json; |
| 5 |
use super::open; |
| 6 |
use super::ui; |
| 7 |
use anyhow::{anyhow, Result}; |
| 8 |
use oxy_core::hash::KeyId; |
| 9 |
use std::path::Path; |
| 10 |
|
| 11 |
pub fn verify(hub: Option<&str>, key: Option<&str>, all: bool, as_json: bool) -> Result<i32> { |
| 12 |
let repo = open()?; |
| 13 |
let remote = repo.default_remote(); |
| 14 |
let url = match hub { |
| 15 |
Some(h) if h.contains("://") => h.to_string(), |
| 16 |
Some(h) => format!("https://{h}"), |
| 17 |
None => repo.config.remotes.get(&remote).cloned().ok_or_else(|| { |
| 18 |
anyhow!("name the hub to check (this checkout has no remote {remote})") |
| 19 |
})?, |
| 20 |
}; |
| 21 |
let ca = repo |
| 22 |
.config |
| 23 |
.remote_cas |
| 24 |
.get(&remote) |
| 25 |
.map(Path::new) |
| 26 |
.filter(|_| hub.is_none()); |
| 27 |
let audit = repo.audit_log(&url, ca).map_err(|e| anyhow!("{e}"))?; |
| 28 |
let key = match key { |
| 29 |
Some(k) => Some( |
| 30 |
KeyId::from_hex(k) |
| 31 |
.ok_or_else(|| anyhow!("--key takes a full KeyId (64 hex digits)"))?, |
| 32 |
), |
| 33 |
None => None, |
| 34 |
}; |
| 35 |
let rid = repo.id().ok(); |
| 36 |
let listed: Vec<(usize, &oxy_core::tlog::Entry)> = audit |
| 37 |
.entries |
| 38 |
.iter() |
| 39 |
.enumerate() |
| 40 |
.filter(|(_, e)| match (&key, all) { |
| 41 |
(_, true) => true, |
| 42 |
(Some(k), _) => e.signers.contains(k), |
| 43 |
(None, _) => Some(e.repo) == rid, |
| 44 |
}) |
| 45 |
.collect(); |
| 46 |
let what = match (&key, all) { |
| 47 |
(_, true) => "every entry".to_string(), |
| 48 |
(Some(k), _) => format!("entries signed by {}", k.short(12)), |
| 49 |
(None, _) => "this repository's entries".to_string(), |
| 50 |
}; |
| 51 |
if as_json { |
| 52 |
println!( |
| 53 |
"{}", |
| 54 |
json::obj([ |
| 55 |
("hub", json::s(&audit.hub)), |
| 56 |
("origin", json::s(&audit.checkpoint.origin)), |
| 57 |
("size", json::n(audit.checkpoint.size)), |
| 58 |
( |
| 59 |
"root", |
| 60 |
json::s(&oxy_core::hash::ObjectRef(audit.checkpoint.root).to_hex()) |
| 61 |
), |
| 62 |
( |
| 63 |
"entries", |
| 64 |
json::arr(listed.iter().map(|(i, e)| { |
| 65 |
json::obj([ |
| 66 |
("index", json::n(*i)), |
| 67 |
("kind", json::s(&e.kind)), |
| 68 |
( |
| 69 |
"repo", |
| 70 |
json::s( |
| 71 |
&e.repo |
| 72 |
.iter() |
| 73 |
.map(|b| format!("{b:02x}")) |
| 74 |
.collect::<String>(), |
| 75 |
), |
| 76 |
), |
| 77 |
("object", json::s(&e.object.to_hex())), |
| 78 |
( |
| 79 |
"signers", |
| 80 |
json::arr(e.signers.iter().map(|k| json::s(&k.to_hex()))), |
| 81 |
), |
| 82 |
("received_ms", json::n(e.received)), |
| 83 |
]) |
| 84 |
})), |
| 85 |
), |
| 86 |
]) |
| 87 |
); |
| 88 |
return Ok(0); |
| 89 |
} |
| 90 |
println!( |
| 91 |
"{} {}: {} entries make the root its log key signed, and extend what you saw before", |
| 92 |
ui::done("Verified"), |
| 93 |
ui::path(&audit.checkpoint.origin), |
| 94 |
audit.checkpoint.size |
| 95 |
); |
| 96 |
if listed.is_empty() { |
| 97 |
println!(" {} {}", ui::note(), ui::dim(format!("no {what} in it"))); |
| 98 |
return Ok(0); |
| 99 |
} |
| 100 |
println!( |
| 101 |
" {} {}", |
| 102 |
ui::note(), |
| 103 |
ui::dim(format!("{} of {what}:", listed.len())) |
| 104 |
); |
| 105 |
for (i, e) in listed { |
| 106 |
let signers: Vec<String> = e.signers.iter().map(|k| k.short(12)).collect(); |
| 107 |
println!( |
| 108 |
" #{i:<6} {:<11} {} {} {}", |
| 109 |
e.kind, |
| 110 |
ui::hash(e.object.short(12)), |
| 111 |
ui::dim(if signers.is_empty() { |
| 112 |
"unsigned".to_string() |
| 113 |
} else { |
| 114 |
format!("by {}", signers.join(", ")) |
| 115 |
}), |
| 116 |
ui::dim(ui::ago(e.received)) |
| 117 |
); |
| 118 |
} |
| 119 |
Ok(0) |
| 120 |
} |