Refactor user ID resolution.

- Add `UserIDDesignators::resolve` to resolve user ID designators
    for a given certificate.

  - Use it from `sq pki certify` and `sq pki authorize`.
This commit is contained in:
Neal H. Walfield 2024-10-15 12:40:27 +02:00
parent 1a32d11c8f
commit 2caba0758e
No known key found for this signature in database
GPG Key ID: 6863C9AD5B4D22D3
5 changed files with 126 additions and 201 deletions

View File

@ -1,12 +1,10 @@
use sequoia_openpgp as openpgp;
use openpgp::packet::UserID;
use openpgp::Result;
use openpgp::types::KeyFlags;
use crate::Sq;
use crate::cli::pki::authorize;
use crate::cli::types::FileStdinOrKeyHandle;
use crate::cli::types::userid_designator::UserIDDesignator;
use crate::commands::FileOrStdout;
use crate::parse_notations;
@ -35,93 +33,8 @@ pub fn authorize(sq: Sq, mut c: authorize::Command)
}
let vc = cert.with_policy(sq.policy, Some(sq.time))?;
// Find the matching User ID.
let mut userids = Vec::new();
// Don't stop at the first error.
let mut missing = false;
let mut bad = None;
for designator in c.userids.iter() {
match designator {
UserIDDesignator::UserID(userid) => {
let userid = UserID::from(&userid[..]);
// If --add-userid is specified, we use the user ID as
// is. Otherwise, we make sure there is a matching
// self-signed user ID.
if c.userids.add_userid().unwrap_or(false) {
userids.push(userid.clone());
} else if let Some(_) = vc.userids()
.find(|ua| {
ua.userid() == &userid
})
{
userids.push(userid.clone());
} else {
wprintln!("{:?} is not a self-signed user ID.",
String::from_utf8_lossy(userid.value()));
missing = true;
}
}
UserIDDesignator::Email(email) => {
// Validate the email address.
let userid = match UserID::from_address(None, None, email) {
Ok(userid) => userid,
Err(err) => {
wprintln!("{:?} is not a valid email address: {}",
email, err);
bad = Some(err);
continue;
}
};
// Extract a normalized version for comparison
// purposes.
let email_normalized = match userid.email_normalized() {
Ok(Some(email)) => email,
Ok(None) => {
wprintln!("{:?} is not a valid email address", email);
bad = Some(anyhow::anyhow!(format!(
"{:?} is not a valid email address", email)));
continue;
}
Err(err) => {
wprintln!("{:?} is not a valid email address: {}",
email, err);
bad = Some(err);
continue;
}
};
// Find any the matching self-signed user IDs.
let mut found = false;
for ua in vc.userids() {
if Some(&email_normalized)
== ua.email_normalized().unwrap_or(None).as_ref()
{
userids.push(ua.userid().clone());
found = true;
}
}
if ! found {
if c.userids.add_userid().unwrap_or(false) {
// Add the bare email address.
userids.push(userid);
} else {
eprintln!("The email address {:?} does not match any \
user IDs.",
email);
missing = true;
}
}
}
}
}
if missing || userids.is_empty() {
let mut userids = c.userids.resolve(&vc)?;
if userids.is_empty() {
// Use all self-signed User IDs.
userids = vc.userids()
.map(|ua| ua.userid().clone())
@ -135,10 +48,6 @@ pub fn authorize(sq: Sq, mut c: authorize::Command)
}
};
if let Some(err) = bad {
return Err(err);
}
let notations = parse_notations(&c.notation)?;
crate::common::pki::certify::certify(

View File

@ -1,12 +1,10 @@
use sequoia_openpgp as openpgp;
use openpgp::packet::UserID;
use openpgp::Result;
use openpgp::types::KeyFlags;
use crate::Sq;
use crate::cli::pki::certify;
use crate::cli::types::FileStdinOrKeyHandle;
use crate::cli::types::userid_designator::UserIDDesignator;
use crate::commands::FileOrStdout;
use crate::parse_notations;
@ -35,112 +33,7 @@ pub fn certify(sq: Sq, mut c: certify::Command)
}
let vc = cert.with_policy(sq.policy, Some(sq.time))?;
// Find the matching User ID.
let mut userids = Vec::new();
// Don't stop at the first error.
let mut missing = false;
let mut bad = None;
for designator in c.userids.iter() {
match designator {
UserIDDesignator::UserID(userid) => {
let userid = UserID::from(&userid[..]);
// If --add-userid is specified, we use the user ID as
// is. Otherwise, we make sure there is a matching
// self-signed user ID.
if c.userids.add_userid().unwrap_or(false) {
userids.push(userid.clone());
} else if let Some(_) = vc.userids()
.find(|ua| {
ua.userid() == &userid
})
{
userids.push(userid.clone());
} else {
wprintln!("{:?} is not a self-signed user ID.",
String::from_utf8_lossy(userid.value()));
missing = true;
}
}
UserIDDesignator::Email(email) => {
// Validate the email address.
let userid = match UserID::from_address(None, None, email) {
Ok(userid) => userid,
Err(err) => {
wprintln!("{:?} is not a valid email address: {}",
email, err);
bad = Some(err);
continue;
}
};
// Extract a normalized version for comparison
// purposes.
let email_normalized = match userid.email_normalized() {
Ok(Some(email)) => email,
Ok(None) => {
wprintln!("{:?} is not a valid email address", email);
bad = Some(anyhow::anyhow!(format!(
"{:?} is not a valid email address", email)));
continue;
}
Err(err) => {
wprintln!("{:?} is not a valid email address: {}",
email, err);
bad = Some(err);
continue;
}
};
// Find any the matching self-signed user IDs.
let mut found = false;
for ua in vc.userids() {
if Some(&email_normalized)
== ua.email_normalized().unwrap_or(None).as_ref()
{
userids.push(ua.userid().clone());
found = true;
}
}
if ! found {
if c.userids.add_userid().unwrap_or(false) {
// Add the bare email address.
userids.push(userid);
} else {
eprintln!("The email address {:?} does not match any \
user IDs.",
email);
missing = true;
}
}
}
}
}
if missing {
wprintln!("{}'s self-signed user IDs:", vc.fingerprint());
let mut have_valid = false;
for ua in vc.userids() {
if let Ok(u) = std::str::from_utf8(ua.userid().value()) {
have_valid = true;
wprintln!(" - {:?}", u);
}
}
if ! have_valid {
wprintln!(" - Certificate has no valid user IDs.");
}
wprintln!("Pass `--add-userid` to certify a user ID even if it \
isn't self signed.");
return Err(anyhow::anyhow!("Not a self-signed user ID"));
};
if let Some(err) = bad {
return Err(err);
}
let userids = c.userids.resolve(&vc)?;
let notations = parse_notations(&c.notation)?;

