Port sq key approvals update to the user ID designator framework.

- See #434.
This commit is contained in:
Neal H. Walfield 2024-11-13 16:31:25 +01:00
parent c7795149c2
commit 9712dc5cc6
No known key found for this signature in database
GPG Key ID: 6863C9AD5B4D22D3
3 changed files with 17 additions and 62 deletions

View File

@ -239,23 +239,10 @@ pub struct UpdateCommand {
OneValueAndFileRequiresOutput,
ApprovalsListDoc>,
#[clap(
long = "name",
help = "Change approvals on this name user ID",
)]
pub names: Vec<String>,
#[clap(
long = "email",
help = "Change approvals on this email address user ID",
)]
pub emails: Vec<String>,
#[clap(
long = "userid",
help = "Change approvals on this user ID",
)]
pub userids: Vec<String>,
#[command(flatten)]
pub userids: UserIDDesignators<
userid_designator::ExistingUserIDEmailNameArgs,
userid_designator::OptionalValue>,
#[clap(
long = "remove-all",

View File

@ -13,7 +13,6 @@ use sequoia_wot as wot;
use crate::Sq;
use crate::cli;
use crate::cli::key::approvals;
use crate::common::userid::make_userid_filter;
pub fn dispatch(sq: Sq, command: approvals::Command)
-> Result<()>
@ -108,11 +107,9 @@ fn update(
) -> Result<()> {
let store = sq.cert_store_or_else()?;
let handle =
sq.resolve_cert(&command.cert, sequoia_wot::FULLY_TRUSTED)?.1;
let key = sq.lookup_one(handle, None, true)?;
let key = sq.resolve_cert(&command.cert, sequoia_wot::FULLY_TRUSTED)?.0;
let vcert = key.with_policy(sq.policy, sq.time)?;
let userids = command.userids.resolve(&vcert)?;
// Lookup any explicitly goodlisted certifiers. We need these to
// verify the certifications.
@ -133,10 +130,16 @@ fn update(
// Now, create new approval signatures.
let mut approval_signatures = Vec::new();
// For the selected user IDs.
let uid_filter = make_userid_filter(
&command.names, &command.emails, &command.userids)?;
for uid in vcert.userids().filter(uid_filter) {
// resolve returns ResolvedUserIDs, which contain UserIDs, but we
// need ValidUserIDAmalgamations.
let all = userids.is_empty();
let mut designated_userids = BTreeSet::from_iter(
userids.into_iter().map(|u| u.userid().clone()));
for uid in vcert.userids() {
if ! all && ! designated_userids.remove(uid.userid()) {
continue;
}
wprintln!(initial_indent = " - ", "{}",
String::from_utf8_lossy(uid.value()));
@ -262,6 +265,7 @@ fn update(
next_approved.into_iter(),
)?);
}
assert!(designated_userids.is_empty());
// Finally, add the new signatures.
let key = key.insert_packets(approval_signatures)?;

View File

@ -1,6 +1,5 @@
use sequoia_openpgp as openpgp;
use openpgp::packet::UserID;
use openpgp::cert::amalgamation::ValidUserIDAmalgamation;
/// A canonical user ID.
#[derive(thiserror::Error, Debug)]
@ -298,41 +297,6 @@ pub fn lint_emails(emails: &[String]) -> Result<(), anyhow::Error> {
}
}
/// Given a list of names, email addresses, and user IDs, returns a
/// user ID filter.
///
/// The names and email addresses are linted first, returning an error
/// with hints if there are any issues found.
///
/// If neither names, email addresses, or user IDs are given, the
/// filter accepts all user IDs.
///
/// Should be used for subcommands that operate on (subsets of) user
/// IDs.
pub fn make_userid_filter<'c>(
names: &'c [String],
emails: &'c [String],
userids: &'c [String]
)
-> anyhow::Result<Box<dyn Fn(&ValidUserIDAmalgamation) -> bool + 'c>>
{
lint_names(names)?;
lint_emails(emails)?;
Ok(Box::new(|uid: &ValidUserIDAmalgamation| {
if emails.is_empty() && names.is_empty() && userids.is_empty() {
// No filter, list all user IDs.
true
} else {
std::str::from_utf8(uid.value())
.map(|u| names.iter().any(|v| u == v)
|| emails.iter().any(|v| u == v)
|| userids.iter().any(|v| u == v))
.unwrap_or(false)
}
}))
}
#[cfg(test)]
mod test {
use super::*;