use std::path::Path;
use anyhow::{Context, Result};
use sequoia_openpgp::parse::Parse;
use sequoia_openpgp::serialize::SerializeInto;
use sequoia_openpgp::{Cert, Fingerprint};
use crate::db::models;
use crate::pgp;
use crate::Oca;
pub fn bridge_new(
oca: &Oca,
remote_cert_file: &Path,
remote_email: Option<&str>,
remote_scope: Option<&str>,
unscoped: bool,
) -> Result<(models::Bridge, Fingerprint)> {
let remote_ca_cert = Cert::from_file(remote_cert_file).context("Failed to read key")?;
let remote_uids: Vec<_> = remote_ca_cert.userids().collect();
if remote_uids.len() != 1 {
return Err(anyhow::anyhow!(
"Expected exactly one User ID in remote CA Cert",
));
}
let remote_uid = remote_uids[0].userid();
let (remote_cert_email, remote_cert_domain) = {
if let Some(remote_email) = remote_uid.email2()? {
let split: Vec<_> = remote_email.split('@').collect();
if split.len() != 2 || split[0] != "openpgp-ca" {
return Err(anyhow::anyhow!(format!(
"Unexpected remote email {remote_email}"
)));
}
let domain = split[1];
(remote_email.to_owned(), domain.to_owned())
} else {
return Err(anyhow::anyhow!("Couldn't get email from remote CA Cert"));
}
};
let email = match remote_email {
None => remote_cert_email,
Some(email) => email.to_owned(),
};
let scope = match remote_scope {
Some(scope) => {
if scope != remote_cert_domain {
return Err(anyhow::anyhow!(
"Scope and domain don't match, currently unsupported"
));
}
scope
}
None => &remote_cert_domain,
};
let regex = domain_to_regex(scope)?;
let scope_regexes = if unscoped { vec![] } else { vec![regex] };
let remote_ca = oca
.secret()
.bridge_to_remote_ca(remote_ca_cert, scope_regexes)?;
let remote_armored = pgp::cert_to_armored(&remote_ca)?;
let remote_fp = remote_ca.fingerprint().to_hex();
let bridge_db = oca
.storage
.bridge_add(&remote_armored, &remote_fp, &email, scope)?;
Ok((bridge_db, remote_ca.fingerprint()))
}
pub fn bridge_revoke(oca: &Oca, email: &str) -> Result<()> {
if let Some(bridge) = oca.storage.bridge_by_email(email)? {
if let Some(db_cert) = oca.storage.cert_by_id(bridge.cert_id)? {
let bridge_cert = pgp::to_cert(db_cert.pub_cert.as_bytes())?;
let (revocation, revoked) = oca.secret().bridge_revoke(&bridge_cert)?;
println!(
"Revocation for the bridge to {}:\n{}",
email,
pgp::revoc_to_armored(&revocation, None)?
);
oca.storage.cert_update(&revoked.to_vec()?)
} else {
Err(anyhow::anyhow!("No cert found for bridge"))
}
} else {
Err(anyhow::anyhow!("Bridge not found"))
}
}
fn domain_to_regex(domain: &str) -> Result<String> {
use addr::parser::DomainName;
use addr::psl::List;
if List.parse_domain_name(domain).is_ok() {
let escaped_domain = &domain.split('.').collect::<Vec<_>>().join("\\.");
Ok(format!("<[^>]+[@.]{escaped_domain}>$"))
} else {
Err(anyhow::anyhow!("Parameter is not a valid domain name"))
}
}