use std::path::PathBuf;
pub mod directories {
pub const LETS_ENCRYPT_PRODUCTION: &str = "https://acme-v02.api.letsencrypt.org/directory";
pub const LETS_ENCRYPT_STAGING: &str = "https://acme-staging-v02.api.letsencrypt.org/directory";
pub const ZEROSSL: &str = "https://acme.zerossl.com/v2/DV90";
pub const BUYPASS: &str = "https://api.buypass.com/acme/directory";
pub const GOOGLE: &str = "https://dv.acme-v02.api.pki.goog/directory";
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ChallengeType {
Http01,
Dns01,
TlsAlpn01,
}
#[derive(Debug, Clone)]
pub struct AcmeConfig {
pub directory_url: String,
pub contact_email: Vec<String>,
pub domains: Vec<String>,
pub challenge_type: ChallengeType,
pub cert_dir: PathBuf,
pub account_dir: PathBuf,
pub renew_before_days: u32,
pub accept_tos: bool,
pub eab_kid: Option<String>,
pub eab_hmac_key: Option<String>,
}
impl AcmeConfig {
pub fn new(
directory_url: impl Into<String>,
contact_email: Vec<String>,
domains: Vec<String>,
) -> Self {
Self {
directory_url: directory_url.into(),
contact_email,
domains,
challenge_type: ChallengeType::Http01,
cert_dir: PathBuf::from("./certs"),
account_dir: PathBuf::from("./accounts"),
renew_before_days: 30,
accept_tos: false,
eab_kid: None,
eab_hmac_key: None,
}
}
pub fn lets_encrypt_production(contact_email: Vec<String>, domains: Vec<String>) -> Self {
Self::new(directories::LETS_ENCRYPT_PRODUCTION, contact_email, domains)
}
pub fn lets_encrypt_staging(contact_email: Vec<String>, domains: Vec<String>) -> Self {
Self::new(directories::LETS_ENCRYPT_STAGING, contact_email, domains)
}
pub fn zerossl(
contact_email: Vec<String>,
domains: Vec<String>,
eab_kid: String,
eab_hmac_key: String,
) -> Self {
let mut config = Self::new(directories::ZEROSSL, contact_email, domains);
config.eab_kid = Some(eab_kid);
config.eab_hmac_key = Some(eab_hmac_key);
config
}
pub fn with_challenge_type(mut self, challenge_type: ChallengeType) -> Self {
self.challenge_type = challenge_type;
self
}
pub fn with_cert_dir(mut self, cert_dir: PathBuf) -> Self {
self.cert_dir = cert_dir;
self
}
pub fn with_account_dir(mut self, account_dir: PathBuf) -> Self {
self.account_dir = account_dir;
self
}
pub fn with_renew_before_days(mut self, days: u32) -> Self {
self.renew_before_days = days;
self
}
pub fn with_accept_tos(mut self, accept: bool) -> Self {
self.accept_tos = accept;
self
}
pub fn with_eab(mut self, kid: String, hmac_key: String) -> Self {
self.eab_kid = Some(kid);
self.eab_hmac_key = Some(hmac_key);
self
}
}
impl Default for AcmeConfig {
fn default() -> Self {
Self::lets_encrypt_staging(
vec!["admin@example.com".to_string()],
vec!["example.com".to_string()],
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_acme_config_new() {
let config = AcmeConfig::new(
directories::LETS_ENCRYPT_PRODUCTION,
vec!["test@example.com".to_string()],
vec!["example.com".to_string()],
);
assert_eq!(config.directory_url, directories::LETS_ENCRYPT_PRODUCTION);
assert_eq!(config.contact_email, vec!["test@example.com"]);
assert_eq!(config.domains, vec!["example.com"]);
assert_eq!(config.challenge_type, ChallengeType::Http01);
}
#[test]
fn test_lets_encrypt_production() {
let config = AcmeConfig::lets_encrypt_production(
vec!["admin@example.com".to_string()],
vec!["example.com".to_string()],
);
assert_eq!(config.directory_url, directories::LETS_ENCRYPT_PRODUCTION);
}
#[test]
fn test_lets_encrypt_staging() {
let config = AcmeConfig::lets_encrypt_staging(
vec!["admin@example.com".to_string()],
vec!["example.com".to_string()],
);
assert_eq!(config.directory_url, directories::LETS_ENCRYPT_STAGING);
}
#[test]
fn test_builder_pattern() {
let config = AcmeConfig::lets_encrypt_production(
vec!["admin@example.com".to_string()],
vec!["example.com".to_string()],
)
.with_challenge_type(ChallengeType::Dns01)
.with_cert_dir(PathBuf::from("/etc/certs"))
.with_renew_before_days(14)
.with_accept_tos(true);
assert_eq!(config.challenge_type, ChallengeType::Dns01);
assert_eq!(config.cert_dir, PathBuf::from("/etc/certs"));
assert_eq!(config.renew_before_days, 14);
assert!(config.accept_tos);
}
#[test]
fn test_zerossl_config() {
let config = AcmeConfig::zerossl(
vec!["admin@example.com".to_string()],
vec!["example.com".to_string()],
"kid123".to_string(),
"hmac456".to_string(),
);
assert_eq!(config.directory_url, directories::ZEROSSL);
assert_eq!(config.eab_kid, Some("kid123".to_string()));
assert_eq!(config.eab_hmac_key, Some("hmac456".to_string()));
}
}