db: correctly deal with user ids uploaded for already-verified email addresses
This commit is contained in:
parent
a02b5ac9ca
commit
cdda40e126
|
@ -895,6 +895,13 @@ mod tests {
|
|||
db.check_consistency().expect("inconsistent database");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_email_4() {
|
||||
let (_tmp_dir, mut db, log_path) = open_db();
|
||||
test::test_same_email_4(&mut db, &log_path);
|
||||
db.check_consistency().expect("inconsistent database");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_selfsig() {
|
||||
let (_tmp_dir, mut db, log_path) = open_db();
|
||||
|
|
|
@ -230,12 +230,7 @@ pub trait Database: Sync + Send {
|
|||
let published_tpk_old = self
|
||||
.by_fpr(&fpr_primary)
|
||||
.and_then(|bytes| Cert::from_bytes(bytes.as_bytes()).ok());
|
||||
let published_uids: Vec<UserID> = published_tpk_old
|
||||
.as_ref()
|
||||
.map(|tpk| tpk.userids()
|
||||
.map(|binding| binding.userid().clone())
|
||||
.collect()
|
||||
).unwrap_or_default();
|
||||
let published_emails = published_tpk_old.as_ref().map(|cert| tpk_get_emails(cert)).unwrap_or_default();
|
||||
|
||||
let unparsed_uids = full_tpk_new
|
||||
.userids()
|
||||
|
@ -246,21 +241,24 @@ pub trait Database: Sync + Send {
|
|||
let mut email_status: Vec<_> = full_tpk_new
|
||||
.userids()
|
||||
.bundles()
|
||||
.filter(|binding| known_uids.contains(binding.userid()))
|
||||
.flat_map(|binding| {
|
||||
let uid = binding.userid();
|
||||
if let Ok(email) = Email::try_from(uid) {
|
||||
if is_status_revoked(binding.revoked(&*POLICY, None)) {
|
||||
Some((email, EmailAddressStatus::Revoked))
|
||||
} else if !is_revoked && published_uids.contains(uid) {
|
||||
Some((email, EmailAddressStatus::Published))
|
||||
} else {
|
||||
Some((email, EmailAddressStatus::NotPublished))
|
||||
}
|
||||
.map(|binding| {
|
||||
if let Ok(email) = Email::try_from(binding.userid()) {
|
||||
Some((binding, email))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.filter(|(binding, email)| known_uids.contains(binding.userid()) || published_emails.contains(email))
|
||||
.flat_map(|(binding, email)| {
|
||||
if is_status_revoked(binding.revoked(&*POLICY, None)) {
|
||||
Some((email, EmailAddressStatus::Revoked))
|
||||
} else if !is_revoked && published_emails.contains(&email) {
|
||||
Some((email, EmailAddressStatus::Published))
|
||||
} else {
|
||||
Some((email, EmailAddressStatus::NotPublished))
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
email_status.sort();
|
||||
// EmailAddressStatus is ordered published, unpublished, revoked. if there are multiple for
|
||||
|
@ -274,7 +272,7 @@ pub trait Database: Sync + Send {
|
|||
|
||||
// If the key is revoked, consider all uids revoked
|
||||
let newly_revoked_uids: Vec<&UserID> = if is_revoked {
|
||||
published_uids.iter().collect()
|
||||
full_tpk_new.userids().bundles().map(|binding| binding.userid()).collect()
|
||||
} else {
|
||||
let revoked_uids: Vec<UserID> = full_tpk_new
|
||||
.userids()
|
||||
|
@ -283,19 +281,28 @@ pub trait Database: Sync + Send {
|
|||
.map(|binding| binding.userid().clone())
|
||||
.collect();
|
||||
|
||||
published_uids.iter()
|
||||
.filter(|uid| revoked_uids.contains(uid))
|
||||
.collect()
|
||||
published_tpk_old
|
||||
.as_ref()
|
||||
.map(|tpk| tpk
|
||||
.userids()
|
||||
.bundles()
|
||||
.map(|binding| binding.userid())
|
||||
.filter(|uid| revoked_uids.contains(uid))
|
||||
.collect()
|
||||
).unwrap_or_default()
|
||||
};
|
||||
|
||||
let published_tpk_new = tpk_filter_userids(
|
||||
&full_tpk_new, |uid| {
|
||||
published_uids.contains(uid) && !newly_revoked_uids.contains(&uid)
|
||||
if let Ok(email) = Email::try_from(uid) {
|
||||
published_emails.contains(&email) && !newly_revoked_uids.contains(&uid)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})?;
|
||||
|
||||
let newly_revoked_emails: Vec<Email> = published_uids.iter()
|
||||
.map(|uid| Email::try_from(uid).ok())
|
||||
.flatten()
|
||||
let newly_revoked_emails: Vec<&Email> = published_emails
|
||||
.iter()
|
||||
.filter(|email| {
|
||||
let has_unrevoked_userid = published_tpk_new
|
||||
.userids()
|
||||
|
@ -304,7 +311,7 @@ pub trait Database: Sync + Send {
|
|||
.map(|binding| binding.userid())
|
||||
.map(|uid| Email::try_from(uid).ok())
|
||||
.flatten()
|
||||
.any(|unrevoked_email| unrevoked_email == *email);
|
||||
.any(|unrevoked_email| &unrevoked_email == *email);
|
||||
!has_unrevoked_userid
|
||||
}).collect();
|
||||
|
||||
|
@ -675,6 +682,14 @@ pub trait Database: Sync + Send {
|
|||
fn check_consistency(&self) -> Result<()>;
|
||||
}
|
||||
|
||||
fn tpk_get_emails(cert: &Cert) -> Vec<Email> {
|
||||
cert
|
||||
.userids()
|
||||
.map(|binding| Email::try_from(binding.userid()))
|
||||
.flatten()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn tpk_get_linkable_fprs(tpk: &Cert) -> Vec<Fingerprint> {
|
||||
let ref signing_capable = KeyFlags::empty()
|
||||
.set_signing(true)
|
||||
|
|
|
@ -180,20 +180,13 @@ pub fn test_uid_verification(db: &mut impl Database, log_path: &Path) {
|
|||
|
||||
// publish w/ one uid less
|
||||
{
|
||||
let packets =
|
||||
tpk.clone().into_packet_pile().into_children().filter(|pkt| {
|
||||
match pkt {
|
||||
Packet::UserID(ref uid) => *uid != uid1,
|
||||
_ => true,
|
||||
}
|
||||
});
|
||||
let pile : PacketPile = packets.collect::<Vec<Packet>>().into();
|
||||
let short_tpk = Cert::from_packet_pile(pile).unwrap();
|
||||
let short_tpk = cert_without_uid(tpk.clone(), &uid1);
|
||||
|
||||
let tpk_status = db.merge(short_tpk).unwrap().into_tpk_status();
|
||||
assert_eq!(TpkStatus {
|
||||
is_revoked: false,
|
||||
email_status: vec!(
|
||||
(email1.clone(), EmailAddressStatus::Published),
|
||||
(email2.clone(), EmailAddressStatus::Published),
|
||||
),
|
||||
unparsed_uids: 0,
|
||||
|
@ -1013,6 +1006,48 @@ pub fn test_same_email_3(db: &mut impl Database, log_path: &Path) {
|
|||
vec![ uid2.clone() ]);
|
||||
}
|
||||
|
||||
// If a key has a verified email address, make sure newly uploaded user
|
||||
// ids with the same email are published as well.
|
||||
pub fn test_same_email_4(db: &mut impl Database, log_path: &Path) {
|
||||
let str_uid1 = "A <test@example.com>";
|
||||
let str_uid2 = "B <test@example.com>";
|
||||
let tpk = CertBuilder::new()
|
||||
.add_userid(str_uid1)
|
||||
.add_userid(str_uid2)
|
||||
.generate()
|
||||
.unwrap()
|
||||
.0;
|
||||
let uid1 = UserID::from(str_uid1);
|
||||
let uid2 = UserID::from(str_uid2);
|
||||
let email = Email::from_str(str_uid1).unwrap();
|
||||
let fpr = Fingerprint::try_from(tpk.fingerprint()).unwrap();
|
||||
|
||||
let cert_uid_1 = cert_without_uid(tpk.clone(), &uid2);
|
||||
let cert_uid_2 = cert_without_uid(tpk.clone(), &uid1);
|
||||
|
||||
// upload key
|
||||
let tpk_status = db.merge(cert_uid_1.clone()).unwrap().into_tpk_status();
|
||||
check_log_entry(log_path, &fpr);
|
||||
db.set_email_published(&fpr, &tpk_status.email_status[0].0).unwrap();
|
||||
assert_eq!(get_userids(&db.by_email(&email).unwrap()[..]),
|
||||
vec![ uid1.clone() ]);
|
||||
|
||||
let tpk_status = db.merge(cert_uid_2.clone()).unwrap().into_tpk_status();
|
||||
check_log_entry(log_path, &fpr);
|
||||
assert_eq!(TpkStatus {
|
||||
is_revoked: false,
|
||||
email_status: vec!(
|
||||
(email.clone(), EmailAddressStatus::Published),
|
||||
),
|
||||
unparsed_uids: 0,
|
||||
}, tpk_status);
|
||||
|
||||
// fetch by both user ids. We should still get both user ids.
|
||||
assert_eq!(get_userids(&db.by_email(&email).unwrap()[..]),
|
||||
vec![ uid1.clone(), uid2.clone() ]);
|
||||
}
|
||||
|
||||
|
||||
pub fn test_bad_uids(db: &mut impl Database, log_path: &Path) {
|
||||
let str_uid1 = "foo@bar.example <foo@bar.example>";
|
||||
let str_uid2 = "A <test@example.com>";
|
||||
|
@ -1083,3 +1118,15 @@ fn check_log_entry(log_path: &Path, fpr: &Fingerprint) {
|
|||
assert_eq!(last_entry, fpr.to_string());
|
||||
|
||||
}
|
||||
|
||||
fn cert_without_uid(cert: Cert, removed_uid: &UserID) -> Cert {
|
||||
let packets =
|
||||
cert.into_packet_pile().into_children().filter(|pkt| {
|
||||
match pkt {
|
||||
Packet::UserID(ref uid) => uid != removed_uid,
|
||||
_ => true,
|
||||
}
|
||||
});
|
||||
let pile : PacketPile = packets.collect::<Vec<Packet>>().into();
|
||||
Cert::from_packet_pile(pile).unwrap()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue