dinio_sdk/models/
certificate.rs1use std::fmt;
2use std::str::FromStr;
3
4use serde::{Deserialize, Serialize};
5use uuid::Uuid;
6
7use crate::error::{Error, Result};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct Certificate {
14 pub uuid: Uuid,
16 #[serde(default, skip_serializing_if = "Option::is_none")]
18 pub zone_id: Option<Uuid>,
19 pub is_default: bool,
22 pub source: String,
25 pub names: Vec<String>,
27 pub subject: String,
29 pub issuer: String,
31 pub serial: String,
33 pub fingerprint_sha256: String,
35 pub valid_from: i64,
37 pub valid_to: i64,
39 pub expired: bool,
41 pub key_type: String,
43 pub chain_length: u32,
45 pub created_at: i64,
47 pub updated_at: i64,
49}
50
51impl Certificate {
52 pub fn source_kind(&self) -> Option<CertificateSource> {
54 self.source.parse().ok()
55 }
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
60pub enum CertificateSource {
61 Manual,
63 Acme,
66}
67
68impl FromStr for CertificateSource {
69 type Err = Error;
70
71 fn from_str(text: &str) -> Result<Self> {
73 match text.trim().to_ascii_lowercase().as_str() {
74 "manual" => Ok(Self::Manual),
75 "acme" => Ok(Self::Acme),
76 _ => Err(Error::InvalidInput(format!(
77 "unknown certificate source {text:?} (Manual or Acme)"
78 ))),
79 }
80 }
81}
82
83#[derive(Clone, Serialize)]
85pub struct NewCertificate {
86 pub chain_pem: String,
88 pub key_pem: String,
90 pub zone_id: Uuid,
92}
93
94impl fmt::Debug for NewCertificate {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 f.debug_struct("NewCertificate")
97 .field(
98 "chain_pem",
99 &format_args!("<{} bytes>", self.chain_pem.len()),
100 )
101 .field("key_pem", &format_args!("<redacted>"))
102 .field("zone_id", &self.zone_id)
103 .finish()
104 }
105}
106
107#[derive(Clone, Serialize)]
110pub struct CertificateRenewal {
111 pub chain_pem: String,
113 pub key_pem: String,
115}
116
117impl fmt::Debug for CertificateRenewal {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 f.debug_struct("CertificateRenewal")
120 .field(
121 "chain_pem",
122 &format_args!("<{} bytes>", self.chain_pem.len()),
123 )
124 .field("key_pem", &format_args!("<redacted>"))
125 .finish()
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn debug_redacts_key() {
135 let new = NewCertificate {
136 chain_pem: "chain".into(),
137 key_pem: "very-secret-key".into(),
138 zone_id: Uuid::nil(),
139 };
140 assert!(!format!("{new:?}").contains("very-secret-key"));
141 let renewal = CertificateRenewal {
142 chain_pem: "chain".into(),
143 key_pem: "very-secret-key".into(),
144 };
145 assert!(!format!("{renewal:?}").contains("very-secret-key"));
146 }
147
148 #[test]
149 fn certificate_deserializes() {
150 let cert: Certificate = serde_json::from_value(serde_json::json!({
151 "uuid": "00000000-0000-0000-0000-000000000001",
152 "zone_id": "00000000-0000-0000-0000-000000000002",
153 "is_default": false, "source": "Acme",
154 "names": ["go.example.com"], "subject": "CN=go.example.com", "issuer": "CN=R11",
155 "serial": "01", "fingerprint_sha256": "AB:CD", "valid_from": 1, "valid_to": 2,
156 "expired": false, "key_type": "EC P-256", "chain_length": 2,
157 "created_at": 1, "updated_at": 1
158 }))
159 .unwrap();
160 assert_eq!(cert.source_kind(), Some(CertificateSource::Acme));
161 }
162}