use super::super::material::{ADDRESS, DIR, ENTRIES, Role};
use super::{ANCHORS, LOOPBACK, PORT};
use crate::registry::Grade;
use crate::xdg::Env;
use std::path::{Path, PathBuf};
pub const SUBCMD: &str = "wire-certs";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Plan {
pub dir: PathBuf,
pub act: Act,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Act {
Mint {
address: String,
force: bool,
},
Leaf(String, Grade),
}
pub fn plan(
world: &Env,
dir: Option<String>,
host: Option<String>,
port: Option<String>,
force: Option<String>,
leaf: Option<String>,
foot: Option<String>,
) -> Plan {
let dir = stated(dir).map_or_else(|| super::super::material::dir(world), PathBuf::from);
let grade = if stated(foot).is_some() {
Grade::Foot
} else {
Grade::Operator
};
let act = stated(leaf).map_or_else(
|| Act::Mint {
address: format!(
"{}:{}",
stated(host).unwrap_or_else(|| LOOPBACK.to_owned()),
stated(port).unwrap_or_else(|| PORT.to_owned())
),
force: stated(force).is_some(),
},
|cn| Act::Leaf(cn, grade),
);
Plan { dir, act }
}
fn stated(value: Option<String>) -> Option<String> {
value.filter(|v| !v.is_empty())
}
pub const READS: [&str; 6] = [
"WIRE_DIR",
"WIRE_HOST",
"WIRE_PORT",
"FORCE",
"WIRE_LEAF",
"WIRE_FOOT",
];
pub fn perform(plan: &Plan) -> i32 {
match &plan.act {
Act::Mint { address, force } => mint(&plan.dir, address, *force),
Act::Leaf(cn, grade) => leaf(&plan.dir, cn, *grade),
}
}
fn mint(dir: &Path, address: &str, force: bool) -> i32 {
if dir.join(ANCHORS).is_file() && !force {
eprintln!(
"yog {SUBCMD}: {} already holds material; rotating distrusts every certificate \
already issued. Re-run with FORCE=1 if that is what you mean.",
dir.display()
);
return 1;
}
match super::mint(dir, address, force) {
Ok(()) => {
report(dir, address);
0
}
Err(e) => {
eprintln!("yog {SUBCMD}: {e}");
1
}
}
}
fn leaf(dir: &Path, cn: &str, grade: Grade) -> i32 {
if let Err(e) = super::issue(dir, cn, grade) {
eprintln!("yog {SUBCMD}: {e}");
return 1;
}
println!("yog {SUBCMD}: issued a {} leaf for {cn}", word(grade));
for name in [format!("{cn}.pem"), format!("{cn}.key")] {
println!(" {}", dir.join(name).display());
}
let client = Role::Client.leaf();
println!(
" carry those and {} to that box by hand, into its {DIR}/{ENTRIES}/<leaf>/ as \
{client}.pem, {client}.key and {ANCHORS}, beside an {ADDRESS} you state; the common \
name inside, not the basename, is the identity",
dir.join(ANCHORS).display()
);
0
}
fn report(dir: &Path, address: &str) {
println!(
"yog {SUBCMD}: {} holds {}",
dir.display(),
super::artifacts().join(", ")
);
println!(" the engine binds and a local seat dials {address}");
println!(
" issue another client with: {SUBCMD} WIRE_LEAF=<common-name>, and a tool host's with \
{}=1 beside that",
READS[5]
);
}
fn word(grade: Grade) -> &'static str {
match grade {
Grade::Operator => "client",
Grade::Foot => crate::registry::peer::FOOT,
}
}
#[cfg(test)]
mod tests;