taxy_api/
acme.rs

1use crate::{id::ShortId, subject_name::SubjectName};
2use base64::{engine::general_purpose, Engine as _};
3use serde_default::DefaultFromSerde;
4use serde_derive::{Deserialize, Serialize};
5use utoipa::ToSchema;
6
7#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
8pub struct Acme {
9    #[schema(inline)]
10    #[serde(flatten)]
11    pub config: AcmeConfig,
12    #[schema(value_type = [String], example = json!(["example.com"]))]
13    pub identifiers: Vec<SubjectName>,
14    #[schema(value_type = String, example = "http-01")]
15    pub challenge_type: String,
16}
17
18#[derive(Debug, DefaultFromSerde, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
19pub struct AcmeConfig {
20    #[serde(default = "default_active", skip_serializing_if = "is_true")]
21    pub active: bool,
22    #[serde(default)]
23    #[schema(example = "Let's Encrypt")]
24    pub provider: String,
25    #[schema(example = "60")]
26    #[serde(default = "default_renewal_days")]
27    pub renewal_days: u64,
28}
29
30fn default_active() -> bool {
31    true
32}
33
34fn is_true(b: &bool) -> bool {
35    *b
36}
37
38fn default_renewal_days() -> u64 {
39    60
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
43pub struct AcmeInfo {
44    pub id: ShortId,
45    #[schema(inline)]
46    #[serde(flatten)]
47    pub config: AcmeConfig,
48    #[schema(example = json!(["example.com"]))]
49    pub identifiers: Vec<String>,
50    #[schema(value_type = String, example = "http-01")]
51    pub challenge_type: String,
52    pub next_renewal: Option<i64>,
53}
54
55#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
56pub struct AcmeRequest {
57    #[schema(example = "https://acme-staging-v02.api.letsencrypt.org/directory")]
58    pub server_url: String,
59    #[schema(example = json!(["mailto:admin@example.com"]))]
60    pub contacts: Vec<String>,
61    #[serde(default)]
62    pub eab: Option<ExternalAccountBinding>,
63    #[schema(inline)]
64    #[serde(flatten)]
65    pub acme: Acme,
66}
67
68#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
69pub struct ExternalAccountBinding {
70    #[schema(example = "f9cf7e3faa1aca7e6086")]
71    pub key_id: String,
72    #[schema(value_type = String, example = "TszzWRgQWTUqo04dxmSuKDH06")]
73    #[serde(
74        serialize_with = "serialize_hmac_key",
75        deserialize_with = "deserialize_hmac_key"
76    )]
77    pub hmac_key: Vec<u8>,
78}
79
80fn serialize_hmac_key<S>(hmac_key: &[u8], serializer: S) -> Result<S::Ok, S::Error>
81where
82    S: serde::Serializer,
83{
84    serializer.serialize_str(&general_purpose::URL_SAFE_NO_PAD.encode(hmac_key))
85}
86
87fn deserialize_hmac_key<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
88where
89    D: serde::Deserializer<'de>,
90{
91    use serde::de::Deserialize;
92    let hmac_key = String::deserialize(deserializer)?;
93    general_purpose::URL_SAFE_NO_PAD
94        .decode(hmac_key.as_bytes())
95        .map_err(serde::de::Error::custom)
96}