View File

@ -20,6 +20,8 @@ pub mod password;
pub mod pki;
pub mod userid;
pub mod types;
pub const NULL_POLICY: &NullPolicy = &NullPolicy::new();
/// Something like a User ID.

1
src/common/types/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod userid_designator;

View File

@ -0,0 +1,120 @@
use sequoia_openpgp as openpgp;
use openpgp::cert::ValidCert;
use openpgp::packet::UserID;
use crate::Result;
use crate::cli::types::UserIDDesignators;
use crate::cli::types::userid_designator::UserIDDesignator;
impl<Arguments, Options> UserIDDesignators<Arguments, Options> {
/// Resolve the user ID designators.
pub fn resolve(&self, vc: &ValidCert) -> Result<Vec<UserID>> {
// Find the matching User ID.
let mut userids = Vec::new();
// Don't stop at the first error.
let mut missing = false;
let mut bad = None;
for designator in self.iter() {
match designator {
UserIDDesignator::UserID(userid) => {
let userid = UserID::from(&userid[..]);
// If --add-userid is specified, we use the user ID as
// is. Otherwise, we make sure there is a matching
// self-signed user ID.
if self.add_userid().unwrap_or(false) {
userids.push(userid.clone());
} else if let Some(_) = vc.userids()
.find(|ua| {
ua.userid() == &userid
})
{
userids.push(userid.clone());
} else {
wprintln!("{:?} is not a self-signed user ID.",
String::from_utf8_lossy(userid.value()));
missing = true;
}
}
UserIDDesignator::Email(email) => {
// Validate the email address.
let userid = match UserID::from_address(None, None, email) {
Ok(userid) => userid,
Err(err) => {
wprintln!("{:?} is not a valid email address: {}",
email, err);
bad = Some(err);
continue;
}
};
// Extract a normalized version for comparison
// purposes.
let email_normalized = match userid.email_normalized() {
Ok(Some(email)) => email,
Ok(None) => {
wprintln!("{:?} is not a valid email address", email);
bad = Some(anyhow::anyhow!(format!(
"{:?} is not a valid email address", email)));
continue;
}
Err(err) => {
wprintln!("{:?} is not a valid email address: {}",
email, err);
bad = Some(err);
continue;
}
};
// Find any the matching self-signed user IDs.
let mut found = false;
for ua in vc.userids() {
if Some(&email_normalized)
== ua.email_normalized().unwrap_or(None).as_ref()
{
userids.push(ua.userid().clone());
found = true;
}
}
if ! found {
if self.add_userid().unwrap_or(false) {
// Add the bare email address.
userids.push(userid);
} else {
eprintln!("The email address {:?} does not match any \
user IDs.",
email);
missing = true;
}
}
}
}
}
if missing {
wprintln!("{}'s self-signed user IDs:", vc.fingerprint());
let mut have_valid = false;
for ua in vc.userids() {
if let Ok(u) = std::str::from_utf8(ua.userid().value()) {
have_valid = true;
wprintln!(" - {:?}", u);
}
}
if ! have_valid {
wprintln!(" - Certificate has no valid user IDs.");
}
wprintln!("Pass `--add-userid` to certify a user ID even if it \
isn't self signed.");
return Err(anyhow::anyhow!("Not a self-signed user ID"));
};
if let Some(err) = bad {
return Err(err);
}
Ok(userids)
}
}