Port sq pki path to the user ID designator framework.

- Port `sq pki path` to the user ID designator framework.
    See #434.

  - This change adds two new additional arguments, `--email` and
    `--name`.
This commit is contained in:
Neal H. Walfield 2024-11-14 16:44:07 +01:00
parent 24f12c6fd8
commit f16ef5d878
No known key found for this signature in database
GPG Key ID: 6863C9AD5B4D22D3
4 changed files with 61 additions and 12 deletions

2
NEWS
View File

@ -63,6 +63,8 @@
- `sq pki path` previously interpreted the last positional argument
as the user ID to authenticate. Make it a named argument
instead, `--userid`.
- Add `sq pki path --email` and `sq pki path --name` as additional
ways to specify the user ID to authenticate.
* Changes in 0.39.0
** Notable changes

View File

@ -2,13 +2,15 @@ use clap::Parser;
use sequoia_openpgp as openpgp;
use openpgp::KeyHandle;
use openpgp::packet::UserID;
use crate::cli::examples;
use examples::Action;
use examples::Actions;
use examples::Example;
use crate::cli::types::UserIDDesignators;
use crate::cli::types::userid_designator;
use super::CertificationNetworkArg;
use super::RequiredTrustAmountArg;
@ -45,12 +47,10 @@ the specified user ID.
)]
pub path: Vec<KeyHandle>,
#[clap(
long = "userid",
value_name = "USERID",
help = "The user ID to authenticate",
)]
pub userid: UserID,
#[command(flatten)]
pub userids: UserIDDesignators<
userid_designator::AnyUserIDEmailNameArgs,
userid_designator::OneValue>,
#[command(flatten)]
pub certification_network: CertificationNetworkArg,

View File

@ -63,6 +63,11 @@ pub type ExistingUserIDEmailNameArgs
pub type AnyUserIDEmailArgs
= <AnyUserIDArg as std::ops::BitOr<AnyEmailArg>>::Output;
/// Enables --userid, --email, and --name (but not --add-userid,
/// --add-email, or --add-name).
pub type AnyUserIDEmailNameArgs
= <AnyUserIDEmailArgs as std::ops::BitOr<AnyNameArg>>::Output;
/// Enables --add-userid, and --add-email (but not --userid, --email,
/// --name, or --add-name).
pub type AddUserIDEmailArgs
@ -192,6 +197,27 @@ impl UserIDDesignator {
userid,
}
}
/// Resolves to the designated user ID.
///
/// If an email or a name, first converts them to a user ID in the
/// usual manner.
pub fn resolve_to_self(&self) -> ResolvedUserID {
use UserIDDesignator::*;
let userid = match self {
UserID(userid) | AnyUserID(userid) | AddUserID(userid) =>
openpgp::packet::UserID::from(&userid[..]),
Email(email) | AnyEmail(email) | AddEmail(email) =>
openpgp::packet::UserID::from(&format!("<{}>", email)[..]),
Name(name) | AnyName(name) | AddName(name) =>
openpgp::packet::UserID::from(&name[..]),
};
ResolvedUserID {
designator: Some(self.clone()),
userid,
}
}
}
/// A data structure that can be flattened into a clap `Command`, and
@ -237,6 +263,11 @@ impl<Arguments, Options> UserIDDesignators<Arguments, Options> {
self.designators.is_empty()
}
/// Like `Vec::len`.
pub fn len(&self) -> usize {
self.designators.len()
}
/// Iterates over the user ID designators.
pub fn iter(&self) -> impl Iterator<Item=&UserIDDesignator> {
self.designators.iter()

View File

@ -16,9 +16,25 @@ pub fn path(sq: Sq, c: Command)
-> Result<()>
{
let Command {
certification_network, trust_amount, path, userid,
certification_network, trust_amount, path, userids,
} = c;
assert_eq!(userids.len(), 1, "guaranteed by clap");
let target = path.last().expect("guaranteed by clap");
let mut userid = None;
if let Ok(cert) = sq.lookup_one(target, None, false) {
if let Ok(vc) = cert.with_policy(sq.policy, sq.time) {
if let Ok(userids) = userids.resolve(&vc) {
assert_eq!(userids.len(), 1);
userid = Some(userids.into_iter().next().unwrap());
}
}
}
let userid = userid.unwrap_or_else(|| {
userids.iter().next().unwrap().resolve_to_self()
});
// Build the network.
let cert_store = match sq.cert_store() {
Ok(Some(cert_store)) => cert_store,
@ -41,7 +57,7 @@ pub fn path(sq: Sq, c: Command)
assert!(path.len() > 0, "guaranteed by clap");
let r = q.lint_path(&path, &userid, required_amount, sq.policy);
let r = q.lint_path(&path, userid.userid(), required_amount, sq.policy);
let target_kh = path.last().expect("have one");
@ -49,11 +65,11 @@ pub fn path(sq: Sq, c: Command)
Ok(path) => {
print_path_header(
target_kh,
&userid,
userid.userid(),
path.amount(),
required_amount,
);
print_path(&path, &userid, " ")?;
print_path(&path, userid.userid(), " ")?;
let trust_amount = path.amount();
if trust_amount >= required_amount {
@ -65,7 +81,7 @@ pub fn path(sq: Sq, c: Command)
Err(err) => {
print_path_header(
target_kh,
&userid,
userid.userid(),
0,
required_amount,
);