warpdrive_proxy/acme/
mod.rs

1//! ACME (Let's Encrypt) certificate provisioning
2//!
3//! This module handles automatic TLS certificate provisioning using the ACME protocol.
4//! It supports:
5//! - HTTP-01 challenge validation
6//! - Certificate generation and renewal
7//! - Certificate storage and loading
8//!
9//! Based on instant-acme for async ACME operations.
10
11mod provisioner;
12mod storage;
13
14pub use provisioner::AcmeProvisioner;
15pub use storage::CertificateStorage;
16
17use std::sync::Arc;
18use tokio::sync::RwLock;
19
20/// ACME challenge response for HTTP-01 validation
21#[derive(Debug, Clone)]
22pub struct ChallengeResponse {
23    /// The token to serve at /.well-known/acme-challenge/{token}
24    pub token: String,
25    /// The key authorization to return as the response body
26    pub key_authorization: String,
27}
28
29/// In-memory challenge storage for HTTP-01 validation
30#[derive(Debug, Clone, Default)]
31pub struct ChallengeStore {
32    challenges: Arc<RwLock<std::collections::HashMap<String, String>>>,
33}
34
35impl ChallengeStore {
36    pub fn new() -> Self {
37        Self::default()
38    }
39
40    /// Store a challenge response for validation
41    pub async fn set(&self, token: String, key_auth: String) {
42        let mut challenges = self.challenges.write().await;
43        challenges.insert(token, key_auth);
44    }
45
46    /// Retrieve a challenge response by token
47    pub async fn get(&self, token: &str) -> Option<String> {
48        let challenges = self.challenges.read().await;
49        challenges.get(token).cloned()
50    }
51
52    /// Remove a challenge after validation
53    pub async fn remove(&self, token: &str) {
54        let mut challenges = self.challenges.write().await;
55        challenges.remove(token);
56    }
57
58    /// Clear all challenges
59    pub async fn clear(&self) {
60        let mut challenges = self.challenges.write().await;
61        challenges.clear()
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[tokio::test]
70    async fn test_challenge_store() {
71        let store = ChallengeStore::new();
72
73        // Test set and get
74        store
75            .set("test_token".to_string(), "test_key_auth".to_string())
76            .await;
77        assert_eq!(
78            store.get("test_token").await,
79            Some("test_key_auth".to_string())
80        );
81
82        // Test remove
83        store.remove("test_token").await;
84        assert_eq!(store.get("test_token").await, None);
85
86        // Test clear
87        store.set("token1".to_string(), "auth1".to_string()).await;
88        store.set("token2".to_string(), "auth2".to_string()).await;
89        store.clear().await;
90        assert_eq!(store.get("token1").await, None);
91        assert_eq!(store.get("token2").await, None);
92    }
93}