allframe_core/grpc/
tls.rs1use std::{env, path::PathBuf};
6
7#[derive(Debug, Clone)]
9pub struct TlsConfig {
10 pub cert_path: PathBuf,
12 pub key_path: PathBuf,
14 pub client_ca_path: Option<PathBuf>,
16}
17
18impl TlsConfig {
19 pub fn new(cert_path: impl Into<PathBuf>, key_path: impl Into<PathBuf>) -> Self {
21 Self {
22 cert_path: cert_path.into(),
23 key_path: key_path.into(),
24 client_ca_path: None,
25 }
26 }
27
28 pub fn with_client_ca(mut self, client_ca_path: impl Into<PathBuf>) -> Self {
30 self.client_ca_path = Some(client_ca_path.into());
31 self
32 }
33
34 pub fn from_env() -> Option<Self> {
41 let cert_path = env::var("GRPC_TLS_CERT")
42 .or_else(|_| env::var("TLS_CERT_PATH"))
43 .ok()?;
44
45 let key_path = env::var("GRPC_TLS_KEY")
46 .or_else(|_| env::var("TLS_KEY_PATH"))
47 .ok()?;
48
49 let client_ca_path = env::var("GRPC_TLS_CLIENT_CA")
50 .or_else(|_| env::var("TLS_CLIENT_CA_PATH"))
51 .ok()
52 .map(PathBuf::from);
53
54 Some(Self {
55 cert_path: PathBuf::from(cert_path),
56 key_path: PathBuf::from(key_path),
57 client_ca_path,
58 })
59 }
60
61 #[cfg(feature = "grpc-tls")]
63 pub fn load(&self) -> Result<(Vec<u8>, Vec<u8>), TlsError> {
64 let cert = std::fs::read(&self.cert_path).map_err(|e| TlsError::CertificateLoad {
65 path: self.cert_path.clone(),
66 source: e.to_string(),
67 })?;
68
69 let key = std::fs::read(&self.key_path).map_err(|e| TlsError::KeyLoad {
70 path: self.key_path.clone(),
71 source: e.to_string(),
72 })?;
73
74 Ok((cert, key))
75 }
76
77 #[cfg(feature = "grpc-tls")]
79 pub fn load_client_ca(&self) -> Result<Option<Vec<u8>>, TlsError> {
80 match &self.client_ca_path {
81 Some(path) => {
82 let ca = std::fs::read(path).map_err(|e| TlsError::ClientCaLoad {
83 path: path.clone(),
84 source: e.to_string(),
85 })?;
86 Ok(Some(ca))
87 }
88 None => Ok(None),
89 }
90 }
91}
92
93#[derive(Debug)]
95pub enum TlsError {
96 CertificateLoad {
98 path: PathBuf,
100 source: String,
102 },
103 KeyLoad {
105 path: PathBuf,
107 source: String,
109 },
110 ClientCaLoad {
112 path: PathBuf,
114 source: String,
116 },
117 Configuration(String),
119}
120
121impl std::fmt::Display for TlsError {
122 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 match self {
124 TlsError::CertificateLoad { path, source } => {
125 write!(f, "Failed to load certificate from {:?}: {}", path, source)
126 }
127 TlsError::KeyLoad { path, source } => {
128 write!(f, "Failed to load key from {:?}: {}", path, source)
129 }
130 TlsError::ClientCaLoad { path, source } => {
131 write!(f, "Failed to load client CA from {:?}: {}", path, source)
132 }
133 TlsError::Configuration(msg) => write!(f, "TLS configuration error: {}", msg),
134 }
135 }
136}
137
138impl std::error::Error for TlsError {}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143
144 #[test]
145 fn test_tls_config_new() {
146 let config = TlsConfig::new("/path/to/cert.pem", "/path/to/key.pem");
147 assert_eq!(config.cert_path, PathBuf::from("/path/to/cert.pem"));
148 assert_eq!(config.key_path, PathBuf::from("/path/to/key.pem"));
149 assert!(config.client_ca_path.is_none());
150 }
151
152 #[test]
153 fn test_tls_config_with_client_ca() {
154 let config = TlsConfig::new("/path/to/cert.pem", "/path/to/key.pem")
155 .with_client_ca("/path/to/ca.pem");
156
157 assert_eq!(
158 config.client_ca_path,
159 Some(PathBuf::from("/path/to/ca.pem"))
160 );
161 }
162
163 #[test]
164 fn test_tls_error_display() {
165 let err = TlsError::CertificateLoad {
166 path: PathBuf::from("/path/to/cert.pem"),
167 source: "file not found".to_string(),
168 };
169 assert!(err.to_string().contains("cert.pem"));
170 assert!(err.to_string().contains("file not found"));
171 }
172}