use std::sync::Arc;
use crate::api::{ApiAccount, ApiDirectory, ApiIdentifier, ApiOrder, ApiRevocation};
use crate::cert::Certificate;
use crate::order::{NewOrder, Order};
use crate::persist::{Persist, PersistKey, PersistKind};
use crate::req::req_expect_header;
use crate::trans::Transport;
use crate::util::{base64url, read_json};
use crate::Result;
mod akey;
pub(crate) use self::akey::AcmeKey;
#[derive(Clone, Debug)]
pub(crate) struct AccountInner<P: Persist> {
pub persist: P,
pub transport: Transport,
pub realm: String,
pub api_account: ApiAccount,
pub api_directory: ApiDirectory,
}
#[derive(Clone)]
pub struct Account<P: Persist> {
inner: Arc<AccountInner<P>>,
}
impl<P: Persist> Account<P> {
pub(crate) fn new(
persist: P,
transport: Transport,
realm: &str,
api_account: ApiAccount,
api_directory: ApiDirectory,
) -> Self {
Account {
inner: Arc::new(AccountInner {
persist,
transport,
realm: realm.to_string(),
api_account,
api_directory,
}),
}
}
pub fn acme_private_key_pem(&self) -> String {
String::from_utf8(self.inner.transport.acme_key().to_pem()).expect("from_utf8")
}
pub fn certificate(&self, primary_name: &str) -> Result<Option<Certificate>> {
let realm = &self.inner.realm;
let persist = &self.inner.persist;
let pk_key = PersistKey::new(realm, PersistKind::PrivateKey, primary_name);
debug!("Read private key: {}", pk_key);
let private_key = persist
.get(&pk_key)?
.and_then(|s| String::from_utf8(s).ok());
let pk_crt = PersistKey::new(realm, PersistKind::Certificate, primary_name);
debug!("Read certificate: {}", pk_crt);
let certificate = persist
.get(&pk_crt)?
.and_then(|s| String::from_utf8(s).ok());
Ok(match (private_key, certificate) {
(Some(k), Some(c)) => Some(Certificate::new(k, c)),
_ => None,
})
}
pub fn new_order(&self, primary_name: &str, alt_names: &[&str]) -> Result<NewOrder<P>> {
let prim_arr = [primary_name];
let domains = prim_arr.iter().chain(alt_names);
let order = ApiOrder {
identifiers: domains
.map(|s| ApiIdentifier {
_type: "dns".into(),
value: s.to_string(),
})
.collect(),
..Default::default()
};
let new_order_url = &self.inner.api_directory.newOrder;
let res = self.inner.transport.call(new_order_url, &order)?;
let order_url = req_expect_header(&res, "location")?;
let api_order: ApiOrder = read_json(res)?;
let order = Order::new(&self.inner, api_order, order_url);
Ok(NewOrder { order })
}
pub fn revoke_certificate(&self, cert: &Certificate, reason: RevocationReason) -> Result<()> {
let certificate = base64url(&cert.certificate_der());
let revoc = ApiRevocation {
certificate,
reason: reason as usize,
};
let url = &self.inner.api_directory.revokeCert;
self.inner.transport.call(url, &revoc)?;
Ok(())
}
pub fn api_account(&self) -> &ApiAccount {
&self.inner.api_account
}
}
pub enum RevocationReason {
Unspecified = 0,
KeyCompromise = 1,
CACompromise = 2,
AffiliationChanged = 3,
Superseded = 4,
CessationOfOperation = 5,
CertificateHold = 6,
RemoveFromCRL = 8,
PrivilegeWithdrawn = 9,
AACompromise = 10,
}
#[cfg(test)]
mod test {
use crate::persist::*;
use crate::*;
#[test]
fn test_create_order() -> Result<()> {
let server = crate::test::with_directory_server();
let url = DirectoryUrl::Other(&server.dir_url);
let persist = MemoryPersist::new();
let dir = Directory::from_url(persist, url)?;
let acc = dir.account("foo@bar.com")?;
let _ = acc.new_order("acmetest.example.com", &[])?;
Ok(())
}
}