papaleguas 0.0.9

ACME client
Documentation
use serde::{Deserialize, Serialize};
use serde_json::Value;
use time::OffsetDateTime;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DirectoryUrl(pub(crate) String);

impl<T: AsRef<str>> From<T> for DirectoryUrl {
    fn from(url: T) -> Self {
        DirectoryUrl(url.as_ref().to_owned())
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct Directory {
    pub new_nonce: String,
    pub new_account: String,
    pub new_order: String,
    pub revoke_cert: String,
    pub key_change: String,
    pub meta: Option<DirectoryMeta>,
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct DirectoryMeta {
    pub terms_of_service: Option<String>,
    pub website: Option<String>,
    pub caa_identities: Option<Vec<String>>,
    pub external_account_required: Option<bool>,
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Default)]
pub struct Account {
    pub status: String,
    pub orders: Option<String>,
    pub key: Value,
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct Identifier {
    pub r#type: String,
    pub value: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Order {
    pub status: OrderStatus,
    #[serde(default, with = "time::serde::iso8601::option")]
    pub expires: Option<OffsetDateTime>,
    pub identifiers: Vec<Identifier>,
    #[serde(default, with = "time::serde::iso8601::option")]
    pub not_before: Option<OffsetDateTime>,
    #[serde(default, with = "time::serde::iso8601::option")]
    pub not_after: Option<OffsetDateTime>,
    pub error: Option<ServerError>,
    pub authorizations: Vec<String>,
    pub finalize: String,
    pub certificate: Option<String>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum OrderStatus {
    Pending,
    Ready,
    Processing,
    Valid,
    Invalid,
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct Authorization {
    pub identifier: Identifier,
    pub status: AuthorizationStatus,
    #[serde(default, with = "time::serde::iso8601::option")]
    pub expires: Option<OffsetDateTime>,
    pub challenges: Vec<Challenge>,
    pub wildcard: Option<bool>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AuthorizationStatus {
    Pending,
    Valid,
    Invalid,
    Deactivated,
    Expired,
    Revoked,
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(tag = "type")]
pub enum Challenge {
    #[serde(rename = "http-01")]
    Http01(TokenChallenge),
    #[serde(rename = "dns-01")]
    Dns01(TokenChallenge),
    #[serde(rename = "tls-alpn-01")]
    TlsAlpn01(TokenChallenge),
    #[serde(other)]
    Other,
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct TokenChallenge {
    pub url: String,
    pub status: ChallengeStatus,
    pub validated: Option<String>,
    pub error: Option<ServerError>,
    pub token: String,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ChallengeStatus {
    Pending,
    Processing,
    Valid,
    Invalid,
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, thiserror::Error)]
#[error("ServerError({}): {}", r#type, detail.clone().unwrap_or_default())]
pub struct ServerError {
    #[serde(default = "default_type")]
    pub r#type: String,
    pub title: Option<String>,
    pub status: Option<u16>,
    pub detail: Option<String>,
    pub subproblems: Option<Vec<ServerError>>,
}

fn default_type() -> String {
    "about:blank".into()
}

impl ServerError {
    pub fn http_status(&self) -> Option<http::StatusCode> {
        http::StatusCode::from_u16(self.status?).ok()
    }

    pub(crate) fn is_bad_nonce(&self) -> bool {
        self.r#type == "urn:ietf:params:acme:error:badNonce"
    }
}