1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! OCSP certificate validation
//!
//! ## Overview
//! - Configure OCSP settings
//! - Query certificate status
//! - Test OCSP connectivity
use crate::client::RestClient;
use crate::error::Result;
use serde::{Deserialize, Serialize};
use serde_json::Value;
/// OCSP configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OcspConfig {
/// Whether to enable/disable OCSP mechanism for the cluster
pub enabled: bool,
/// OCSP responder URL (required when OCSP is enabled)
#[serde(skip_serializing_if = "Option::is_none")]
pub responder_url: Option<String>,
/// Response timeout in seconds
#[serde(skip_serializing_if = "Option::is_none")]
pub response_timeout: Option<u32>,
/// Query frequency in seconds - how often to check OCSP status
#[serde(skip_serializing_if = "Option::is_none")]
pub query_frequency: Option<u32>,
/// Recovery frequency in seconds - how often to retry after failure
#[serde(skip_serializing_if = "Option::is_none")]
pub recovery_frequency: Option<u32>,
/// Maximum number of recovery attempts
#[serde(skip_serializing_if = "Option::is_none")]
pub recovery_max_tries: Option<u32>,
}
/// OCSP status information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OcspStatus {
/// Overall OCSP status (e.g., "GOOD", "REVOKED", "UNKNOWN")
pub status: String,
/// Timestamp of last OCSP response update
#[serde(skip_serializing_if = "Option::is_none")]
pub last_update: Option<String>,
/// Timestamp when the next OCSP update is expected
#[serde(skip_serializing_if = "Option::is_none")]
pub next_update: Option<String>,
/// Certificate status from OCSP response ("GOOD", "REVOKED", "UNKNOWN")
#[serde(skip_serializing_if = "Option::is_none")]
pub certificate_status: Option<String>,
/// Timestamp when certificate was revoked (if revoked)
#[serde(skip_serializing_if = "Option::is_none")]
pub revocation_time: Option<String>,
/// Reason for certificate revocation (if revoked)
#[serde(skip_serializing_if = "Option::is_none")]
pub revocation_reason: Option<String>,
}
/// OCSP test result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OcspTestResult {
/// Whether the OCSP test was successful
pub success: bool,
/// Test result message or error description
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
/// Response time from OCSP server in milliseconds
#[serde(skip_serializing_if = "Option::is_none")]
pub response_time_ms: Option<u32>,
}
/// OCSP handler for managing OCSP configuration
pub struct OcspHandler {
client: RestClient,
}
impl OcspHandler {
/// Create a new handler bound to the given REST client.
pub fn new(client: RestClient) -> Self {
OcspHandler { client }
}
/// Get OCSP configuration
pub async fn get_config(&self) -> Result<OcspConfig> {
self.client.get("/v1/ocsp").await
}
/// Update OCSP configuration
pub async fn update_config(&self, config: OcspConfig) -> Result<OcspConfig> {
self.client.put("/v1/ocsp", &config).await
}
/// Get OCSP status
pub async fn get_status(&self) -> Result<OcspStatus> {
self.client.get("/v1/ocsp/status").await
}
/// Test OCSP connectivity.
///
/// `POST /v1/ocsp/test`. The REST API documents this verb as POST;
/// the previous implementation used GET, which is not in the spec
/// and returns 404/405 against modern clusters.
pub async fn test(&self) -> Result<OcspTestResult> {
self.client
.post("/v1/ocsp/test", &serde_json::Value::Null)
.await
}
/// Test OCSP via POST. Deprecated alias for [`Self::test`], which now
/// uses POST itself per the documented verb.
#[deprecated(
since = "0.9.0",
note = "use `test`; `test` now uses POST per the spec"
)]
pub async fn test_post(&self) -> Result<OcspTestResult> {
self.test().await
}
/// Trigger OCSP query
pub async fn query(&self) -> Result<()> {
self.client
.post_action("/v1/ocsp/query", &Value::Null)
.await
}
/// Clear OCSP cache
pub async fn clear_cache(&self) -> Result<()> {
self.client.delete("/v1/ocsp/cache").await
}
}