papaleguas 0.0.9

ACME client
Documentation
use std::sync::Arc;

use serde::{Deserialize, Serialize};

use crate::{
    api,
    error::AcmeResult,
    key,
    order::{NewOrderRequest, Order},
    utils::add_field,
    AcmeClientInner, AcmeRequest,
};

#[derive(Debug, Clone)]
pub struct Account {
    pub(crate) inner: Arc<AccountInner>,
}

#[derive(Debug)]
pub(crate) struct AccountInner {
    pub(crate) acme: Arc<AcmeClientInner>,
    pub(crate) kid: String,
    pub(crate) key: key::PrivateKey,
    pub(crate) account: api::Account,
}

impl Account {
    fn new(
        acme: Arc<AcmeClientInner>,
        kid: String,
        key: key::PrivateKey,
        account: api::Account,
    ) -> Self {
        Account {
            inner: Arc::new(AccountInner {
                acme,
                kid,
                account,
                key,
            }),
        }
    }

    pub fn kid(&self) -> &str {
        self.inner.kid.as_str()
    }

    pub fn key(&self) -> &key::PrivateKey {
        &self.inner.key
    }

    pub fn new_order(&self) -> NewOrderRequest {
        NewOrderRequest::new(self)
    }

    pub async fn find_order(&self, url: impl Into<String>) -> AcmeResult<Order> {
        let url = url.into();

        let res = self
            .inner
            .acme
            .send_request(AcmeRequest {
                url: &url,
                kid: Some(self.kid()),
                private_key: &self.inner.key,
                payload: None,
            })
            .await?;

        Ok(Order {
            acme: self.inner.acme.clone(),
            account: self.inner.clone(),
            url,
            order: serde_json::from_slice(&res.into_body())?,
        })
    }

    pub async fn orders_urls(&self) -> AcmeResult<Vec<String>> {
        #[derive(Deserialize)]
        struct Orders {
            orders: Vec<String>,
        }

        match &self.inner.account.orders {
            Some(orders) => {
                let res = self
                    .inner
                    .acme
                    .send_request(AcmeRequest {
                        url: orders,
                        kid: Some(self.kid()),
                        private_key: &self.inner.key,
                        payload: None,
                    })
                    .await?;

                Ok(serde_json::from_slice::<Orders>(&res.into_body())?.orders)
            }
            None => Ok(Vec::new()),
        }
    }
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct NewAccountRequest<'a> {
    #[serde(skip)]
    acme: Arc<AcmeClientInner>,
    #[serde(skip)]
    url: &'a str,
    #[serde(skip)]
    private_key: Option<key::PrivateKey>,
    contacts: Vec<&'a str>,
    terms_of_service_agreed: bool,
    only_return_existing: bool,
}

impl<'a> NewAccountRequest<'a> {
    pub(crate) fn new(acme: Arc<AcmeClientInner>, url: &'a str) -> Self {
        Self {
            acme,
            url,
            private_key: None,
            contacts: Vec::with_capacity(1),
            terms_of_service_agreed: false,
            only_return_existing: false,
        }
    }

    add_field!(contacts, Vec<&'a str>);
    add_field!(terms_of_service_agreed, bool);
    add_field!(only_return_existing, bool);

    pub fn private_key(self, key: impl TryInto<key::PrivateKey>) -> Self {
        Self {
            private_key: key.try_into().ok(),
            ..self
        }
    }

    pub fn contact(self, email: &'a str) -> Self {
        let mut contacts = self.contacts;
        contacts.push(email);
        Self { contacts, ..self }
    }

    #[cfg(feature = "rsa")]
    pub fn with_auto_generated_rsa_key(self) -> Self {
        Self {
            private_key: Some(key::PrivateKey::random_rsa_key(rand::thread_rng())),
            ..self
        }
    }

    pub fn with_auto_generated_ec_key(self) -> Self {
        Self {
            private_key: Some(key::PrivateKey::random_ec_key(rand::thread_rng())),
            ..self
        }
    }

    pub async fn send(self) -> AcmeResult<Account> {
        let payload = serde_json::to_value(&self)?;
        let key = self.private_key.ok_or("Invalid private key")?;

        let res = self
            .acme
            .send_request(AcmeRequest {
                url: self.url,
                private_key: &key,
                kid: None,
                payload: Some(payload),
            })
            .await?;

        let kid = res
            .headers()
            .get(http::header::LOCATION)
            .and_then(|kid| kid.to_str().map(|kid| kid.to_owned()).ok())
            .ok_or("Failed to parse account key id")?;

        let account = serde_json::from_slice(&res.into_body())?;

        Ok(Account::new(self.acme, kid, key, account))
    }
}