2019-04-28 00:22:35 +00:00
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::convert::TryFrom;
|
2019-05-05 15:01:12 +00:00
|
|
|
use std::fs::{create_dir_all, read_link, remove_file, rename, set_permissions, Permissions};
|
2019-04-26 20:14:57 +00:00
|
|
|
use std::io::Write;
|
2019-02-21 21:05:01 +00:00
|
|
|
use std::path::{Path, PathBuf};
|
2019-05-05 15:01:12 +00:00
|
|
|
use std::os::unix::fs::PermissionsExt;
|
2018-08-16 18:35:19 +00:00
|
|
|
|
2019-02-07 19:58:31 +00:00
|
|
|
use tempfile;
|
2018-10-24 17:45:11 +00:00
|
|
|
use url;
|
2019-02-22 14:45:34 +00:00
|
|
|
use pathdiff::diff_paths;
|
2018-08-16 18:35:19 +00:00
|
|
|
|
2019-02-22 21:37:01 +00:00
|
|
|
//use sequoia_openpgp::armor::{Writer, Kind};
|
2019-02-22 15:14:57 +00:00
|
|
|
|
2019-04-26 20:14:57 +00:00
|
|
|
use {Database, Query};
|
2019-01-04 13:07:14 +00:00
|
|
|
use types::{Email, Fingerprint, KeyID};
|
2019-04-29 20:52:45 +00:00
|
|
|
use sync::FlockMutexGuard;
|
2019-02-07 19:58:31 +00:00
|
|
|
use Result;
|
2018-08-16 18:35:19 +00:00
|
|
|
|
2019-04-27 16:19:02 +00:00
|
|
|
use tempfile::NamedTempFile;
|
|
|
|
|
2019-04-28 00:22:35 +00:00
|
|
|
use openpgp::TPK;
|
|
|
|
|
2018-09-19 20:22:59 +00:00
|
|
|
pub struct Filesystem {
|
2019-04-26 16:33:26 +00:00
|
|
|
tmp_dir: PathBuf,
|
|
|
|
|
2019-04-28 20:41:41 +00:00
|
|
|
keys_internal_dir: PathBuf,
|
|
|
|
keys_external_dir: PathBuf,
|
2019-04-27 16:19:02 +00:00
|
|
|
keys_dir_full: PathBuf,
|
2019-06-04 18:02:52 +00:00
|
|
|
keys_dir_quarantined: PathBuf,
|
2019-04-27 16:19:02 +00:00
|
|
|
keys_dir_published: PathBuf,
|
|
|
|
|
|
|
|
links_dir_by_fingerprint: PathBuf,
|
|
|
|
links_dir_by_keyid: PathBuf,
|
|
|
|
links_dir_by_email: PathBuf,
|
2019-05-28 14:31:23 +00:00
|
|
|
|
|
|
|
dry_run: bool,
|
2018-08-16 18:35:19 +00:00
|
|
|
}
|
|
|
|
|
2019-02-21 21:05:01 +00:00
|
|
|
/// Returns the given path, ensuring that the parent directory exists.
|
|
|
|
///
|
|
|
|
/// Use this on paths returned by .path_to_* before creating the
|
|
|
|
/// object.
|
|
|
|
fn ensure_parent(path: &Path) -> Result<&Path> {
|
|
|
|
let parent = path.parent().unwrap();
|
2019-02-22 10:56:31 +00:00
|
|
|
create_dir_all(parent)?;
|
2019-02-21 21:05:01 +00:00
|
|
|
Ok(path)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-16 18:35:19 +00:00
|
|
|
impl Filesystem {
|
2019-04-26 16:33:26 +00:00
|
|
|
pub fn new_from_base(base_dir: impl Into<PathBuf>) -> Result<Self> {
|
|
|
|
let base_dir: PathBuf = base_dir.into();
|
|
|
|
|
|
|
|
let keys_dir = base_dir.join("keys");
|
|
|
|
let tmp_dir = base_dir.join("tmp");
|
|
|
|
|
2019-04-28 20:41:41 +00:00
|
|
|
Self::new(&keys_dir, &keys_dir, tmp_dir)
|
2019-04-26 16:33:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new(
|
2019-04-28 20:41:41 +00:00
|
|
|
keys_internal_dir: impl Into<PathBuf>,
|
|
|
|
keys_external_dir: impl Into<PathBuf>,
|
2019-04-26 16:33:26 +00:00
|
|
|
tmp_dir: impl Into<PathBuf>,
|
|
|
|
) -> Result<Self> {
|
2019-05-28 14:31:23 +00:00
|
|
|
Self::new_internal(keys_internal_dir, keys_external_dir, tmp_dir, false)
|
|
|
|
}
|
2018-08-16 18:35:19 +00:00
|
|
|
|
2019-05-28 14:31:23 +00:00
|
|
|
pub fn new_internal(
|
|
|
|
keys_internal_dir: impl Into<PathBuf>,
|
|
|
|
keys_external_dir: impl Into<PathBuf>,
|
|
|
|
tmp_dir: impl Into<PathBuf>,
|
|
|
|
dry_run: bool,
|
|
|
|
) -> Result<Self> {
|
2019-04-26 16:33:26 +00:00
|
|
|
let tmp_dir = tmp_dir.into();
|
|
|
|
create_dir_all(&tmp_dir)?;
|
|
|
|
|
2019-04-28 20:41:41 +00:00
|
|
|
let keys_internal_dir: PathBuf = keys_internal_dir.into();
|
|
|
|
let keys_external_dir: PathBuf = keys_external_dir.into();
|
|
|
|
let keys_dir_full = keys_internal_dir.join("full");
|
2019-06-04 18:02:52 +00:00
|
|
|
let keys_dir_quarantined = keys_internal_dir.join("quarantined");
|
2019-06-09 11:55:50 +00:00
|
|
|
let keys_dir_published = keys_external_dir.join("pub");
|
2019-04-27 16:19:02 +00:00
|
|
|
create_dir_all(&keys_dir_full)?;
|
2019-06-04 18:02:52 +00:00
|
|
|
create_dir_all(&keys_dir_quarantined)?;
|
2019-04-27 16:19:02 +00:00
|
|
|
create_dir_all(&keys_dir_published)?;
|
|
|
|
|
2019-06-09 11:55:50 +00:00
|
|
|
let links_dir = keys_external_dir.join("links");
|
|
|
|
let links_dir_by_keyid = links_dir.join("by-keyid");
|
|
|
|
let links_dir_by_fingerprint = links_dir.join("by-fpr");
|
|
|
|
let links_dir_by_email = links_dir.join("by-email");
|
2019-04-27 16:19:02 +00:00
|
|
|
create_dir_all(&links_dir_by_keyid)?;
|
|
|
|
create_dir_all(&links_dir_by_fingerprint)?;
|
|
|
|
create_dir_all(&links_dir_by_email)?;
|
2018-08-16 18:35:19 +00:00
|
|
|
|
2019-04-26 16:33:26 +00:00
|
|
|
info!("Opened filesystem database.");
|
2019-04-28 20:41:41 +00:00
|
|
|
info!("keys_internal_dir: '{}'", keys_internal_dir.display());
|
|
|
|
info!("keys_external_dir: '{}'", keys_external_dir.display());
|
2019-04-26 16:33:26 +00:00
|
|
|
info!("tmp_dir: '{}'", tmp_dir.display());
|
2019-02-21 21:05:01 +00:00
|
|
|
Ok(Filesystem {
|
2019-04-28 20:41:41 +00:00
|
|
|
keys_internal_dir,
|
|
|
|
keys_external_dir,
|
2019-04-27 16:19:02 +00:00
|
|
|
tmp_dir,
|
|
|
|
|
|
|
|
keys_dir_full,
|
|
|
|
keys_dir_published,
|
2019-06-04 18:02:52 +00:00
|
|
|
keys_dir_quarantined,
|
2019-04-27 16:19:02 +00:00
|
|
|
|
|
|
|
links_dir_by_keyid,
|
|
|
|
links_dir_by_fingerprint,
|
|
|
|
links_dir_by_email,
|
2019-05-28 14:31:23 +00:00
|
|
|
|
|
|
|
dry_run,
|
2019-02-21 21:05:01 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-04-27 16:19:02 +00:00
|
|
|
/// Returns the path to the given Fingerprint.
|
|
|
|
fn fingerprint_to_path_full(&self, fingerprint: &Fingerprint) -> PathBuf {
|
|
|
|
let hex = fingerprint.to_string();
|
2019-04-27 22:17:58 +00:00
|
|
|
self.keys_dir_full.join(path_split(&hex))
|
2019-04-27 16:19:02 +00:00
|
|
|
}
|
|
|
|
|
2019-06-04 18:02:52 +00:00
|
|
|
/// Returns the path to the given Fingerprint.
|
|
|
|
fn fingerprint_to_path_quarantined(&self, fingerprint: &Fingerprint) -> PathBuf {
|
|
|
|
let hex = fingerprint.to_string();
|
|
|
|
self.keys_dir_quarantined.join(&hex)
|
|
|
|
}
|
|
|
|
|
2019-04-27 16:19:02 +00:00
|
|
|
/// Returns the path to the given Fingerprint.
|
|
|
|
fn fingerprint_to_path_published(&self, fingerprint: &Fingerprint) -> PathBuf {
|
|
|
|
let hex = fingerprint.to_string();
|
2019-04-27 22:17:58 +00:00
|
|
|
self.keys_dir_published.join(path_split(&hex))
|
2019-04-27 16:19:02 +00:00
|
|
|
}
|
|
|
|
|
2019-02-21 21:05:01 +00:00
|
|
|
/// Returns the path to the given KeyID.
|
2019-04-27 16:19:02 +00:00
|
|
|
fn link_by_keyid(&self, keyid: &KeyID) -> PathBuf {
|
2019-02-21 23:29:15 +00:00
|
|
|
let hex = keyid.to_string();
|
2019-04-27 22:17:58 +00:00
|
|
|
self.links_dir_by_keyid.join(path_split(&hex))
|
2019-02-21 21:05:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the path to the given Fingerprint.
|
2019-04-27 16:19:02 +00:00
|
|
|
fn link_by_fingerprint(&self, fingerprint: &Fingerprint) -> PathBuf {
|
2019-02-21 23:29:15 +00:00
|
|
|
let hex = fingerprint.to_string();
|
2019-04-27 22:17:58 +00:00
|
|
|
self.links_dir_by_fingerprint.join(path_split(&hex))
|
2019-02-21 21:05:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the path to the given Email.
|
2019-04-27 16:19:02 +00:00
|
|
|
fn link_by_email(&self, email: &Email) -> PathBuf {
|
2019-03-05 12:31:59 +00:00
|
|
|
let email =
|
|
|
|
url::form_urlencoded::byte_serialize(email.as_str().as_bytes())
|
|
|
|
.collect::<String>();
|
2019-04-27 22:17:58 +00:00
|
|
|
self.links_dir_by_email.join(path_split(&email))
|
2018-08-16 18:35:19 +00:00
|
|
|
}
|
|
|
|
|
2019-04-28 20:41:41 +00:00
|
|
|
fn read_from_path(&self, path: &Path, allow_internal: bool) -> Option<String> {
|
2019-04-16 13:51:00 +00:00
|
|
|
use std::fs;
|
|
|
|
|
2019-04-28 20:41:41 +00:00
|
|
|
if !path.starts_with(&self.keys_external_dir) &&
|
|
|
|
!(allow_internal && path.starts_with(&self.keys_internal_dir)) {
|
|
|
|
panic!("Attempted to access file outside expected dirs!");
|
2019-04-27 16:19:02 +00:00
|
|
|
}
|
|
|
|
|
2019-04-16 13:51:00 +00:00
|
|
|
if path.exists() {
|
|
|
|
fs::read_to_string(path).ok()
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-07 22:44:51 +00:00
|
|
|
/// Returns the Fingerprint the given path is pointing to.
|
|
|
|
pub fn path_to_fingerprint(path: &Path) -> Option<Fingerprint> {
|
2019-03-11 14:49:01 +00:00
|
|
|
use std::str::FromStr;
|
2019-04-27 22:17:58 +00:00
|
|
|
let merged = path_merge(path);
|
2019-06-07 22:44:51 +00:00
|
|
|
Fingerprint::from_str(&merged).ok()
|
2019-03-11 14:49:01 +00:00
|
|
|
}
|
|
|
|
|
2019-06-07 22:44:51 +00:00
|
|
|
/// Returns the KeyID the given path is pointing to.
|
|
|
|
fn path_to_keyid(path: &Path) -> Option<KeyID> {
|
2019-02-28 17:43:44 +00:00
|
|
|
use std::str::FromStr;
|
2019-04-27 22:17:58 +00:00
|
|
|
let merged = path_merge(path);
|
2019-06-07 22:44:51 +00:00
|
|
|
KeyID::from_str(&merged).ok()
|
2019-02-28 17:43:44 +00:00
|
|
|
}
|
|
|
|
|
2019-03-11 14:49:01 +00:00
|
|
|
/// Returns the Email the given path is pointing to.
|
2019-06-07 22:44:51 +00:00
|
|
|
fn path_to_email(path: &Path) -> Option<Email> {
|
2019-03-11 14:49:01 +00:00
|
|
|
use std::str::FromStr;
|
2019-04-27 22:17:58 +00:00
|
|
|
let merged = path_merge(path);
|
|
|
|
let decoded = url::form_urlencoded::parse(merged.as_bytes()).next()?.0;
|
2019-03-11 14:49:01 +00:00
|
|
|
Email::from_str(&decoded).ok()
|
|
|
|
}
|
|
|
|
|
2019-04-28 00:22:35 +00:00
|
|
|
/// Returns the backing primary key fingerprint for any key path.
|
2019-06-07 22:44:51 +00:00
|
|
|
fn path_to_primary(path: &Path) -> Option<Fingerprint> {
|
2019-04-28 00:22:35 +00:00
|
|
|
use std::fs;
|
|
|
|
let typ = fs::symlink_metadata(&path).ok()?.file_type();
|
|
|
|
if typ.is_symlink() {
|
|
|
|
let path = read_link(path).ok()?;
|
2019-06-07 22:44:51 +00:00
|
|
|
Filesystem::path_to_fingerprint(&path)
|
2019-04-28 00:22:35 +00:00
|
|
|
} else {
|
2019-06-07 22:44:51 +00:00
|
|
|
Filesystem::path_to_fingerprint(path)
|
2019-04-28 00:22:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn perform_checks(
|
|
|
|
&self,
|
|
|
|
checks_dir: &Path,
|
|
|
|
tpks: &mut HashMap<Fingerprint, TPK>,
|
|
|
|
check: impl Fn(&Path, &TPK, &Fingerprint) -> Result<()>,
|
|
|
|
) -> Result<()> {
|
|
|
|
use walkdir::WalkDir;
|
|
|
|
use std::fs;
|
|
|
|
use failure::format_err;
|
2019-03-11 14:49:01 +00:00
|
|
|
|
2019-04-28 00:22:35 +00:00
|
|
|
for entry in WalkDir::new(checks_dir) {
|
2019-04-27 22:17:58 +00:00
|
|
|
let entry = entry?;
|
|
|
|
let path = entry.path();
|
|
|
|
let typ = fs::symlink_metadata(&path)?.file_type();
|
|
|
|
if typ.is_dir() {
|
|
|
|
continue;
|
2019-03-11 14:49:01 +00:00
|
|
|
}
|
|
|
|
|
2019-04-27 22:17:58 +00:00
|
|
|
// Compute the corresponding primary fingerprint just
|
|
|
|
// by looking at the paths.
|
2019-06-07 22:44:51 +00:00
|
|
|
let primary_fp = Filesystem::path_to_primary(path)
|
2019-04-27 22:17:58 +00:00
|
|
|
.ok_or_else(
|
|
|
|
|| format_err!("Malformed path: {:?}",
|
2019-04-28 00:22:35 +00:00
|
|
|
path.read_link().unwrap()))?;
|
|
|
|
// Load into cache.
|
|
|
|
if ! tpks.contains_key(&primary_fp) {
|
|
|
|
tpks.insert(
|
|
|
|
primary_fp.clone(),
|
|
|
|
self.lookup(&Query::ByFingerprint(primary_fp.clone()))
|
|
|
|
?.ok_or_else(
|
|
|
|
|| format_err!("No TPK with fingerprint {:?}",
|
|
|
|
primary_fp))?);
|
|
|
|
}
|
2019-03-11 14:49:01 +00:00
|
|
|
|
2019-04-27 22:17:58 +00:00
|
|
|
let tpk = tpks.get(&primary_fp)
|
|
|
|
.ok_or_else(
|
|
|
|
|| format_err!("Broken symlink {:?}: No such Key {}",
|
|
|
|
path, primary_fp))?;
|
|
|
|
|
2019-04-28 00:22:35 +00:00
|
|
|
check(&path, &tpk, &primary_fp)?;
|
2019-03-11 14:49:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2018-08-16 18:35:19 +00:00
|
|
|
}
|
|
|
|
|
2019-02-22 19:26:10 +00:00
|
|
|
// Like `symlink`, but instead of failing if `symlink_name` already
|
|
|
|
// exists, atomically update `symlink_name` to have `symlink_content`.
|
|
|
|
fn symlink(symlink_content: &Path, symlink_name: &Path) -> Result<()> {
|
|
|
|
use std::os::unix::fs::{symlink};
|
|
|
|
|
|
|
|
let symlink_dir = ensure_parent(symlink_name)?.parent().unwrap();
|
|
|
|
let tmp_dir = tempfile::Builder::new()
|
|
|
|
.prefix("link")
|
|
|
|
.rand_bytes(16)
|
|
|
|
.tempdir_in(symlink_dir)?;
|
|
|
|
let symlink_name_tmp = tmp_dir.path().join("link");
|
|
|
|
|
|
|
|
symlink(&symlink_content, &symlink_name_tmp)?;
|
|
|
|
rename(&symlink_name_tmp, &symlink_name)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2018-08-16 18:35:19 +00:00
|
|
|
impl Database for Filesystem {
|
2019-04-29 20:52:45 +00:00
|
|
|
type MutexGuard = FlockMutexGuard;
|
|
|
|
|
|
|
|
fn lock(&self) -> Result<Self::MutexGuard> {
|
|
|
|
FlockMutexGuard::lock(&self.keys_internal_dir)
|
2019-02-22 21:27:36 +00:00
|
|
|
}
|
|
|
|
|
2019-05-05 15:01:12 +00:00
|
|
|
fn write_to_temp(&self, content: &[u8]) -> Result<NamedTempFile> {
|
2019-04-27 16:19:02 +00:00
|
|
|
let mut tempfile = tempfile::Builder::new()
|
|
|
|
.prefix("key")
|
|
|
|
.rand_bytes(16)
|
|
|
|
.tempfile_in(&self.tmp_dir)?;
|
|
|
|
tempfile.write_all(content).unwrap();
|
|
|
|
Ok(tempfile)
|
|
|
|
}
|
2019-05-05 15:01:12 +00:00
|
|
|
|
2019-04-27 16:19:02 +00:00
|
|
|
fn move_tmp_to_full(&self, file: NamedTempFile, fpr: &Fingerprint) -> Result<()> {
|
2019-05-28 14:31:23 +00:00
|
|
|
if self.dry_run {
|
|
|
|
return Ok(());
|
|
|
|
}
|
2019-05-05 15:01:12 +00:00
|
|
|
set_permissions(file.path(), Permissions::from_mode(0o640))?;
|
2019-04-27 16:19:02 +00:00
|
|
|
let target = self.fingerprint_to_path_full(fpr);
|
|
|
|
file.persist(ensure_parent(&target)?)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
2019-05-05 15:01:12 +00:00
|
|
|
|
2019-04-27 16:19:02 +00:00
|
|
|
fn move_tmp_to_published(&self, file: NamedTempFile, fpr: &Fingerprint) -> Result<()> {
|
2019-05-28 14:31:23 +00:00
|
|
|
if self.dry_run {
|
|
|
|
return Ok(());
|
|
|
|
}
|
2019-05-05 15:01:12 +00:00
|
|
|
set_permissions(file.path(), Permissions::from_mode(0o644))?;
|
2019-04-27 16:19:02 +00:00
|
|
|
let target = self.fingerprint_to_path_published(fpr);
|
|
|
|
file.persist(ensure_parent(&target)?)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-06-04 18:02:52 +00:00
|
|
|
fn write_to_quarantine(&self, fpr: &Fingerprint, content: &[u8]) -> Result<()> {
|
|
|
|
let mut tempfile = tempfile::Builder::new()
|
|
|
|
.prefix("key")
|
|
|
|
.rand_bytes(16)
|
|
|
|
.tempfile_in(&self.tmp_dir)?;
|
|
|
|
tempfile.write_all(content).unwrap();
|
|
|
|
|
|
|
|
let target = self.fingerprint_to_path_quarantined(fpr);
|
|
|
|
tempfile.persist(ensure_parent(&target)?)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-04-27 16:19:02 +00:00
|
|
|
fn check_link_fpr(&self, fpr: &Fingerprint, fpr_target: &Fingerprint) -> Result<Option<Fingerprint>> {
|
2019-05-05 21:33:24 +00:00
|
|
|
let link_keyid = self.link_by_keyid(&fpr.into());
|
|
|
|
let link_fpr = self.link_by_fingerprint(&fpr);
|
2019-04-27 16:19:02 +00:00
|
|
|
|
2019-05-05 21:33:24 +00:00
|
|
|
let path_published = self.fingerprint_to_path_published(fpr_target);
|
|
|
|
|
|
|
|
if let Ok(link_fpr_target) = link_keyid.canonicalize() {
|
2019-05-09 13:28:09 +00:00
|
|
|
if !link_fpr_target.ends_with(&path_published) {
|
|
|
|
info!("Fingerprint points to different key for {} (expected {:?} to be suffix of {:?})",
|
|
|
|
fpr, &path_published, &link_fpr_target);
|
2019-05-27 09:28:29 +00:00
|
|
|
Err(failure::err_msg(format!("Fingerprint collision for key {}", fpr)))?;
|
2019-05-05 21:33:24 +00:00
|
|
|
}
|
2019-04-27 16:19:02 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 09:11:37 +00:00
|
|
|
if let Ok(link_keyid_target) = link_keyid.canonicalize() {
|
|
|
|
if !link_keyid_target.ends_with(&path_published) {
|
|
|
|
info!("KeyID points to different key for {} (expected {:?} to be suffix of {:?})",
|
|
|
|
fpr, &path_published, &link_keyid_target);
|
|
|
|
Err(failure::err_msg(format!("KeyID collision for key {}", fpr)))?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-07 16:18:58 +00:00
|
|
|
if !link_fpr.exists() || !link_keyid.exists() {
|
2019-05-05 21:33:24 +00:00
|
|
|
Ok(Some(fpr.clone()))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
2019-04-27 16:19:02 +00:00
|
|
|
}
|
|
|
|
|
2019-02-28 17:43:44 +00:00
|
|
|
fn lookup_primary_fingerprint(&self, term: &Query) -> Option<Fingerprint> {
|
|
|
|
use super::Query::*;
|
2019-06-10 15:29:49 +00:00
|
|
|
let path = match term {
|
|
|
|
ByFingerprint(ref fp) => self.link_by_fingerprint(fp),
|
|
|
|
ByKeyID(ref keyid) => self.link_by_keyid(keyid),
|
|
|
|
ByEmail(ref email) => self.link_by_email(email),
|
|
|
|
};
|
|
|
|
path.read_link()
|
|
|
|
.ok()
|
|
|
|
.and_then(|link_path| Filesystem::path_to_fingerprint(&link_path))
|
2019-02-28 17:43:44 +00:00
|
|
|
}
|
|
|
|
|
2019-03-01 11:57:00 +00:00
|
|
|
/// Gets the path to the underlying file, if any.
|
|
|
|
fn lookup_path(&self, term: &Query) -> Option<PathBuf> {
|
|
|
|
use super::Query::*;
|
|
|
|
let path = match term {
|
2019-04-27 16:19:02 +00:00
|
|
|
ByFingerprint(ref fp) => self.link_by_fingerprint(fp),
|
|
|
|
ByKeyID(ref keyid) => self.link_by_keyid(keyid),
|
|
|
|
ByEmail(ref email) => self.link_by_email(email),
|
2019-03-01 11:57:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if path.exists() {
|
2019-04-28 20:41:41 +00:00
|
|
|
let x = diff_paths(&path, &self.keys_external_dir).expect("related paths");
|
2019-04-26 20:14:57 +00:00
|
|
|
Some(x)
|
2019-03-01 11:57:00 +00:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-22 00:13:20 +00:00
|
|
|
fn link_email(&self, email: &Email, fpr: &Fingerprint) -> Result<()> {
|
2019-05-28 14:31:23 +00:00
|
|
|
if self.dry_run {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
2019-04-27 16:19:02 +00:00
|
|
|
let link = self.link_by_email(&email);
|
2019-04-27 20:08:41 +00:00
|
|
|
let target = diff_paths(&self.fingerprint_to_path_published(fpr),
|
2019-02-22 14:45:34 +00:00
|
|
|
link.parent().unwrap()).unwrap();
|
2018-08-16 18:35:19 +00:00
|
|
|
|
2019-02-22 19:26:10 +00:00
|
|
|
if link == target {
|
|
|
|
return Ok(());
|
2018-08-16 18:35:19 +00:00
|
|
|
}
|
|
|
|
|
2019-02-22 19:26:10 +00:00
|
|
|
symlink(&target, ensure_parent(&link)?)
|
2018-08-16 18:35:19 +00:00
|
|
|
}
|
|
|
|
|
2019-02-22 00:13:20 +00:00
|
|
|
fn unlink_email(&self, email: &Email, fpr: &Fingerprint) -> Result<()> {
|
2019-04-27 16:19:02 +00:00
|
|
|
let link = self.link_by_email(&email);
|
2018-08-16 18:35:19 +00:00
|
|
|
|
2019-03-07 15:00:36 +00:00
|
|
|
match read_link(&link) {
|
2018-08-16 18:35:19 +00:00
|
|
|
Ok(target) => {
|
2019-04-27 20:08:41 +00:00
|
|
|
let expected = diff_paths(&self.fingerprint_to_path_published(fpr),
|
2019-02-22 14:45:34 +00:00
|
|
|
link.parent().unwrap()).unwrap();
|
2018-08-16 18:35:19 +00:00
|
|
|
|
|
|
|
if target == expected {
|
2019-02-22 00:13:20 +00:00
|
|
|
remove_file(link)?;
|
2018-08-16 18:35:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(_) => {}
|
|
|
|
}
|
2019-02-22 19:26:10 +00:00
|
|
|
|
2019-02-22 00:13:20 +00:00
|
|
|
Ok(())
|
2018-08-16 18:35:19 +00:00
|
|
|
}
|
|
|
|
|
2019-04-27 22:02:30 +00:00
|
|
|
fn link_fpr(&self, from: &Fingerprint, primary_fpr: &Fingerprint) -> Result<()> {
|
2019-05-28 14:31:23 +00:00
|
|
|
if self.dry_run {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
2019-04-27 22:02:30 +00:00
|
|
|
let link_fpr = self.link_by_fingerprint(from);
|
|
|
|
let link_keyid = self.link_by_keyid(&from.into());
|
|
|
|
let target = diff_paths(&self.fingerprint_to_path_published(primary_fpr),
|
|
|
|
link_fpr.parent().unwrap()).unwrap();
|
2019-02-22 19:39:51 +00:00
|
|
|
|
2019-04-27 22:02:30 +00:00
|
|
|
symlink(&target, ensure_parent(&link_fpr)?)?;
|
|
|
|
symlink(&target, ensure_parent(&link_keyid)?)
|
2019-01-04 13:07:14 +00:00
|
|
|
}
|
|
|
|
|
2019-04-27 22:02:30 +00:00
|
|
|
fn unlink_fpr(&self, from: &Fingerprint, primary_fpr: &Fingerprint) -> Result<()> {
|
|
|
|
let link_fpr = self.link_by_fingerprint(from);
|
|
|
|
let link_keyid = self.link_by_keyid(&from.into());
|
2019-06-07 16:18:58 +00:00
|
|
|
let expected = diff_paths(&self.fingerprint_to_path_published(primary_fpr),
|
|
|
|
link_fpr.parent().unwrap()).unwrap();
|
2019-01-04 13:07:14 +00:00
|
|
|
|
2019-04-27 22:02:30 +00:00
|
|
|
match read_link(&link_fpr) {
|
2019-01-04 13:07:14 +00:00
|
|
|
Ok(target) => {
|
|
|
|
if target == expected {
|
2019-06-07 16:18:58 +00:00
|
|
|
remove_file(&link_fpr)?;
|
2019-01-04 13:07:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(_) => {}
|
|
|
|
}
|
2019-04-27 22:02:30 +00:00
|
|
|
match read_link(&link_keyid) {
|
2019-01-04 13:07:14 +00:00
|
|
|
Ok(target) => {
|
|
|
|
if target == expected {
|
2019-04-27 22:02:30 +00:00
|
|
|
remove_file(link_keyid)?;
|
2019-01-04 13:07:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(_) => {}
|
|
|
|
}
|
2019-04-27 22:02:30 +00:00
|
|
|
|
2019-02-22 00:13:20 +00:00
|
|
|
Ok(())
|
2019-01-04 13:07:14 +00:00
|
|
|
}
|
|
|
|
|
2019-04-27 16:19:02 +00:00
|
|
|
// XXX: slow
|
|
|
|
fn by_fpr_full(&self, fpr: &Fingerprint) -> Option<String> {
|
|
|
|
let path = self.fingerprint_to_path_full(fpr);
|
2019-04-28 20:41:41 +00:00
|
|
|
self.read_from_path(&path, true)
|
2019-04-27 16:19:02 +00:00
|
|
|
}
|
|
|
|
|
2019-06-07 16:18:58 +00:00
|
|
|
// XXX: slow
|
|
|
|
fn by_primary_fpr(&self, fpr: &Fingerprint) -> Option<String> {
|
|
|
|
let path = self.fingerprint_to_path_published(fpr);
|
|
|
|
self.read_from_path(&path, false)
|
|
|
|
}
|
|
|
|
|
2018-08-16 18:35:19 +00:00
|
|
|
// XXX: slow
|
2019-02-22 20:29:51 +00:00
|
|
|
fn by_fpr(&self, fpr: &Fingerprint) -> Option<String> {
|
2019-04-27 16:19:02 +00:00
|
|
|
let path = self.link_by_fingerprint(fpr);
|
2019-04-28 20:41:41 +00:00
|
|
|
self.read_from_path(&path, false)
|
2018-08-16 18:35:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// XXX: slow
|
2019-02-22 20:29:51 +00:00
|
|
|
fn by_email(&self, email: &Email) -> Option<String> {
|
2019-04-27 16:19:02 +00:00
|
|
|
let path = self.link_by_email(&email);
|
2019-04-28 20:41:41 +00:00
|
|
|
self.read_from_path(&path, false)
|
2018-08-16 18:35:19 +00:00
|
|
|
}
|
2019-01-04 13:07:14 +00:00
|
|
|
|
|
|
|
// XXX: slow
|
2019-02-22 20:29:51 +00:00
|
|
|
fn by_kid(&self, kid: &KeyID) -> Option<String> {
|
2019-04-27 16:19:02 +00:00
|
|
|
let path = self.link_by_keyid(kid);
|
2019-04-28 20:41:41 +00:00
|
|
|
self.read_from_path(&path, false)
|
2019-01-04 13:07:14 +00:00
|
|
|
}
|
2019-06-07 16:18:58 +00:00
|
|
|
|
|
|
|
/// Checks the database for consistency.
|
|
|
|
///
|
|
|
|
/// Note that this operation may take a long time, and is
|
|
|
|
/// generally only useful for testing.
|
|
|
|
fn check_consistency(&self) -> Result<()> {
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use failure::format_err;
|
|
|
|
|
|
|
|
// A cache of all TPKs, for quick lookups.
|
|
|
|
let mut tpks = HashMap::new();
|
|
|
|
|
|
|
|
self.perform_checks(&self.keys_dir_published, &mut tpks,
|
|
|
|
|path, _, primary_fp| {
|
|
|
|
// The KeyID corresponding with this path.
|
2019-06-07 22:44:51 +00:00
|
|
|
let fp = Filesystem::path_to_fingerprint(&path)
|
2019-06-07 16:18:58 +00:00
|
|
|
.ok_or_else(|| format_err!("Malformed path: {:?}", path))?;
|
|
|
|
|
|
|
|
if fp != *primary_fp {
|
|
|
|
return Err(format_err!(
|
|
|
|
"{:?} points to the wrong TPK, expected {} \
|
|
|
|
but found {}",
|
|
|
|
path, fp, primary_fp));
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
)?;
|
|
|
|
|
|
|
|
// check that all subkeys are linked
|
|
|
|
self.perform_checks(&self.keys_dir_published, &mut tpks,
|
|
|
|
|_, tpk, primary_fp| {
|
|
|
|
let fingerprints = tpk
|
|
|
|
.keys_all()
|
|
|
|
.certification_capable()
|
|
|
|
.signing_capable()
|
|
|
|
.map(|(_, _, key)| key.fingerprint())
|
|
|
|
.map(|fpr| Fingerprint::try_from(fpr))
|
|
|
|
.flatten();
|
|
|
|
|
|
|
|
for fpr in fingerprints {
|
|
|
|
if let Some(missing_fpr) = self.check_link_fpr(&fpr, &primary_fp)? {
|
|
|
|
return Err(format_err!(
|
|
|
|
"Missing link to key {} for sub {}", primary_fp, missing_fpr));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
)?;
|
|
|
|
|
|
|
|
// check that all published uids are linked
|
|
|
|
self.perform_checks(&self.keys_dir_published, &mut tpks,
|
|
|
|
|_, tpk, primary_fp| {
|
|
|
|
let emails = tpk
|
|
|
|
.userids()
|
|
|
|
.map(|binding| binding.userid().clone())
|
|
|
|
.map(|userid| Email::try_from(&userid).unwrap());
|
|
|
|
|
|
|
|
for email in emails {
|
|
|
|
let email_path = self.link_by_email(&email);
|
|
|
|
if !email_path.exists() {
|
|
|
|
return Err(format_err!(
|
|
|
|
"Missing link to key {} for email {}", primary_fp, email));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
|
|
self.perform_checks(&self.links_dir_by_fingerprint, &mut tpks,
|
|
|
|
|path, tpk, _| {
|
|
|
|
// The KeyID corresponding with this path.
|
2019-06-07 22:44:51 +00:00
|
|
|
let id = Filesystem::path_to_keyid(&path)
|
2019-06-07 16:18:58 +00:00
|
|
|
.ok_or_else(|| format_err!("Malformed path: {:?}", path))?;
|
|
|
|
|
|
|
|
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 \
|
|
|
|
contain the (sub)key {}", path, id));
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
)?;
|
|
|
|
|
|
|
|
self.perform_checks(&self.links_dir_by_keyid, &mut tpks,
|
|
|
|
|path, tpk, _| {
|
|
|
|
// The KeyID corresponding with this path.
|
2019-06-07 22:44:51 +00:00
|
|
|
let id = Filesystem::path_to_keyid(&path)
|
2019-06-07 16:18:58 +00:00
|
|
|
.ok_or_else(|| format_err!("Malformed path: {:?}", path))?;
|
|
|
|
|
|
|
|
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 \
|
|
|
|
contain the (sub)key {}", path, id));
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
)?;
|
|
|
|
|
|
|
|
self.perform_checks(&self.links_dir_by_email, &mut tpks,
|
|
|
|
|path, tpk, _| {
|
|
|
|
// The Email corresponding with this path.
|
2019-06-07 22:44:51 +00:00
|
|
|
let email = Filesystem::path_to_email(&path)
|
2019-06-07 16:18:58 +00:00
|
|
|
.ok_or_else(|| format_err!("Malformed path: {:?}", path))?;
|
|
|
|
let mut found = false;
|
|
|
|
for uidb in tpk.userids() {
|
|
|
|
if Email::try_from(uidb.userid()).unwrap() == email
|
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ! found {
|
|
|
|
return Err(format_err!(
|
|
|
|
"{:?} points to the wrong TPK, the TPK does not \
|
|
|
|
contain the email {}", path, email));
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
})?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2018-08-16 18:35:19 +00:00
|
|
|
}
|
|
|
|
|
2019-04-27 22:17:58 +00:00
|
|
|
fn path_split(path: &str) -> PathBuf {
|
|
|
|
if path.len() > 4 {
|
|
|
|
[&path[..2], &path[2..4], &path[4..]].iter().collect()
|
|
|
|
} else {
|
|
|
|
path.into()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn path_merge(path: &Path) -> String {
|
|
|
|
let comps = path.iter().rev().take(3).collect::<Vec<_>>().into_iter().rev();
|
|
|
|
let comps: Vec<_> = comps.map(|os| os.to_string_lossy()).collect();
|
|
|
|
comps.join("")
|
|
|
|
}
|
|
|
|
|
2018-08-16 18:35:19 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2019-03-05 16:30:56 +00:00
|
|
|
use test;
|
|
|
|
use openpgp::tpk::TPKBuilder;
|
2019-02-07 19:58:31 +00:00
|
|
|
use tempfile::TempDir;
|
2018-08-16 18:35:19 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn init() {
|
|
|
|
let tmpdir = TempDir::new().unwrap();
|
2019-04-26 20:14:57 +00:00
|
|
|
let _ = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
2018-08-16 18:35:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn new() {
|
|
|
|
let tmpdir = TempDir::new().unwrap();
|
2019-04-26 20:14:57 +00:00
|
|
|
let db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
2019-06-07 20:30:51 +00:00
|
|
|
let k1 = TPKBuilder::new().add_userid("a@invalid.example.org")
|
2019-03-07 12:51:18 +00:00
|
|
|
.generate().unwrap().0;
|
2019-06-07 20:30:51 +00:00
|
|
|
let k2 = TPKBuilder::new().add_userid("b@invalid.example.org")
|
2019-03-07 12:51:18 +00:00
|
|
|
.generate().unwrap().0;
|
2019-06-07 20:30:51 +00:00
|
|
|
let k3 = TPKBuilder::new().add_userid("c@invalid.example.org")
|
2019-03-07 12:51:18 +00:00
|
|
|
.generate().unwrap().0;
|
2018-08-16 18:35:19 +00:00
|
|
|
|
2019-05-27 09:28:29 +00:00
|
|
|
assert!(db.merge(k1).unwrap().into_tpk_status().email_status.len() > 0);
|
|
|
|
assert!(db.merge(k2.clone()).unwrap().into_tpk_status().email_status.len() > 0);
|
|
|
|
assert!(!db.merge(k2).unwrap().into_tpk_status().email_status.len() > 0);
|
|
|
|
assert!(db.merge(k3.clone()).unwrap().into_tpk_status().email_status.len() > 0);
|
|
|
|
assert!(!db.merge(k3.clone()).unwrap().into_tpk_status().email_status.len() > 0);
|
|
|
|
assert!(!db.merge(k3).unwrap().into_tpk_status().email_status.len() > 0);
|
2018-08-16 18:35:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn uid_verification() {
|
|
|
|
let tmpdir = TempDir::new().unwrap();
|
2019-04-26 20:14:57 +00:00
|
|
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
2018-08-16 18:35:19 +00:00
|
|
|
|
|
|
|
test::test_uid_verification(&mut db);
|
2019-06-07 16:18:58 +00:00
|
|
|
db.check_consistency().expect("inconsistent database");
|
2018-08-16 18:35:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn uid_deletion() {
|
|
|
|
let tmpdir = TempDir::new().unwrap();
|
2019-04-26 20:14:57 +00:00
|
|
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
2018-08-16 18:35:19 +00:00
|
|
|
|
|
|
|
test::test_uid_deletion(&mut db);
|
2019-06-07 16:18:58 +00:00
|
|
|
db.check_consistency().expect("inconsistent database");
|
2018-08-16 18:35:19 +00:00
|
|
|
}
|
2019-01-04 13:07:14 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn subkey_lookup() {
|
|
|
|
let tmpdir = TempDir::new().unwrap();
|
2019-04-26 20:14:57 +00:00
|
|
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
2019-01-04 13:07:14 +00:00
|
|
|
|
|
|
|
test::test_subkey_lookup(&mut db);
|
2019-06-07 16:18:58 +00:00
|
|
|
db.check_consistency().expect("inconsistent database");
|
2019-01-04 13:07:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn kid_lookup() {
|
|
|
|
let tmpdir = TempDir::new().unwrap();
|
2019-04-26 20:14:57 +00:00
|
|
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
2019-01-04 13:07:14 +00:00
|
|
|
|
|
|
|
test::test_kid_lookup(&mut db);
|
2019-06-07 16:18:58 +00:00
|
|
|
db.check_consistency().expect("inconsistent database");
|
2019-01-04 13:07:14 +00:00
|
|
|
}
|
2019-01-15 15:59:35 +00:00
|
|
|
|
2019-03-11 10:31:21 +00:00
|
|
|
#[test]
|
|
|
|
fn upload_revoked_tpk() {
|
|
|
|
let tmpdir = TempDir::new().unwrap();
|
2019-04-26 20:14:57 +00:00
|
|
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
2019-03-11 10:31:21 +00:00
|
|
|
test::test_upload_revoked_tpk(&mut db);
|
2019-06-07 16:18:58 +00:00
|
|
|
db.check_consistency().expect("inconsistent database");
|
2019-03-11 10:31:21 +00:00
|
|
|
}
|
|
|
|
|
2019-01-15 15:59:35 +00:00
|
|
|
#[test]
|
|
|
|
fn uid_revocation() {
|
|
|
|
let tmpdir = TempDir::new().unwrap();
|
2019-04-26 20:14:57 +00:00
|
|
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
2019-01-15 15:59:35 +00:00
|
|
|
|
|
|
|
test::test_uid_revocation(&mut db);
|
2019-06-07 16:18:58 +00:00
|
|
|
db.check_consistency().expect("inconsistent database");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn regenerate() {
|
|
|
|
let tmpdir = TempDir::new().unwrap();
|
|
|
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
|
|
|
|
|
|
|
test::test_regenerate(&mut db);
|
|
|
|
db.check_consistency().expect("inconsistent database");
|
2019-01-15 15:59:35 +00:00
|
|
|
}
|
2019-02-07 20:01:59 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn key_reupload() {
|
|
|
|
let tmpdir = TempDir::new().unwrap();
|
2019-04-26 20:14:57 +00:00
|
|
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
2019-02-07 20:01:59 +00:00
|
|
|
|
|
|
|
test::test_reupload(&mut db);
|
2019-06-07 16:18:58 +00:00
|
|
|
db.check_consistency().expect("inconsistent database");
|
2019-02-07 20:01:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn uid_replacement() {
|
|
|
|
let tmpdir = TempDir::new().unwrap();
|
2019-04-26 20:14:57 +00:00
|
|
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
2019-02-07 20:01:59 +00:00
|
|
|
|
|
|
|
test::test_uid_replacement(&mut db);
|
2019-06-07 16:18:58 +00:00
|
|
|
db.check_consistency().expect("inconsistent database");
|
2019-02-07 20:01:59 +00:00
|
|
|
}
|
2019-02-08 14:46:05 +00:00
|
|
|
|
2019-03-20 10:42:19 +00:00
|
|
|
#[test]
|
|
|
|
fn uid_unlinking() {
|
|
|
|
let tmpdir = TempDir::new().unwrap();
|
2019-04-26 20:14:57 +00:00
|
|
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
2019-03-20 10:42:19 +00:00
|
|
|
test::test_unlink_uid(&mut db);
|
2019-06-07 16:18:58 +00:00
|
|
|
db.check_consistency().expect("inconsistent database");
|
2019-03-20 10:42:19 +00:00
|
|
|
}
|
|
|
|
|
2019-02-22 19:23:24 +00:00
|
|
|
#[test]
|
|
|
|
fn same_email_1() {
|
|
|
|
let tmpdir = TempDir::new().unwrap();
|
2019-04-26 20:14:57 +00:00
|
|
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
2019-02-22 19:23:24 +00:00
|
|
|
|
|
|
|
test::test_same_email_1(&mut db);
|
2019-06-07 16:18:58 +00:00
|
|
|
db.check_consistency().expect("inconsistent database");
|
2019-02-22 19:23:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn same_email_2() {
|
|
|
|
let tmpdir = TempDir::new().unwrap();
|
2019-04-26 20:14:57 +00:00
|
|
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
2019-02-22 19:23:24 +00:00
|
|
|
|
|
|
|
test::test_same_email_2(&mut db);
|
2019-06-07 16:18:58 +00:00
|
|
|
db.check_consistency().expect("inconsistent database");
|
2019-02-22 19:23:24 +00:00
|
|
|
}
|
2019-02-28 17:43:44 +00:00
|
|
|
|
2019-06-11 12:29:24 +00:00
|
|
|
#[test]
|
|
|
|
fn no_selfsig() {
|
|
|
|
let tmpdir = TempDir::new().unwrap();
|
|
|
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
|
|
|
|
|
|
|
test::test_no_selfsig(&mut db);
|
|
|
|
db.check_consistency().expect("inconsistent database");
|
|
|
|
}
|
|
|
|
|
2019-06-08 15:15:46 +00:00
|
|
|
#[test]
|
|
|
|
fn bad_uids() {
|
|
|
|
let tmpdir = TempDir::new().unwrap();
|
|
|
|
let mut db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
|
|
|
|
|
|
|
test::test_bad_uids(&mut db);
|
|
|
|
db.check_consistency().expect("inconsistent database");
|
|
|
|
}
|
|
|
|
|
2019-02-28 17:43:44 +00:00
|
|
|
#[test]
|
|
|
|
fn reverse_fingerprint_to_path() {
|
|
|
|
let tmpdir = TempDir::new().unwrap();
|
2019-04-26 20:14:57 +00:00
|
|
|
let db = Filesystem::new_from_base(tmpdir.path()).unwrap();
|
2019-02-28 17:43:44 +00:00
|
|
|
|
|
|
|
let fp: Fingerprint =
|
|
|
|
"CBCD8F030588653EEDD7E2659B7DD433F254904A".parse().unwrap();
|
|
|
|
|
2019-06-07 22:44:51 +00:00
|
|
|
assert_eq!(Filesystem::path_to_fingerprint(&db.link_by_fingerprint(&fp)),
|
2019-03-13 13:26:57 +00:00
|
|
|
Some(fp.clone()));
|
2019-06-07 16:18:58 +00:00
|
|
|
db.check_consistency().expect("inconsistent database");
|
2019-02-28 17:43:44 +00:00
|
|
|
}
|
2018-08-16 18:35:19 +00:00
|
|
|
}
|