use crate::command_runner::CommandRunner; use crate::symbols::Symbol; use async_trait::async_trait; use std::borrow::Borrow; use std::error::Error; use std::fs::File as FsFile; use std::io::Write; use std::marker::PhantomData; use std::path::Path; #[derive(Debug)] pub struct Cert<_C, C, D, P> { domain: D, command_runner: C, root_cert_path: P, account_key_path: P, challenges_path: P, csr_path: P, cert_path: P, phantom: PhantomData<_C>, } impl<_C, C, D, P> Cert<_C, C, D, P> { pub fn new( domain: D, command_runner: C, root_cert_path: P, account_key_path: P, challenges_path: P, csr_path: P, cert_path: P, ) -> Self { Self { domain, command_runner, root_cert_path, account_key_path, challenges_path, csr_path, cert_path, phantom: PhantomData::default(), } } } const DAYS_IN_SECONDS: u32 = 24 * 60 * 60; #[async_trait(?Send)] impl<_C: CommandRunner, C: Borrow<_C>, D: AsRef, P: AsRef> Symbol for Cert<_C, C, D, P> { async fn target_reached(&self) -> Result> { if !self.cert_path.as_ref().exists() { return Ok(false); } let output = self .command_runner .borrow() .run_with_args( "openssl", args![ "x509", "-in", self.cert_path.as_ref(), "-noout", "-subject", "-checkend", (30 * DAYS_IN_SECONDS).to_string(), ], ) .await?; if output.status.success() && output.stdout == format!( "subject=CN = {}\nCertificate will not expire\n", self.domain.as_ref() ) .as_bytes() { Ok( self .command_runner .borrow() .run_successfully( "openssl", args![ "verify", "--untrusted", self.root_cert_path.as_ref(), self.cert_path.as_ref(), ], ) .await .is_ok(), ) } else if output.status.code() == Some(1) && output.stdout == format!( "subject=CN = {}\nCertificate will expire\n", self.domain.as_ref() ) .as_bytes() { Ok(false) } else { Err(String::from_utf8(output.stderr)?.into()) } } async fn execute(&self) -> Result<(), Box> { let output = self .command_runner .borrow() .get_output( "acme-tiny", args![ "--account-key", self.account_key_path.as_ref(), "--csr", self.csr_path.as_ref(), "--acme-dir", self.challenges_path.as_ref(), ], ) .await?; let mut file = FsFile::create(self.cert_path.as_ref())?; file.write_all(&output)?; Ok(()) } } #[cfg(test)] mod test {}