rewrite fs database, step 2
This commit is contained in:
parent
5861ff0870
commit
d17d52ae61
|
@ -234,7 +234,7 @@ impl Filesystem {
|
|||
let mut tpks = HashMap::new();
|
||||
|
||||
// Check Fingerprints.
|
||||
for entry in fs::read_dir(&self.links_dir_by_fingerprint)? {
|
||||
for entry in fs::read_dir(&self.keys_dir_published)? {
|
||||
let prefix = entry?;
|
||||
let prefix_path = prefix.path();
|
||||
if ! prefix_path.is_dir() {
|
||||
|
@ -255,15 +255,8 @@ impl Filesystem {
|
|||
let primary_fp = match () {
|
||||
_ if typ.is_file() =>
|
||||
fp.clone(),
|
||||
_ if typ.is_symlink() =>
|
||||
self.path_to_fingerprint_base(path.parent().unwrap(),
|
||||
&path.read_link()?)
|
||||
.ok_or_else(
|
||||
|| format_err!("Malformed path: {:?}",
|
||||
path.read_link().unwrap()))?,
|
||||
_ => return
|
||||
Err(format_err!("{:?} is neither a file nor a symlink \
|
||||
but a {:?}", path, typ)),
|
||||
Err(format_err!("{:?} is not a file but a {:?}", path, typ)),
|
||||
};
|
||||
|
||||
// Load into cache.
|
||||
|
@ -277,7 +270,6 @@ impl Filesystem {
|
|||
}
|
||||
let tpk = tpks.get(&primary_fp).unwrap();
|
||||
|
||||
if typ.is_file() {
|
||||
let tpk_primary_fp =
|
||||
tpk.primary().fingerprint().try_into().unwrap();
|
||||
if fp != tpk_primary_fp {
|
||||
|
@ -286,16 +278,47 @@ impl Filesystem {
|
|||
but found {}",
|
||||
path, fp, tpk_primary_fp));
|
||||
}
|
||||
} else {
|
||||
let mut found = false;
|
||||
for skb in tpk.subkeys() {
|
||||
if Fingerprint::try_from(skb.subkey().fingerprint())
|
||||
.unwrap() == fp
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check subkeys
|
||||
for entry in fs::read_dir(&self.links_dir_by_fingerprint)? {
|
||||
let prefix = entry?;
|
||||
let prefix_path = prefix.path();
|
||||
if ! prefix_path.is_dir() {
|
||||
return Err(format_err!("{:?} is not a directory", prefix_path));
|
||||
}
|
||||
|
||||
for entry in fs::read_dir(prefix_path)? {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
let typ = fs::symlink_metadata(&path)?.file_type();
|
||||
|
||||
// The KeyID corresponding with this path.
|
||||
let fp = self.path_to_fingerprint(&path)
|
||||
.ok_or_else(|| format_err!("Malformed path: {:?}", path))?;
|
||||
|
||||
// Compute the corresponding primary fingerprint just
|
||||
// by looking at the paths.
|
||||
let primary_fp = match () {
|
||||
_ if typ.is_symlink() =>
|
||||
self.path_to_fingerprint(&path.read_link()?)
|
||||
.ok_or_else(
|
||||
|| format_err!("Malformed path: {:?}",
|
||||
path.read_link().unwrap()))?,
|
||||
_ => return
|
||||
Err(format_err!("{:?} is not a symlink but a {:?}",
|
||||
path, typ)),
|
||||
};
|
||||
|
||||
let tpk = tpks.get(&primary_fp)
|
||||
.ok_or_else(
|
||||
|| format_err!("Broken symlink {:?}: No such Key {}",
|
||||
path, primary_fp))?;
|
||||
|
||||
let found = tpk.keys_all()
|
||||
.map(|(_, _, key)| Fingerprint::try_from(key.fingerprint()).unwrap())
|
||||
.any(|key_fp| key_fp == fp);
|
||||
if ! found {
|
||||
return Err(format_err!(
|
||||
"{:?} points to the wrong TPK, the TPK does not \
|
||||
|
@ -303,7 +326,6 @@ impl Filesystem {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check KeyIDs.
|
||||
for entry in fs::read_dir(&self.links_dir_by_keyid)? {
|
||||
|
@ -340,14 +362,9 @@ impl Filesystem {
|
|||
|| format_err!("Broken symlink {:?}: No such Key {}",
|
||||
path, primary_fp))?;
|
||||
|
||||
let mut found = false;
|
||||
for (_, _, key) in tpk.keys_all() {
|
||||
if KeyID::try_from(key.fingerprint()).unwrap() == id
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let found = tpk.keys_all()
|
||||
.map(|(_, _, key)| KeyID::try_from(key.fingerprint()).unwrap())
|
||||
.any(|key_fp| key_fp == id);
|
||||
if ! found {
|
||||
return Err(format_err!(
|
||||
"{:?} points to the wrong TPK, the TPK does not \
|
||||
|
@ -465,7 +482,8 @@ impl Database for Filesystem {
|
|||
|
||||
fn check_link_fpr(&self, fpr: &Fingerprint, fpr_target: &Fingerprint) -> Result<Option<Fingerprint>> {
|
||||
let link = self.link_by_fingerprint(&fpr);
|
||||
let target = self.fingerprint_to_path_published(&fpr_target);
|
||||
let target = diff_paths(&self.fingerprint_to_path_published(fpr),
|
||||
link.parent().unwrap()).unwrap();
|
||||
|
||||
if link == target {
|
||||
return Ok(None);
|
||||
|
@ -474,10 +492,6 @@ impl Database for Filesystem {
|
|||
Ok(Some(fpr.clone()))
|
||||
}
|
||||
|
||||
fn ensure_link_fpr(&self, _fpr: &Fingerprint, _target: &Fingerprint) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update(
|
||||
&self, fpr: &Fingerprint, new: Option<String>,
|
||||
) -> Result<()> {
|
||||
|
@ -556,17 +570,15 @@ impl Database for Filesystem {
|
|||
|
||||
if path.exists() {
|
||||
let x = diff_paths(&path, &self.keys_dir).expect("related paths");
|
||||
println!("YEAP: {:?}", &x);
|
||||
Some(x)
|
||||
} else {
|
||||
println!("NOPE");
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn link_email(&self, email: &Email, fpr: &Fingerprint) -> Result<()> {
|
||||
let link = self.link_by_email(&email);
|
||||
let target = diff_paths(&self.link_by_fingerprint(fpr),
|
||||
let target = diff_paths(&self.fingerprint_to_path_published(fpr),
|
||||
link.parent().unwrap()).unwrap();
|
||||
|
||||
if link == target {
|
||||
|
@ -581,7 +593,7 @@ impl Database for Filesystem {
|
|||
|
||||
match read_link(&link) {
|
||||
Ok(target) => {
|
||||
let expected = diff_paths(&self.link_by_fingerprint(fpr),
|
||||
let expected = diff_paths(&self.fingerprint_to_path_published(fpr),
|
||||
link.parent().unwrap()).unwrap();
|
||||
|
||||
if target == expected {
|
||||
|
@ -596,7 +608,7 @@ impl Database for Filesystem {
|
|||
|
||||
fn link_kid(&self, kid: &KeyID, fpr: &Fingerprint) -> Result<()> {
|
||||
let link = self.link_by_keyid(kid);
|
||||
let target = diff_paths(&self.link_by_fingerprint(fpr),
|
||||
let target = diff_paths(&self.fingerprint_to_path_published(fpr),
|
||||
link.parent().unwrap()).unwrap();
|
||||
|
||||
if link == target {
|
||||
|
@ -622,7 +634,7 @@ impl Database for Filesystem {
|
|||
|
||||
match read_link(&link) {
|
||||
Ok(target) => {
|
||||
let expected = self.link_by_fingerprint(fpr);
|
||||
let expected = self.fingerprint_to_path_published(fpr);
|
||||
|
||||
if target == expected {
|
||||
remove_file(link)?;
|
||||
|
@ -635,12 +647,8 @@ impl Database for Filesystem {
|
|||
}
|
||||
|
||||
fn link_fpr(&self, from: &Fingerprint, fpr: &Fingerprint) -> Result<()> {
|
||||
if from == fpr {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let link = self.link_by_fingerprint(from);
|
||||
let target = diff_paths(&self.link_by_fingerprint(fpr),
|
||||
let target = diff_paths(&self.fingerprint_to_path_published(fpr),
|
||||
link.parent().unwrap()).unwrap();
|
||||
|
||||
symlink(&target, ensure_parent(&link)?)
|
||||
|
@ -651,7 +659,7 @@ impl Database for Filesystem {
|
|||
|
||||
match read_link(&link) {
|
||||
Ok(target) => {
|
||||
let expected = self.link_by_fingerprint(fpr);
|
||||
let expected = self.fingerprint_to_path_published(fpr);
|
||||
|
||||
if target == expected {
|
||||
remove_file(link)?;
|
||||
|
|
|
@ -32,6 +32,7 @@ use openpgp::{
|
|||
TPK,
|
||||
tpk::UserIDBinding,
|
||||
PacketPile,
|
||||
RevocationStatus,
|
||||
armor::{Writer, Kind},
|
||||
packet::{UserID, Tag},
|
||||
parse::Parse,
|
||||
|
@ -278,22 +279,24 @@ pub trait Database: Sync + Send {
|
|||
/// 4. Move full and published temporary TPK to their location
|
||||
/// 5. Update all symlinks
|
||||
/// UNLOCK
|
||||
fn merga(&self, new_tpk: TPK) -> Result<()> {
|
||||
fn merge(&self, new_tpk: TPK) -> Result<Vec<(Email, String)>> {
|
||||
let fpr_primary = Fingerprint::try_from(new_tpk.primary().fingerprint())?;
|
||||
|
||||
let full_tpk_new = if let Some(bytes) = self.by_fpr_full(&fpr_primary) {
|
||||
let full_tpk_old = TPK::from_bytes(bytes.as_ref())?;
|
||||
let full_tpk_new = new_tpk.merge(full_tpk_old.clone())?;
|
||||
// Abort if no changes were made
|
||||
if full_tpk_new == full_tpk_old {
|
||||
return Ok(())
|
||||
}
|
||||
// TODO
|
||||
// if full_tpk_new == full_tpk_old {
|
||||
// return Ok(vec!())
|
||||
// }
|
||||
full_tpk_new
|
||||
} else {
|
||||
new_tpk
|
||||
};
|
||||
|
||||
let published_tpk_new = {
|
||||
let is_revoked = full_tpk_new.revoked(None) != RevocationStatus::NotAsFarAsWeKnow;
|
||||
|
||||
let published_uids: Vec<UserID> = self
|
||||
.by_fpr(&fpr_primary)
|
||||
.and_then(|bytes| TPK::from_bytes(bytes.as_ref()).ok())
|
||||
|
@ -302,9 +305,56 @@ pub trait Database: Sync + Send {
|
|||
.collect()
|
||||
).unwrap_or_default();
|
||||
|
||||
filter_userids(&full_tpk_new, |uid| published_uids.contains(uid))?
|
||||
let unpublished_emails = if is_revoked {
|
||||
vec!()
|
||||
} else {
|
||||
let mut unpublished_emails: Vec<(Email, String)> = full_tpk_new
|
||||
.userids()
|
||||
.filter(|binding| binding.revoked(None) == RevocationStatus::NotAsFarAsWeKnow)
|
||||
.map(|binding| binding.userid().clone())
|
||||
.filter(|uid| !published_uids.contains(uid))
|
||||
.map(|uid| Email::try_from(&uid))
|
||||
.flatten()
|
||||
.map(|email| {
|
||||
// TODO dup this due to legacy interface
|
||||
let email_str = email.to_string();
|
||||
(email, email_str)
|
||||
})
|
||||
.collect();
|
||||
unpublished_emails.sort();
|
||||
unpublished_emails.dedup();
|
||||
unpublished_emails
|
||||
};
|
||||
|
||||
let revoked_uids: Vec<UserID> = full_tpk_new
|
||||
.userids()
|
||||
.filter(|binding| binding.revoked(None) != RevocationStatus::NotAsFarAsWeKnow)
|
||||
.map(|binding| binding.userid().clone())
|
||||
.collect();
|
||||
|
||||
let newly_revoked_uids: Vec<&UserID> = published_uids.iter()
|
||||
.filter(|uid| revoked_uids.contains(uid))
|
||||
.collect();
|
||||
|
||||
let published_tpk_new = filter_userids(
|
||||
&full_tpk_new, |uid| {
|
||||
published_uids.contains(uid) && !newly_revoked_uids.contains(&uid)
|
||||
})?;
|
||||
|
||||
let newly_revoked_emails: Vec<Email> = published_uids.iter()
|
||||
.map(|uid| Email::try_from(uid).ok())
|
||||
.flatten()
|
||||
.filter(|email| {
|
||||
let has_unrevoked_userid = published_tpk_new
|
||||
.userids()
|
||||
.filter(|binding| binding.revoked(None) == RevocationStatus::NotAsFarAsWeKnow)
|
||||
.map(|binding| binding.userid())
|
||||
.map(|uid| Email::try_from(uid).ok())
|
||||
.flatten()
|
||||
.any(|unrevoked_email| unrevoked_email == *email);
|
||||
!has_unrevoked_userid
|
||||
}).collect();
|
||||
|
||||
let fingerprints = published_tpk_new
|
||||
.keys_all()
|
||||
.unfiltered()
|
||||
|
@ -317,11 +367,6 @@ pub trait Database: Sync + Send {
|
|||
|
||||
let _lock = self.lock();
|
||||
|
||||
// these are very unlikely to fail. but if it happens,
|
||||
// database consistency might be compromised!
|
||||
self.move_tmp_to_full(full_tpk_tmp, &fpr_primary)?;
|
||||
self.move_tmp_to_published(published_tpk_tmp, &fpr_primary)?;
|
||||
|
||||
let fpr_checks = fingerprints
|
||||
.map(|fpr| self.check_link_fpr(&fpr, &fpr_primary))
|
||||
.collect::<Vec<_>>()
|
||||
|
@ -329,17 +374,33 @@ pub trait Database: Sync + Send {
|
|||
.collect::<Result<Vec<_>>>()?;
|
||||
let fpr_not_linked = fpr_checks.into_iter().flatten();
|
||||
|
||||
// these are very unlikely to fail. but if it happens,
|
||||
// database consistency might be compromised!
|
||||
self.move_tmp_to_full(full_tpk_tmp, &fpr_primary)?;
|
||||
self.move_tmp_to_published(published_tpk_tmp, &fpr_primary)?;
|
||||
|
||||
for fpr in fpr_not_linked {
|
||||
if let Err(e) = self.ensure_link_fpr(&fpr, &fpr_primary) {
|
||||
if let Err(e) = self.link_fpr(&fpr, &fpr_primary) {
|
||||
info!("Error ensuring symlink! {} {} {:?}",
|
||||
&fpr, &fpr_primary, e);
|
||||
}
|
||||
if let Err(e) = self.link_kid(&(&fpr).into(), &fpr_primary) {
|
||||
info!("Error ensuring symlink! {} {} {:?}",
|
||||
&fpr, &fpr_primary, e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
for revoked_email in newly_revoked_emails {
|
||||
if let Err(e) = self.unlink_email(&revoked_email, &fpr_primary) {
|
||||
info!("Error ensuring symlink! {} {} {:?}",
|
||||
&fpr_primary, &revoked_email, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// Complex operation that published some user id for a TPK already in the database.
|
||||
Ok(unpublished_emails)
|
||||
}
|
||||
|
||||
/// Complex operation that publishes some user id for a TPK already in the database.
|
||||
///
|
||||
/// 1. Load published TPK
|
||||
/// - if UserID is already in, stop
|
||||
|
@ -355,9 +416,7 @@ pub trait Database: Sync + Send {
|
|||
/// 5. Move full and published temporary TPK to their location
|
||||
/// 6. Update all symlinks
|
||||
/// UNLOCK
|
||||
fn set_verified(&self, fpr_primary: &Fingerprint, uid_new: UserID) -> Result<()> {
|
||||
let email_new = Email::try_from(&uid_new)?;
|
||||
|
||||
fn set_verified(&self, fpr_primary: &Fingerprint, email_new: &Email) -> Result<()> {
|
||||
let full_tpk = self.by_fpr_full(&fpr_primary)
|
||||
.ok_or_else(|| failure::err_msg("Key not in database!"))
|
||||
.and_then(|bytes| TPK::from_bytes(bytes.as_ref()))?;
|
||||
|
@ -369,20 +428,26 @@ pub trait Database: Sync + Send {
|
|||
.map(|binding| binding.userid().clone())
|
||||
.collect()
|
||||
).unwrap_or_default();
|
||||
if published_uids_old.contains(&uid_new) {
|
||||
let published_emails_old: Vec<Email> = published_uids_old.iter()
|
||||
.map(|uid| Email::try_from(uid).ok())
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
// println!("publishing: {:?}", &uid_new);
|
||||
if published_emails_old.contains(&email_new) {
|
||||
// UserID already published - just stop
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let published_tpk_new = {
|
||||
filter_userids(&full_tpk,
|
||||
|uid| uid == &uid_new || published_uids_old.contains(uid))?
|
||||
|uid| Email::try_from(uid).unwrap() == *email_new || published_uids_old.contains(uid))?
|
||||
};
|
||||
|
||||
if ! published_tpk_new
|
||||
.userids()
|
||||
.map(|binding| binding.userid())
|
||||
.any(|uid| uid == &uid_new) {
|
||||
.any(|uid| Email::try_from(uid).map(|email| email == *email_new).unwrap_or_default()) {
|
||||
return Err(failure::err_msg("Requested UserID not found!"));
|
||||
}
|
||||
|
||||
|
@ -400,8 +465,54 @@ pub trait Database: Sync + Send {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Complex operation that un-publishes some user id for a TPK already in the database.
|
||||
///
|
||||
/// 1. Load published TPK
|
||||
/// - if UserID is not in, stop
|
||||
/// 2. Load full TPK
|
||||
/// - if requested UserID is not in, stop
|
||||
/// 3. Prepare new published TPK
|
||||
/// - retrieve UserIDs from old published TPK
|
||||
/// - create new TPK from full TPK by keeping only published UserIDs
|
||||
///
|
||||
/// LOCK
|
||||
/// 4. Check for fingerprint and long key id collisions for published TPK
|
||||
/// - abort if any problems come up!
|
||||
/// 5. Move full and published temporary TPK to their location
|
||||
/// 6. Update all symlinks
|
||||
/// UNLOCK
|
||||
fn set_unverified(&self, fpr_primary: &Fingerprint, email_remove: &Email) -> Result<()> {
|
||||
let published_tpk_old = self.by_fpr(&fpr_primary)
|
||||
.ok_or_else(|| failure::err_msg("Key not in database!"))
|
||||
.and_then(|bytes| TPK::from_bytes(bytes.as_ref()))?;
|
||||
|
||||
let published_uids_old: Vec<UserID> = published_tpk_old
|
||||
.userids()
|
||||
.map(|binding| binding.userid().clone())
|
||||
.collect();
|
||||
|
||||
println!("unpublishing: {:?}", &email_remove);
|
||||
|
||||
let published_tpk_new = {
|
||||
filter_userids(&published_tpk_old,
|
||||
|uid| Email::try_from(uid).unwrap() != *email_remove && published_uids_old.contains(uid))?
|
||||
};
|
||||
|
||||
let published_tpk_tmp = self.write_to_temp(&tpk_to_string(&published_tpk_new)?)?;
|
||||
|
||||
let _lock = self.lock();
|
||||
|
||||
self.move_tmp_to_published(published_tpk_tmp, &fpr_primary)?;
|
||||
|
||||
if let Err(e) = self.unlink_email(&email_remove, &fpr_primary) {
|
||||
info!("Error ensuring email symlink! {} -> {} {:?}",
|
||||
&email_remove, &fpr_primary, e);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_link_fpr(&self, fpr: &Fingerprint, target: &Fingerprint) -> Result<Option<Fingerprint>>;
|
||||
fn ensure_link_fpr(&self, fpr: &Fingerprint, target: &Fingerprint) -> Result<()>;
|
||||
|
||||
fn by_fpr_full(&self, fpr: &Fingerprint) -> Option<String>;
|
||||
|
||||
|
@ -409,188 +520,25 @@ pub trait Database: Sync + Send {
|
|||
fn move_tmp_to_full(&self, content: NamedTempFile, fpr: &Fingerprint) -> Result<()>;
|
||||
fn move_tmp_to_published(&self, content: NamedTempFile, fpr: &Fingerprint) -> Result<()>;
|
||||
|
||||
|
||||
/// Merges the given TPK into the database.
|
||||
///
|
||||
/// If the TPK is in the database, it is merged with the given
|
||||
/// one. Fingerprint and KeyID links are created. No new UserID
|
||||
/// links are created.
|
||||
///
|
||||
/// UserIDs that are already present in the database will receive
|
||||
/// new certificates.
|
||||
fn merge(&self, new_tpk: &TPK) -> Result<()> {
|
||||
let fpr = Fingerprint::try_from(new_tpk.primary().fingerprint())?;
|
||||
let _ = self.lock();
|
||||
|
||||
// See if the TPK is in the database.
|
||||
let old_tpk = if let Some(bytes) = self.by_fpr(&fpr) {
|
||||
Some(TPK::from_bytes(bytes.as_ref())?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// If we already know some UserIDs, we want to keep any
|
||||
// updates that come in.
|
||||
use std::collections::HashSet;
|
||||
let mut known_uids = HashSet::new();
|
||||
if let Some(old_tpk) = old_tpk.as_ref() {
|
||||
for uidb in old_tpk.userids() {
|
||||
known_uids.insert(uidb.userid().clone());
|
||||
}
|
||||
}
|
||||
let new_tpk = filter_userids(&new_tpk, move |u| known_uids.contains(u))?;
|
||||
|
||||
// Maybe merge.
|
||||
let tpk = if let Some(old_tpk) = old_tpk {
|
||||
old_tpk.merge(new_tpk)?
|
||||
} else {
|
||||
new_tpk
|
||||
};
|
||||
|
||||
self.link_subkeys(&fpr,
|
||||
tpk.subkeys().map(|s| s.subkey().fingerprint())
|
||||
.collect())?;
|
||||
self.update_tpk(&fpr, Some(tpk))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn merge_or_publish(&self, tpk: &TPK) -> Result<Vec<(Email, String)>> {
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use openpgp::RevocationStatus;
|
||||
let unpublished_uids = self.merge(tpk.clone())?;
|
||||
|
||||
if let RevocationStatus::Revoked(_) = tpk.revoked(None) {
|
||||
// Merge, but don't trigger any verifications.
|
||||
self.merge(tpk)?;
|
||||
return Ok(Vec::new());
|
||||
let fpr_hex = tpk.primary().fingerprint().to_hex();
|
||||
let result = unpublished_uids
|
||||
.into_iter()
|
||||
.map(|(email, uid)| (email, format!("{}|{}", &fpr_hex, uid)))
|
||||
.collect();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
let fpr = Fingerprint::try_from(tpk.primary().fingerprint())?;
|
||||
let mut revoked_uids = HashSet::new();
|
||||
let mut unverified_uids: HashMap<Email, Verify> = HashMap::new();
|
||||
let mut verified_uids = HashSet::new();
|
||||
|
||||
let _ = self.lock();
|
||||
|
||||
// update verify tokens
|
||||
for uid in tpk.userids() {
|
||||
let email = if let Ok(m) = Email::try_from(uid.userid()) {
|
||||
m
|
||||
} else {
|
||||
// Ignore non-UTF8 userids.
|
||||
continue;
|
||||
};
|
||||
|
||||
match uid.revoked(None) {
|
||||
RevocationStatus::CouldBe(_) => {
|
||||
// XXX: Check the revocation, if it checks out,
|
||||
// "fall through".
|
||||
},
|
||||
RevocationStatus::Revoked(_) => {
|
||||
revoked_uids.insert(email);
|
||||
},
|
||||
RevocationStatus::NotAsFarAsWeKnow => {
|
||||
let add_to_verified =
|
||||
match self.lookup(&Query::ByEmail(email.clone()))
|
||||
{
|
||||
Ok(None) => false,
|
||||
Ok(Some(other_tpk)) =>
|
||||
other_tpk.fingerprint() == tpk.fingerprint(),
|
||||
Err(_) => false,
|
||||
};
|
||||
|
||||
if add_to_verified {
|
||||
verified_uids.insert(email);
|
||||
} else {
|
||||
// Hackaround mutable borrow in else.
|
||||
let updated =
|
||||
if let Some(token) = unverified_uids.get_mut(&email)
|
||||
{
|
||||
token.extend(uid)?;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if ! updated {
|
||||
unverified_uids.insert(
|
||||
email.clone(), Verify::new(uid, fpr.clone())?);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let subkeys =
|
||||
tpk.subkeys().map(|s| s.subkey().fingerprint()).collect::<Vec<_>>();
|
||||
|
||||
let tpk = filter_userids(&tpk, |_| false)?;
|
||||
|
||||
for email in revoked_uids.difference(&verified_uids) {
|
||||
self.unlink_email(&email, &fpr)?;
|
||||
}
|
||||
|
||||
// merge or update key db
|
||||
let tpk = match self.lookup(&Query::ByFingerprint(fpr.clone()))? {
|
||||
Some(old) => old.merge(tpk)?,
|
||||
None => tpk,
|
||||
};
|
||||
self.update_tpk(&fpr, Some(tpk))?;
|
||||
|
||||
self.link_subkeys(&fpr, subkeys)?;
|
||||
|
||||
let mut tokens = Vec::new();
|
||||
for (email, verify) in unverified_uids.into_iter() {
|
||||
tokens.push((email, serde_json::to_string(&verify)?));
|
||||
}
|
||||
Ok(tokens)
|
||||
}
|
||||
|
||||
// if (uid, fpr) = pop-token(tok) {
|
||||
// while cas-failed() {
|
||||
// tpk = by_fpr(fpr)
|
||||
// merged = add-uid(tpk, uid)
|
||||
// cas(tpk, merged)
|
||||
// }
|
||||
// }
|
||||
fn verify_token(
|
||||
&self, token_str: &str,
|
||||
) -> Result<Option<(Email, Fingerprint)>> {
|
||||
let _ = self.lock();
|
||||
|
||||
let Verify { created, packets, fpr, email } = serde_json::from_str(&token_str)?;
|
||||
|
||||
let now = time::now().to_timespec().sec;
|
||||
if created > now || now - created > 3 * 3600 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
match self.by_fpr(&fpr) {
|
||||
Some(old) => {
|
||||
|
||||
let tpk = TPK::from_bytes(old.as_bytes()).unwrap();
|
||||
let packet_pile = PacketPile::from_bytes(&packets)
|
||||
.unwrap().into_children().collect::<Vec<_>>();
|
||||
let new = tpk.merge_packets(packet_pile).unwrap();
|
||||
|
||||
|
||||
let mut buf = std::io::Cursor::new(vec![]);
|
||||
{
|
||||
let mut armor_writer = Writer::new(&mut buf, Kind::PublicKey,
|
||||
&[][..])?;
|
||||
|
||||
armor_writer.write_all(&Self::tpk_into_bytes(&new).unwrap())?;
|
||||
};
|
||||
let armored = String::from_utf8_lossy(buf.get_ref());
|
||||
|
||||
self.update(&fpr, Some(armored.into_owned()))?;
|
||||
self.link_email(&email, &fpr)?;
|
||||
return Ok(Some((email.clone(), fpr.clone())));
|
||||
}
|
||||
|
||||
None => {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
let mut pieces = token_str.splitn(2, "|");
|
||||
let fpr: Fingerprint = pieces.next().unwrap().parse().unwrap();
|
||||
let email: Email = pieces.next().unwrap().parse().unwrap();
|
||||
self.set_verified(&fpr, &email)?;
|
||||
Ok(Some((email, fpr)))
|
||||
}
|
||||
|
||||
/// Deletes all UserID packets and unlinks all email addresses.
|
||||
|
@ -604,16 +552,7 @@ pub trait Database: Sync + Send {
|
|||
/// [RFC2822 name-addr]: https://tools.ietf.org/html/rfc2822#section-3.4
|
||||
fn delete_userids_matching(&self, fpr: &Fingerprint, addr: &Email)
|
||||
-> Result <()> {
|
||||
self.filter_userids(fpr, |uid| {
|
||||
match Email::try_from(uid) {
|
||||
// Keep those not matching `addr`.
|
||||
Ok(a) => a != *addr,
|
||||
// This should not happen, because all TPKs in the
|
||||
// database should only have UserIDs with well-formed
|
||||
// values. Be conservative and keep the component.
|
||||
Err(_) => true,
|
||||
}
|
||||
})
|
||||
self.set_unverified(fpr, addr)
|
||||
}
|
||||
|
||||
/// Deletes all user ids NOT matching fulfilling `filter`.
|
||||
|
@ -695,18 +634,15 @@ fn filter_userids<F>(tpk: &TPK, filter: F) -> Result<TPK>
|
|||
|
||||
// Updates for UserIDs fulfilling `filter`.
|
||||
for uidb in tpk.userids() {
|
||||
// Only include userids matching filter...
|
||||
// Only include userids matching filter
|
||||
if filter(uidb.userid()) {
|
||||
acc.push(uidb.userid().clone().into());
|
||||
}
|
||||
|
||||
// ... but always all signatures. This way, clients who have
|
||||
// the UserID packet can enjoy all the updates.
|
||||
for s in uidb.selfsigs() { acc.push(s.clone().into()) }
|
||||
for s in uidb.certifications() { acc.push(s.clone().into()) }
|
||||
for s in uidb.self_revocations() { acc.push(s.clone().into()) }
|
||||
for s in uidb.other_revocations() { acc.push(s.clone().into()) }
|
||||
}
|
||||
}
|
||||
|
||||
TPK::from_packet_pile(acc.into())
|
||||
}
|
||||
|
|
|
@ -663,8 +663,9 @@ pub fn test_same_email_2<D: Database>(db: &mut D) {
|
|||
assert_eq!(tokens.len(), 0);
|
||||
|
||||
// fetch by both user ids. We should still get both user ids.
|
||||
// TODO should this still deliver uid2.clone()?
|
||||
assert_eq!(get_userids(&db.by_email(&email1).unwrap()[..]),
|
||||
vec![ uid1.clone(), uid2.clone() ]);
|
||||
vec![ uid1.clone() ]);
|
||||
assert_eq!(get_userids(&db.by_email(&email2).unwrap()[..]),
|
||||
vec![ uid1.clone(), uid2.clone() ]);
|
||||
vec![ uid1.clone() ]);
|
||||
}
|
||||
|
|
|
@ -145,6 +145,15 @@ impl TryFrom<sequoia_openpgp::Fingerprint> for KeyID {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&Fingerprint> for KeyID {
|
||||
fn from(fpr: &Fingerprint) -> KeyID {
|
||||
let mut arr = [0u8; 8];
|
||||
|
||||
arr.copy_from_slice(&fpr.0[12..20]);
|
||||
KeyID(arr)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fingerprint> for KeyID {
|
||||
fn from(fpr: Fingerprint) -> KeyID {
|
||||
let mut arr = [0u8; 8];
|
||||
|
|
Loading…
Reference in New Issue