Extend and adjust the set of user ID designators.

- Rename the `Exact` designator semantics to `By`.  By default, the
    `By` arguments are called `--userid`, `--userid-by-email`, and
    `--userid-by-name`.

  - Add a new set of designators called `Exact` for the arguments
    `--userid`, `--email`, and `--name`.  The semantics of `Exact`
    are: the value must match a self-signed user ID, however, the
    returned user ID is just the value, not the matching self-signed
    user ID.  That is, if there is a self-signed user ID `Alice
    <alice@example.org>`, `--email alice@example.org` matches and
    returns the user ID `<alice@example.org>`, and `--name Alice`
    returns the user ID `Alice`.

  - Change the semantics of `Add` user ID designators (by default,
    `--userid-or-add`, `--email-or-add`, and `--name-or-add`) so that
    they just return a user ID with just the specified value.  That is
    `--email alice@example.org` returns the user ID
    `<alice@example.org>`.

  - The following commands use user ID designators and their semantics
    are unchanged:

    - `sq key approvals list`: Unchanged.
    - `sq key approvals update`: Unchanged.
    - `sq pki authenticate`: Unchanged.
    - `sq pki lookup`: Unchanged.
    - `sq pki path`: Unchanged.

  - The following commands use user ID designators and their semantics
    changed as follows:

    - `sq pki link add`: `--email-or-add` had the old `Add` semantics
      and now has the new `Add` semantics.

    - `sq pki link authorize`: `--email-or-add` had the old `Add`
      semantics and now has the new `Add` semantics.

    - `sq pki link retract`: `--email` had the old `Add` semantics and
      now has the new `Add` semantics.

    - `sq key userid revoke`: `--email-or-add` had the old `Add` semantics and
      now has the new `Add` semantics.

    - `sq key vouch add`: `--email-or-add` had the old `Add` semantics and
      now has the new `Add` semantics.

    - `sq key vouch authorize --email-or-add` had the old `Add`
      semantics and now has the new `Add` semantics.
This commit is contained in:
Neal H. Walfield 2024-12-12 17:05:33 +01:00
parent f9d1112735
commit c0ef0f5dbd
No known key found for this signature in database
GPG Key ID: 6863C9AD5B4D22D3
11 changed files with 372 additions and 79 deletions

View File

@ -107,7 +107,7 @@ pub struct ListCommand {
#[command(flatten)]
pub userids: UserIDDesignators<
userid_designator::ExactArgs,
userid_designator::PlainByArgs,
userid_designator::OptionalValue>,
#[clap(
@ -243,7 +243,7 @@ pub struct UpdateCommand {
#[command(flatten)]
pub userids: UserIDDesignators<
userid_designator::ExactArgs,
userid_designator::PlainByArgs,
userid_designator::OptionalValue>,
#[clap(

View File

@ -219,7 +219,7 @@ pub struct RevokeCommand {
#[command(flatten)]
pub userids: UserIDDesignators<
userid_designator::ExactAndAddArgs,
userid_designator::PlainByAndAddArgs,
userid_designator::OneValue>,
#[clap(

View File

@ -180,7 +180,7 @@ pub struct AddCommand {
#[command(flatten)]
pub userids: UserIDDesignators<
userid_designator::AllExactAndAddArgs>,
userid_designator::AllPlainByAndAddArgs>,
#[clap(
long = "amount",
@ -344,7 +344,7 @@ pub struct AuthorizeCommand {
#[command(flatten)]
pub userids: UserIDDesignators<
userid_designator::AllExactAndAddArgs>,
userid_designator::AllPlainByAndAddArgs>,
#[clap(
long = "amount",
@ -535,7 +535,7 @@ with the email address alice@example.org.",
command: &[
"sq", "pki", "link", "add",
"--cert=EB28F26E2739A4870ECC47726F0073F60FD0CBF0",
"--email=alice@example.org",
"--email-or-add=alice@example.org",
],
hide: &[],
}),

View File

@ -96,7 +96,7 @@ pub struct Command {
#[command(flatten)]
pub userids: UserIDDesignators<
userid_designator::AllExactAndAddArgs>,
userid_designator::AllPlainByAndAddArgs>,
#[clap(
long = "amount",

View File

@ -106,7 +106,7 @@ pub struct Command {
#[command(flatten)]
pub userids: UserIDDesignators<
userid_designator::AllExactAndAddArgs>,
userid_designator::AllPlainByAndAddArgs>,
#[clap(
long = "amount",

View File

@ -14,35 +14,67 @@ pub type AllUserIDsArg = typenum::U1;
/// Adds `--userid` `--email`, and `--name` arguments.
///
/// For `UserIDDesignator::resolve`, the value must match on a
/// self-signed user ID, and returns the value as is.
/// self-signed user ID, but returns the value as is. That is, if
/// there is a self-signed user ID "Alice <alice@example.org>",
/// "--email alice@example.org" matches and returns the user ID
/// "<alice@example.org>".
pub type ExactArgs = typenum::U2;
/// Adds `--userid`, `--userid-by-email`, and `--userid-by-name`
/// arguments.
///
/// For `UserIDDesignator::resolve`, the value must correspond to a
/// self-signed user ID. That is, if there is a self-signed user ID
/// "Alice <alice@example.org>", "--email alice@example.org" matches
/// and returns the matching user ID, i.e., "Alice
/// <alice@example.org>".
pub type ByArgs = typenum::U4;
/// Adds a `--userid-or-add`, `--email-or-add`, and `--name-or-add`
/// argument.
///
/// For `UserIDDesignator::resolve`, acts like `ExactArgs`, but if
/// there is no matching self-signed user ID, creates one from the
/// value.
pub type AddArgs = typenum::U4;
pub type AddArgs = typenum::U8;
/// Requires ByArgs, conflicts with ExactArgs. Renames ByArgs'
/// arguments to `--userid`, `--email`, and `--name`.
pub type PlainIsBy = typenum::U16;
/// Requires AddArgs, conflicts with ExactArgs. Renames AddArgs'
/// arguments to `--userid`, `--email`, and `--name`.
pub type PlainIsAdd = typenum::U8;
pub type PlainIsAdd = typenum::U32;
pub type PlainByArgs
= <ByArgs as std::ops::BitOr<PlainIsBy>>::Output;
pub type PlainAddArgs
= <AddArgs as std::ops::BitOr<PlainIsAdd>>::Output;
pub type AllPlainAddArgs
= <AllUserIDsArg as std::ops::BitOr<PlainAddArgs>>::Output;
pub type ExactAndAddArgs
= <<ExactArgs as std::ops::BitOr<AddArgs>>::Output
pub type PlainByAndAddArgs
= <<PlainIsBy as std::ops::BitOr<ByArgs>>::Output
as std::ops::BitOr<AddArgs>>::Output;
pub type AllExactAndAddArgs
= <<AllUserIDsArg as std::ops::BitOr<ExactAndAddArgs>>::Output
as std::ops::BitOr<AddArgs>>::Output;
pub type AllPlainByAndAddArgs
= <AllUserIDsArg as std::ops::BitOr<PlainByAndAddArgs>>::Output;
#[cfg(test)]
pub type ExactAndAddArgs
= <ExactArgs as std::ops::BitOr<AddArgs>>::Output;
#[cfg(test)]
pub type ExactByAndAddArgs
= <ByArgs as std::ops::BitOr<ExactAndAddArgs>>::Output;
#[cfg(test)]
pub type AllExactByAndAddArgs
= <AllUserIDsArg as std::ops::BitOr<ExactByAndAddArgs>>::Output;
/// Argument parser options.
@ -94,7 +126,7 @@ impl Documentation for SelfSignedDocumentation {
use UserIDDesignatorType::*;
use UserIDDesignatorSemantics::*;
match (typ, semantics) {
(UserID, Exact) => {
(UserID, Exact | By) => {
("Use the specified self-signed user ID",
Some("\
Use the specified self-signed user ID
@ -119,36 +151,40 @@ you need to use this option to explicitly opt in.")
})
}
(Email, Exact) => {
("\
Use a user ID consisting of just the email address, if the email address \
occurs in a self-signed user ID",
None)
}
(Email, By) => {
("Use the self-signed user ID with the specified email address",
None)
}
(Email, Add) => {
("\
Use the self-signed user ID with the specified email address or use a \
new user ID",
("Use a user ID with the specified email address",
Some("\
Use the self-signed user ID with the specified email address or use a \
new user ID
Use a user ID with the specified email address
This first searches for a matching self-signed user ID. If there is \
no self-signed user ID with the specified email, it uses \
a new user ID with the specified email address, and no display name."))
The user ID consists of just the email address. The email address does not \
have to appear in a self-signed user ID."))
}
(Name, Exact) => {
("\
Use a user ID consisting of just the display name, if the display name \
occurs in a self-signed user ID",
None)
}
(Name, By) => {
("Use the self-signed user ID with the specified display name",
None)
}
(Name, Add) => {
("\
Use the self-signed user ID with the specified display name or use a \
new user ID",
("Use a user ID with the specified display name",
Some("\
Use the self-signed user ID with the specified display name or use a \
new user ID
Use a user ID with the specified display name
This first searches for a matching self-signed user ID. If there is \
no self-signed user ID with the specified name, it uses a new user ID \
with the specified display name, and no email address."))
The user ID consists of just the display named. The display name does not \
have to appear in a self-signed user ID."))
}
}
}
@ -202,13 +238,20 @@ pub enum UserIDDesignatorType {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum UserIDDesignatorSemantics {
/// For `UserIDDesignator::resolve`, the value must match on a
/// self-signed user ID and returns the self-signed user ID. That
/// is, if there is a self-signed user ID "Alice
/// <alice@example.org>", "--email alice@example.org" matches and
/// returns the user ID "Alice <alice@example.org>".
/// self-signed user ID, but returns the value. That is, if there
/// is a self-signed user ID "Alice <alice@example.org>", "--email
/// alice@example.org" matches and returns the user ID
/// "<alice@example.org>".
Exact,
/// For `UserIDDesignator::resolve`, acts like `ExactArgs`, but if
/// For `UserIDDesignator::resolve`, the value must correspond to
/// a self-signed user ID. That is, if there is a self-signed
/// user ID "Alice <alice@example.org>", "--email
/// alice@example.org" matches and returns the matching user ID,
/// i.e., "Alice <alice@example.org>".
By,
/// `UserIDDesignator::resolve`, acts like `ExactArgs`, but if
/// there is no matching self-signed user ID, creates one from the
/// value.
Add,
@ -221,6 +264,11 @@ impl UserIDDesignatorSemantics {
matches!(self, &UserIDDesignatorSemantics::Exact)
}
/// If `self` is `UserIDDesignatorSemantics::By`.
fn is_by(&self) -> bool {
matches!(self, &UserIDDesignatorSemantics::By)
}
/// If `self` is `UserIDDesignatorSemantics::Add`.
fn is_add(&self) -> bool {
matches!(self, &UserIDDesignatorSemantics::Add)
@ -287,6 +335,11 @@ impl UserIDDesignator {
self.semantics().is_exact()
}
/// If `self` is `UserIDDesignatorSemantics::By`.
fn is_by(&self) -> bool {
self.semantics().is_by()
}
/// If `self` is `UserIDDesignatorSemantics::Add`.
pub fn is_add(&self) -> bool {
self.semantics().is_add()
@ -436,13 +489,21 @@ where
let arguments = Arguments::to_usize();
let all_arg = (arguments & AllUserIDsArg::to_usize()) > 0;
let exact_args = (arguments & ExactArgs::to_usize()) > 0;
let by_args = (arguments & ByArgs::to_usize()) > 0;
let add_args = (arguments & AddArgs::to_usize()) > 0;
let plain_is_by = (arguments & PlainIsBy::to_usize()) > 0;
let plain_is_add = (arguments & PlainIsAdd::to_usize()) > 0;
// Can't use PlainIsAdd with ExactArgs.
// Can't use PlainIsBy or PlainIsAdd with ExactArgs or with
// each other.
assert!(! (exact_args && plain_is_by));
assert!(! (exact_args && plain_is_add));
assert!(! (plain_is_by && plain_is_add));
// If plain_is_xxx is set, then by_xxx must be set.
if plain_is_by {
assert!(by_args);
}
if plain_is_add {
assert!(add_args);
}
@ -505,10 +566,10 @@ Use all self-signed user IDs"));
use UserIDDesignatorType::*;
use UserIDDesignatorSemantics::*;
if exact_args {
if exact_args || plain_is_by {
let full_name = "userid";
let (help, long_help) = Docs::help(
UserID, true, Exact);
UserID, true, if plain_is_by { By } else { Exact });
let mut arg = clap::Arg::new(&full_name)
.long(&full_name)
@ -550,6 +611,65 @@ Use all self-signed user IDs"));
arg_group = arg_group.arg(full_name);
}
if by_args {
let full_name = if plain_is_by {
"email"
} else {
"userid-by-email"
};
let (help, long_help) = Docs::help(Email, plain_is_by, By);
let mut arg = clap::Arg::new(&full_name)
.long(&full_name)
.value_name("EMAIL")
.value_parser(parse_as_email)
.action(action.clone())
.help(help);
if let Some(long_help) = long_help {
arg = arg.long_help(long_help);
}
if all_arg {
arg = arg.conflicts_with("all");
}
cmd = cmd.arg(arg);
arg_group = arg_group.arg(full_name);
}
// plain_is_by is handled below. This improves the ordering.
let render_by_name = |mut cmd: clap::Command,
mut arg_group: clap::ArgGroup|
{
let full_name = if plain_is_by {
"name"
} else {
"userid-by-name"
};
let (help, long_help) = Docs::help(Name, plain_is_by, By);
let mut arg = clap::Arg::new(&full_name)
.long(&full_name)
.value_name("DISPLAY_NAME")
.action(action.clone())
.help(help);
if let Some(long_help) = long_help {
arg = arg.long_help(long_help);
}
if all_arg {
arg = arg.conflicts_with("all");
}
cmd = cmd.arg(arg);
arg_group = arg_group.arg(full_name);
(cmd, arg_group)
};
if by_args && ! plain_is_by {
(cmd, arg_group) = render_by_name(cmd, arg_group);
}
if exact_args {
let full_name = "email";
let (help, long_help) = Docs::help(Email, true, Exact);
@ -613,6 +733,10 @@ Use all self-signed user IDs"));
arg_group = arg_group.arg(full_name);
}
if by_args && plain_is_by {
(cmd, arg_group) = render_by_name(cmd, arg_group);
}
if add_args {
let full_name = if plain_is_add {
"name"
@ -677,13 +801,21 @@ where
let arguments = Arguments::to_usize();
let all_arg = (arguments & AllUserIDsArg::to_usize()) > 0;
let exact_args = (arguments & ExactArgs::to_usize()) > 0;
let by_args = (arguments & ByArgs::to_usize()) > 0;
let add_args = (arguments & AddArgs::to_usize()) > 0;
let plain_is_by = (arguments & PlainIsBy::to_usize()) > 0;
let plain_is_add = (arguments & PlainIsAdd::to_usize()) > 0;
// Can't use PlainIsAdd with ExactArgs or with each other.
// Can't use PlainIsBy or PlainIsAdd with ExactArgs or with
// each other.
assert!(! (exact_args && plain_is_by));
assert!(! (exact_args && plain_is_add));
assert!(! (plain_is_by && plain_is_add));
// If plain_is_xxx is set, then by_xxx must be set.
if plain_is_by {
assert!(by_args);
}
if plain_is_add {
assert!(add_args);
}
@ -728,6 +860,35 @@ where
}
}
if by_args {
if plain_is_by {
if let Ok(Some(userids)) = matches.try_get_many::<String>("userid") {
for userid in userids.cloned() {
designators.push(
UserIDDesignator::UserID(By, userid));
}
}
}
if let Ok(Some(emails))
= matches.try_get_many::<String>(
if plain_is_by { "email" } else { "userid-by-email" })
{
for email in emails.cloned() {
designators.push(
UserIDDesignator::Email(By ,email));
}
}
if let Ok(Some(names))
= matches.try_get_many::<String>(
if plain_is_by { "name" } else { "userid-by-name" })
{
for name in names.cloned() {
designators.push(
UserIDDesignator::Name(By, name));
}
}
}
if add_args {
if let Ok(Some(userids))
= matches.try_get_many::<String>(
@ -866,7 +1027,7 @@ mod test {
macro_rules! check {
($t:ty,
$plain:expr, $add:expr) =>
$plain:expr, $by:expr, $add:expr) =>
{{
#[derive(Parser, Debug)]
#[clap(name = "prog")]
@ -930,6 +1091,36 @@ mod test {
assert!(m.is_err());
}
// Check if --userid-by-email is recognized.
let m = command.clone().try_get_matches_from(vec![
"prog",
"--userid-by-email", "alice@example.org",
"--userid-by-email", "bob@example.org",
]);
if $by {
let m = m.expect("valid arguments");
let c = CLI::from_arg_matches(&m).expect("ok");
assert_eq!(c.userids.designators.len(), 2);
assert!(c.userids.designators.iter().all(|d| d.is_by()));
} else {
assert!(m.is_err());
}
// Check if --userid-by-name is recognized.
let m = command.clone().try_get_matches_from(vec![
"prog",
"--userid-by-name", "alice",
"--userid-by-name", "bob",
]);
if $by {
let m = m.expect("valid arguments");
let c = CLI::from_arg_matches(&m).expect("ok");
assert_eq!(c.userids.designators.len(), 2);
assert!(c.userids.designators.iter().all(|d| d.is_by()));
} else {
assert!(m.is_err());
}
// Either --email is unknown, or the --email's value
// is invalid.
let m = command.clone().try_get_matches_from(vec![
@ -986,14 +1177,19 @@ mod test {
}
use UserIDDesignatorSemantics::*;
// plain, add
check!(typenum::U0, None, false);
check!(ExactArgs, Exact, false);
check!(AddArgs, None, true);
check!(PlainAddArgs, Add, false);
check!(AllPlainAddArgs, Add, false);
check!(ExactAndAddArgs, Exact, true);
check!(AllExactAndAddArgs, Exact, true);
// plain, by, add
check!(typenum::U0, None, false, false);
check!(ExactArgs, Exact, false, false);
check!(ByArgs, None, true, false);
check!(AddArgs, None, false, true);
check!(PlainByArgs, By, false, false);
check!(PlainAddArgs, Add, false, false);
check!(AllPlainAddArgs, Add, false, false);
check!(PlainByAndAddArgs, By, false, true);
check!(AllPlainByAndAddArgs, By, false, true);
check!(ExactAndAddArgs, Exact, false, true);
check!(ExactByAndAddArgs, Exact, true, true);
check!(AllExactByAndAddArgs, Exact, true, true);
}
#[test]
@ -1107,7 +1303,7 @@ mod test {
#[clap(name = "prog")]
struct CLI {
#[command(flatten)]
pub userids: UserIDDesignators<AllExactAndAddArgs>,
pub userids: UserIDDesignators<AllExactByAndAddArgs>,
}
let command = CLI::command();
@ -1135,6 +1331,8 @@ mod test {
// Can't combine --all with any other designator.
for (arg, value) in &[
("--userid", "foo"),
("--userid-by-email", "foo@example.org"),
("--userid-by-name", "foo"),
("--userid-or-add", "foo"),
("--email", "foo@example.org"),
("--email-or-add", "foo@example.org"),

View File

@ -169,20 +169,26 @@ where
ambiguous_email = true;
}
if semantics == &Exact || semantics == &Add {
found = true;
if semantics == &By {
userids.push(designator.clone()
.resolve_to(ua.userid().clone()));
} else {
userids.push(designator.clone()
.resolve_to(email_userid.clone()));
// Since we're not returning the
// matching self-signed user ID, we
// don't need to worry about ambiguous
// matches.
break;
}
found = true;
}
}
if ! found {
match semantics {
Exact => {
Exact | By => {
eprintln!("None of the self-signed user IDs \
are for the email address {:?}.",
email);
@ -221,21 +227,27 @@ where
ambiguous_name = true;
}
if semantics == &Exact || semantics == &Add {
found = true;
if semantics == &By {
userids.push(designator.clone()
.resolve_to(ua.userid().clone()));
} else {
userids.push(designator.clone()
.resolve_to(name_userid.clone()));
// Since we're not returning the
// matching self-signed user ID, we
// don't need to worry about ambiguous
// matches.
break;
}
found = true;
}
}
}
if ! found {
match semantics {
Exact => {
Exact | By => {
eprintln!("None of the self-signed user IDs \
are for the display name {:?}.",
name);

View File

@ -235,6 +235,7 @@ pub enum UserIDArg<'a> {
Name(&'a str),
AddUserID(&'a str),
AddEmail(&'a str),
ByEmail(&'a str),
}
impl<'a> From<&'a str> for UserIDArg<'a> {
@ -263,7 +264,8 @@ impl UserIDArg<'_> {
| UserIDArg::Email(s)
| UserIDArg::Name(s)
| UserIDArg::AddUserID(s)
| UserIDArg::AddEmail(s) =>
| UserIDArg::AddEmail(s)
| UserIDArg::ByEmail(s) =>
{
s
}
@ -283,6 +285,8 @@ impl UserIDArg<'_> {
cmd.arg("--userid-or-add").arg(userid),
UserIDArg::AddEmail(email) =>
cmd.arg("--email-or-add").arg(email),
UserIDArg::ByEmail(email) =>
cmd.arg("--userid-by-email").arg(email),
};
}
@ -302,6 +306,8 @@ impl UserIDArg<'_> {
unreachable!(),
UserIDArg::AddEmail(email) =>
unreachable!(),
UserIDArg::ByEmail(userid) =>
unreachable!(),
};
}
}

View File

@ -320,9 +320,7 @@ fn userid_designators() {
UserIDArg::Email(other_email)).is_err());
revocations(&sq, cert.key_handle(), other_userid, 0);
// 4. --email-or-add: use the self-signed user ID with the
// specified email address, or use a user ID with the email
// address.
// 4. --email-or-add: use a user ID with the email address.
let (cert, fpr, sq) = setup();
// Self-signed and authenticated.
@ -330,9 +328,10 @@ fn userid_designators() {
&[], &fpr, UserIDArg::UserID(self_signed_userid)).is_ok());
assert!(revoke(&sq, cert.key_handle(),
UserIDArg::AddEmail(self_signed_email)).is_ok());
revocations(&sq, cert.key_handle(), self_signed_userid, 1);
revocations(&sq, cert.key_handle(), self_signed_userid, 0);
revocations(&sq, cert.key_handle(), &format!("<{}>", self_signed_email), 1);
assert!(sq.pki_authenticate(
&[], &fpr, UserIDArg::UserID(self_signed_userid)).is_err());
&[], &fpr, UserIDArg::UserID(&format!("<{}>", self_signed_email))).is_err());
// Authenticated, but not self-signed.
assert!(sq.pki_authenticate(

View File

@ -785,15 +785,18 @@ fn no_ambiguous_email() {
sq.tick(1);
// Ambiguous.
// --email links the matching self-signed user ID: Ambiguous is
// not allowed.
assert!(
sq.pki_link_add_maybe(
&[], alice.key_handle(), &[UserIDArg::Email("alice@example.org")])
.is_err());
// --email-or-add links a user ID with the email address:
// Ambiguous is allowed.
assert!(
sq.pki_link_add_maybe(
&[], alice.key_handle(), &[UserIDArg::AddEmail("alice@example.org")])
.is_err());
.is_ok());
// Not a self-signed user ID.
assert!(
@ -806,7 +809,7 @@ fn no_ambiguous_email() {
&[], alice.key_handle(),
&[UserIDArg::UserID("Alice <alice@example.org>")]);
// As well as adding as a user ID.
// As well as adding a user ID.
sq.pki_link_add(
&[], alice.key_handle(),
&[UserIDArg::AddUserID("<alice@example.org>")]);
@ -883,7 +886,12 @@ fn link_userid_designators() {
let mut sq = Sq::new();
let (cert, cert_path, _rev_path) = sq.key_generate(
&[], &["Alice <alice@example.org>", "Alice <alice@an.org>" ]);
&[],
&[
"Alice <alice@example.org>",
"Alice <alice@an.org>",
"Alice <alice@third.org>",
]);
let fpr = cert.fingerprint().to_string();
sq.key_import(cert_path);
@ -938,6 +946,19 @@ fn link_userid_designators() {
cert.key_handle(), UserIDArg::AddEmail("alice@example.com"));
assert!(sq.pki_authenticate(
&[], &fpr, UserIDArg::UserID("<alice@example.com>")).is_ok());
// Use --email-or-add to link "<alice@third.org>", which is
// part of the self signed user ID "Alice <alice@third.org>".
// This should link "<alice@third.org>", not the self-signed
// user ID.
link(&mut sq,
cert.key_handle(), UserIDArg::AddEmail("alice@third.org"));
assert!(sq.pki_authenticate(
&[], &fpr, UserIDArg::UserID("<alice@third.org>")).is_ok());
if ! authorize {
assert!(sq.pki_authenticate(
&[], &fpr, UserIDArg::UserID("Alice <alice@third.org>")).is_err());
}
}
}
@ -947,7 +968,11 @@ fn link_retract_userid_designators() {
let mut sq = Sq::new();
let (cert, cert_path, _rev_path) = sq.key_generate(
&[], &["Alice <alice@example.org>" ]);
&[],
&[
"Alice <alice@example.org>",
"<alice@some.org>",
]);
let fpr = cert.fingerprint().to_string();
sq.key_import(cert_path);
@ -962,6 +987,12 @@ fn link_retract_userid_designators() {
assert!(sq.pki_authenticate(
&[], &fpr, UserIDArg::UserID("Alice <alice@example.org>")).is_ok());
// We can't retract using --email: it retracts "<alice@example.org>".
sq.tick(1);
assert!(sq.pki_link_retract_maybe(
&[], cert.key_handle(),
&[ UserIDArg::Email("alice@example.org") ]).is_err());
sq.tick(1);
sq.pki_link_retract(&[], cert.key_handle(),
&[ UserIDArg::UserID("Alice <alice@example.org>") ]);
@ -976,16 +1007,20 @@ fn link_retract_userid_designators() {
assert!(sq.pki_authenticate(
&[], &fpr, UserIDArg::UserID("Alice <alice@example.com>")).is_ok());
// We can't retract using --email: it retracts "<alice@example.com>".
sq.tick(1);
assert!(sq.pki_link_retract_maybe(
&[], cert.key_handle(),
&[ UserIDArg::Email("alice@example.com") ]).is_err());
// But we can retract using --user "Alice <alice@example.com>".
sq.pki_link_retract(&[], cert.key_handle(),
&[ UserIDArg::UserID("Alice <alice@example.com>") ]);
assert!(sq.pki_authenticate(
&[], &fpr, UserIDArg::UserID("Alice <alice@example.com>")).is_err());
// 2. Retract using "--email". The email address must be part of
// a self-signed user ID, but it uses a user ID with just the email
// address.
// 2. Retract using "--email". It uses a user ID with just the
// email address.
// Link "Alice <alice@example.org>", which is a self signed user
// ID.
@ -998,12 +1033,38 @@ fn link_retract_userid_designators() {
assert!(sq.pki_authenticate(
&[], &fpr, UserIDArg::UserID("<alice@example.org>")).is_err());
// We can't unlink it using --email, because that doesn't match on
// self-signed user IDs.
sq.tick(1);
sq.pki_link_retract(&[], cert.key_handle(),
&[ UserIDArg::Email("alice@example.org") ]);
assert!(sq.pki_link_retract_maybe(
&[], cert.key_handle(),
&[ UserIDArg::Email("alice@example.org") ]).is_err());
assert!(sq.pki_authenticate(
&[], &fpr, UserIDArg::UserID("Alice <alice@example.org>")).is_ok());
assert!(sq.pki_link_retract_maybe(
&[], cert.key_handle(),
&[ UserIDArg::UserID("Alice <alice@example.org>") ]).is_ok());
assert!(sq.pki_authenticate(
&[], &fpr, UserIDArg::UserID("Alice <alice@example.org>")).is_err());
// Link "<alice@some.org>", which is a self signed user ID.
sq.tick(1);
sq.pki_link_add(
&[], cert.key_handle(),
&[ UserIDArg::UserID("<alice@some.org>") ]);
assert!(sq.pki_authenticate(
&[], &fpr, UserIDArg::UserID("<alice@some.org>")).is_ok());
// We can unlink it using --email: that matchs on self-signed user
// IDs.
sq.tick(1);
assert!(sq.pki_link_retract_maybe(
&[], cert.key_handle(),
&[ UserIDArg::Email("alice@some.org") ]).is_ok());
assert!(sq.pki_authenticate(
&[], &fpr, UserIDArg::UserID("<alice@some.org>")).is_err());
// Link "<alice@example.com>", which is not part of a self signed
// user ID.
sq.tick(1);
@ -1069,9 +1130,10 @@ fn retract() {
&[ UserIDArg::UserID("Alice <alice@example.org>") ]);
sq.tick(1);
sq.pki_link_retract(
// --email => "<alice@example.org>", which was not linked.
assert!(sq.pki_link_retract_maybe(
&[], cert.key_handle(),
&[ UserIDArg::Email("alice@example.org") ]);
&[ UserIDArg::Email("alice@example.org") ]).is_err());
}
sq.pki_link_add(
@ -1084,8 +1146,7 @@ fn retract() {
&[], cert.key_handle(),
&[ UserIDArg::UserID("Alice <alice@example.com>") ]);
// We can't name "Alice <alice@example.com>" by email, because
// it is not self-signed.
// --email => "<alice@example.com>", which was not linked.
sq.tick(1);
assert!(sq.pki_link_retract_maybe(
&[], cert.key_handle(),

View File

@ -545,7 +545,11 @@ fn userid_designators() {
};
let (cert, cert_path, _rev_path) = sq.key_generate(
&[], &["Alice <alice@example.org>", "Alice <alice@an.org>" ]);
&[], &[
"Alice <alice@example.org>",
"Alice <alice@an.org>",
"Alice <alice@third.org>",
]);
let fpr = cert.fingerprint().to_string();
sq.key_import(cert_path);
@ -602,5 +606,18 @@ fn userid_designators() {
cert.key_handle(), UserIDArg::AddEmail("alice@example.com"));
assert!(sq.pki_authenticate(
&[], &fpr, UserIDArg::UserID("<alice@example.com>")).is_ok());
// Use --email-or-add to link "<alice@third.org>", which is
// part of the self signed user ID "Alice <alice@third.org>".
// This should link "<alice@third.org>", not the self-signed
// user ID.
vouch(&mut sq,
cert.key_handle(), UserIDArg::AddEmail("alice@third.org"));
assert!(sq.pki_authenticate(
&[], &fpr, UserIDArg::UserID("<alice@third.org>")).is_ok());
if ! authorize {
assert!(sq.pki_authenticate(
&[], &fpr, UserIDArg::UserID("Alice <alice@third.org>")).is_err());
}
}
}