mirror of https://github.com/breard-r/acmed.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
6.8 KiB
247 lines
6.8 KiB
use crate::acmed::{Algorithm, Challenge, Format};
|
|
use crate::errors::Error;
|
|
use log::info;
|
|
use serde::Deserialize;
|
|
use std::fs::{self, File};
|
|
use std::path::Path;
|
|
use std::io::prelude::*;
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct Config {
|
|
pub global: Option<GlobalOptions>,
|
|
pub endpoint: Vec<Endpoint>,
|
|
pub hook: Vec<Hook>,
|
|
pub certificate: Vec<Certificate>,
|
|
}
|
|
|
|
impl Config {
|
|
pub fn get_account_dir(&self) -> String {
|
|
let account_dir = match &self.global {
|
|
Some(g) => match &g.accounts_directory {
|
|
Some(d) => &d,
|
|
None => crate::DEFAULT_ACCOUNTS_DIR,
|
|
},
|
|
None => crate::DEFAULT_ACCOUNTS_DIR,
|
|
};
|
|
account_dir.to_string()
|
|
}
|
|
|
|
pub fn get_hook(&self, name: &str) -> Result<Hook, Error> {
|
|
for hook in self.hook.iter() {
|
|
if name == hook.name {
|
|
return Ok(hook.clone());
|
|
}
|
|
}
|
|
Err(Error::new(&format!("{}: hook not found", name)))
|
|
}
|
|
|
|
pub fn get_cert_file_mode(&self) -> u32 {
|
|
match &self.global {
|
|
Some(g) => match g.cert_file_mode {
|
|
Some(m) => m,
|
|
None => crate::DEFAULT_CERT_FILE_MODE,
|
|
},
|
|
None => crate::DEFAULT_CERT_FILE_MODE,
|
|
}
|
|
}
|
|
|
|
pub fn get_cert_file_user(&self) -> Option<String> {
|
|
match &self.global {
|
|
Some(g) => g.cert_file_user.to_owned(),
|
|
None => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_cert_file_group(&self) -> Option<String> {
|
|
match &self.global {
|
|
Some(g) => g.cert_file_group.to_owned(),
|
|
None => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_pk_file_mode(&self) -> u32 {
|
|
match &self.global {
|
|
Some(g) => match g.pk_file_mode {
|
|
Some(m) => m,
|
|
None => crate::DEFAULT_PK_FILE_MODE,
|
|
},
|
|
None => crate::DEFAULT_PK_FILE_MODE,
|
|
}
|
|
}
|
|
|
|
pub fn get_pk_file_user(&self) -> Option<String> {
|
|
match &self.global {
|
|
Some(g) => g.pk_file_user.to_owned(),
|
|
None => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_pk_file_group(&self) -> Option<String> {
|
|
match &self.global {
|
|
Some(g) => g.pk_file_group.to_owned(),
|
|
None => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct GlobalOptions {
|
|
pub accounts_directory: Option<String>,
|
|
pub certificates_directory: Option<String>,
|
|
pub cert_file_mode: Option<u32>,
|
|
pub cert_file_user: Option<String>,
|
|
pub cert_file_group: Option<String>,
|
|
pub pk_file_mode: Option<u32>,
|
|
pub pk_file_user: Option<String>,
|
|
pub pk_file_group: Option<String>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct Endpoint {
|
|
pub name: String,
|
|
pub url: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
pub struct Hook {
|
|
pub name: String,
|
|
pub cmd: String,
|
|
pub args: Option<Vec<String>>,
|
|
pub stdin: Option<String>,
|
|
pub stdout: Option<String>,
|
|
pub stderr: Option<String>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct Certificate {
|
|
pub email: String,
|
|
pub endpoint: String,
|
|
pub domains: Vec<String>,
|
|
pub challenge: String,
|
|
pub challenge_hooks: Vec<String>,
|
|
pub post_operation_hook: Option<Vec<String>>,
|
|
pub algorithm: Option<String>,
|
|
pub directory: Option<String>,
|
|
pub name: Option<String>,
|
|
pub name_format: Option<String>,
|
|
pub formats: Option<Vec<String>>,
|
|
}
|
|
|
|
impl Certificate {
|
|
pub fn get_algorithm(&self) -> Result<Algorithm, Error> {
|
|
let algo = match &self.algorithm {
|
|
Some(a) => &a,
|
|
None => crate::DEFAULT_ALGO,
|
|
};
|
|
Algorithm::from_str(algo)
|
|
}
|
|
|
|
pub fn get_challenge(&self) -> Result<Challenge, Error> {
|
|
Challenge::from_str(&self.challenge)
|
|
}
|
|
|
|
pub fn get_formats(&self) -> Result<Vec<Format>, Error> {
|
|
let ret = match &self.formats {
|
|
Some(fmts) => {
|
|
let mut lst = Vec::new();
|
|
for f in fmts.iter() {
|
|
lst.push(match f.as_str() {
|
|
"der" => Format::Der,
|
|
"pem" => Format::Pem,
|
|
_ => return Err(Error::new(&format!("{}: unknown format.", f))),
|
|
});
|
|
}
|
|
lst.sort();
|
|
lst.dedup();
|
|
lst
|
|
}
|
|
None => vec![crate::DEFAULT_FMT],
|
|
};
|
|
Ok(ret)
|
|
}
|
|
|
|
pub fn get_crt_name(&self) -> String {
|
|
match &self.name {
|
|
Some(n) => n.to_string(),
|
|
None => self.domains.first().unwrap().to_string(),
|
|
}
|
|
}
|
|
|
|
pub fn get_crt_name_format(&self) -> String {
|
|
match &self.name_format {
|
|
Some(n) => n.to_string(),
|
|
None => crate::DEFAULT_CERT_FORMAT.to_string(),
|
|
}
|
|
}
|
|
|
|
pub fn get_crt_dir(&self, cnf: &Config) -> String {
|
|
let crt_directory = match &self.directory {
|
|
Some(d) => &d,
|
|
None => match &cnf.global {
|
|
Some(g) => match &g.certificates_directory {
|
|
Some(d) => &d,
|
|
None => crate::DEFAULT_CERT_DIR,
|
|
},
|
|
None => crate::DEFAULT_CERT_DIR,
|
|
},
|
|
};
|
|
crt_directory.to_string()
|
|
}
|
|
|
|
pub fn get_remote_url(&self, cnf: &Config) -> Result<String, Error> {
|
|
for endpoint in cnf.endpoint.iter() {
|
|
if endpoint.name == self.endpoint {
|
|
return Ok(endpoint.url.to_owned());
|
|
}
|
|
}
|
|
Err(Error::new(&format!("{}: unknown endpoint.", self.endpoint)))
|
|
}
|
|
|
|
pub fn get_challenge_hooks(&self, cnf: &Config) -> Result<Vec<Hook>, Error> {
|
|
let mut res = vec![];
|
|
for name in self.challenge_hooks.iter() {
|
|
let h = cnf.get_hook(&name)?;
|
|
res.push(h);
|
|
}
|
|
Ok(res)
|
|
}
|
|
|
|
pub fn get_post_operation_hook(&self, cnf: &Config) -> Result<Vec<Hook>, Error> {
|
|
let mut res = vec![];
|
|
match &self.post_operation_hook {
|
|
Some(po_hooks) => for name in po_hooks.iter() {
|
|
let h = cnf.get_hook(&name)?;
|
|
res.push(h);
|
|
},
|
|
None => {}
|
|
};
|
|
Ok(res)
|
|
}
|
|
}
|
|
|
|
fn create_dir(path: &str) -> Result<(), Error> {
|
|
if Path::new(path).is_dir() {
|
|
Ok(())
|
|
} else {
|
|
fs::create_dir_all(path)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn init_directories(config: &Config) -> Result<(), Error> {
|
|
create_dir(&config.get_account_dir())?;
|
|
for crt in config.certificate.iter() {
|
|
create_dir(&crt.get_crt_dir(config))?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn from_file(file: &str) -> Result<Config, Error> {
|
|
info!("Loading configuration file: {}", file);
|
|
let mut file = File::open(file)?;
|
|
let mut contents = String::new();
|
|
file.read_to_string(&mut contents)?;
|
|
let config: Config = toml::from_str(&contents)?;
|
|
init_directories(&config)?;
|
|
Ok(config)
|
|
}
|