1use crate::{
2 CryptoError, Entry, IndexedStorer, IndexedTypeStorer, StorableType, Storer, TypeStorer,
3};
4use async_trait::async_trait;
5use mongodb::bson::Document;
6use once_cell::sync::Lazy;
7use reqwest::StatusCode;
8use serde::{Deserialize, Serialize};
9use std::{
10 error::Error,
11 fmt::{self, Display, Formatter},
12 fs::File,
13 io::Read,
14 sync::{Arc, RwLock},
15};
16
17static CLIENT_TLS_CONFIG: Lazy<RwLock<Arc<Option<ClientTlsConfig>>>> =
18 Lazy::new(|| RwLock::new(Default::default()));
19
20#[derive(Debug)]
21pub enum RedactStorerError {
22 InternalError {
24 source: Box<dyn Error + Send + Sync>,
25 },
26
27 NotFound,
29
30 Pkcs12FileNotReadable { source: std::io::Error },
32
33 ServerCaCertFileNotReadable { source: std::io::Error },
35
36 HttpClientNotBuildable { source: reqwest::Error },
38}
39
40impl Error for RedactStorerError {
41 fn source(&self) -> Option<&(dyn Error + 'static)> {
42 match *self {
43 RedactStorerError::InternalError { ref source } => Some(source.as_ref()),
44 RedactStorerError::NotFound => None,
45 RedactStorerError::Pkcs12FileNotReadable { ref source } => Some(source),
46 RedactStorerError::HttpClientNotBuildable { ref source } => Some(source),
47 RedactStorerError::ServerCaCertFileNotReadable { ref source } => Some(source),
48 }
49 }
50}
51
52impl Display for RedactStorerError {
53 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
54 match *self {
55 RedactStorerError::InternalError { .. } => {
56 write!(f, "Internal error occurred")
57 }
58 RedactStorerError::NotFound => {
59 write!(f, "Requested document not found")
60 }
61 RedactStorerError::Pkcs12FileNotReadable { .. } => {
62 write!(f, "Could not open PKCS12 client TLS file")
63 }
64 RedactStorerError::HttpClientNotBuildable { .. } => {
65 write!(f, "Could not build HTTP request client")
66 }
67 RedactStorerError::ServerCaCertFileNotReadable { .. } => {
68 write!(f, "Could not read server CA certificate")
69 }
70 }
71 }
72}
73
74impl From<RedactStorerError> for CryptoError {
75 fn from(rse: RedactStorerError) -> Self {
76 match rse {
77 RedactStorerError::InternalError { .. } => CryptoError::InternalError {
78 source: Box::new(rse),
79 },
80 RedactStorerError::NotFound => CryptoError::NotFound {
81 source: Box::new(rse),
82 },
83 RedactStorerError::Pkcs12FileNotReadable { .. } => CryptoError::InternalError {
84 source: Box::new(rse),
85 },
86 RedactStorerError::HttpClientNotBuildable { .. } => CryptoError::InternalError {
87 source: Box::new(rse),
88 },
89 RedactStorerError::ServerCaCertFileNotReadable { .. } => CryptoError::InternalError {
90 source: Box::new(rse),
91 },
92 }
93 }
94}
95
96#[derive(Serialize, Deserialize, Debug, Clone, Default)]
97pub struct ClientTlsConfig {
98 pub pkcs12_path: String,
99 pub server_ca_path: Option<String>,
100}
101
102impl ClientTlsConfig {
103 pub fn current() -> Arc<Option<ClientTlsConfig>> {
104 CLIENT_TLS_CONFIG.read().unwrap().clone()
105 }
106
107 pub fn make_current(self) {
108 *CLIENT_TLS_CONFIG.write().unwrap() = Arc::new(Some(self))
109 }
110}
111
112#[derive(Serialize, Deserialize, Debug, Clone)]
113pub struct RedactStorer {
114 url: String,
115}
116
117impl RedactStorer {
120 pub fn new(url: &str) -> Self {
121 Self {
122 url: url.to_owned(),
123 }
124 }
125}
126
127impl From<RedactStorer> for IndexedTypeStorer {
128 fn from(rs: RedactStorer) -> Self {
129 IndexedTypeStorer::Redact(rs)
130 }
131}
132
133impl From<RedactStorer> for TypeStorer {
134 fn from(rs: RedactStorer) -> Self {
135 TypeStorer::Indexed(IndexedTypeStorer::Redact(rs))
136 }
137}
138
139impl RedactStorer {
140 fn get_http_client() -> Result<reqwest::Client, RedactStorerError> {
141 let mut headers = reqwest::header::HeaderMap::new();
142 headers.insert(
143 reqwest::header::CONNECTION,
144 reqwest::header::HeaderValue::from_static("close"),
145 );
146 match *ClientTlsConfig::current() {
147 Some(ref ctc) => {
148 let mut pkcs12_vec: Vec<u8> = vec![];
149 File::open(&ctc.pkcs12_path)
150 .map_err(|source| RedactStorerError::Pkcs12FileNotReadable { source })?
151 .read_to_end(&mut pkcs12_vec)
152 .map_err(|source| RedactStorerError::Pkcs12FileNotReadable { source })?;
153 let pkcs12 = reqwest::Identity::from_pem(&pkcs12_vec)
154 .map_err(|source| RedactStorerError::HttpClientNotBuildable { source })?;
155
156 match &ctc.server_ca_path {
157 Some(path) => {
158 let mut ca_cert_vec: Vec<u8> = vec![];
159 File::open(path)
160 .map_err(|source| RedactStorerError::ServerCaCertFileNotReadable {
161 source,
162 })?
163 .read_to_end(&mut ca_cert_vec)
164 .map_err(|source| RedactStorerError::ServerCaCertFileNotReadable {
165 source,
166 })?;
167 let ca_cert =
168 reqwest::Certificate::from_pem(&ca_cert_vec).map_err(|source| {
169 RedactStorerError::HttpClientNotBuildable { source }
170 })?;
171 Ok::<_, RedactStorerError>(
172 reqwest::Client::builder()
173 .identity(pkcs12)
174 .add_root_certificate(ca_cert)
175 .tls_built_in_root_certs(false)
176 .use_rustls_tls()
177 .default_headers(headers)
178 .build()
179 .map_err(|source| RedactStorerError::HttpClientNotBuildable {
180 source,
181 })?,
182 )
183 }
184 None => Ok::<_, RedactStorerError>(
185 reqwest::Client::builder()
186 .identity(pkcs12)
187 .use_rustls_tls()
188 .default_headers(headers)
189 .build()
190 .map_err(|source| RedactStorerError::HttpClientNotBuildable {
191 source,
192 })?,
193 ),
194 }
195 }
196 None => Ok(reqwest::Client::builder()
197 .use_rustls_tls()
198 .default_headers(headers)
199 .build()
200 .map_err(|source| RedactStorerError::HttpClientNotBuildable { source })?),
201 }
202 }
203}
204
205#[async_trait]
206impl IndexedStorer for RedactStorer {
207 async fn get_indexed<T: StorableType>(
208 &self,
209 path: &str,
210 index: &Option<Document>,
211 ) -> Result<Entry<T>, CryptoError> {
212 let mut req_url = format!("{}/{}?", &self.url, path);
213 if let Some(i) = index {
214 req_url.push_str(format!("index={}", i).as_ref());
215 }
216 let http_client = RedactStorer::get_http_client()?;
217
218 match http_client.get(&req_url).send().await {
219 Ok(r) => Ok(r
220 .error_for_status()
221 .map_err(|source| -> CryptoError {
222 if source.status() == Some(reqwest::StatusCode::NOT_FOUND) {
223 RedactStorerError::NotFound.into()
224 } else {
225 RedactStorerError::InternalError {
226 source: Box::new(source),
227 }
228 .into()
229 }
230 })?
231 .json::<Entry<T>>()
232 .await
233 .map_err(|source| -> CryptoError {
234 RedactStorerError::InternalError {
235 source: Box::new(source),
236 }
237 .into()
238 })?),
239 Err(source) => Err(RedactStorerError::InternalError {
240 source: Box::new(source),
241 }
242 .into()),
243 }
244 }
245
246 async fn list_indexed<T: StorableType>(
247 &self,
248 path: &str,
249 skip: u64,
250 page_size: i64,
251 index: &Option<Document>,
252 ) -> Result<Vec<Entry<T>>, CryptoError> {
253 let mut req_url = format!(
254 "{}/{}?skip={}&page_size={}",
255 &self.url, path, skip, page_size
256 );
257 if let Some(i) = index {
258 req_url.push_str(format!("&index={}", i).as_ref());
259 }
260 let http_client = RedactStorer::get_http_client()?;
261
262 match http_client.get(&req_url).send().await {
263 Ok(r) => Ok(r
264 .error_for_status()
265 .map_err(|source| -> CryptoError {
266 if source.status() == Some(reqwest::StatusCode::NOT_FOUND) {
267 RedactStorerError::NotFound.into()
268 } else {
269 RedactStorerError::InternalError {
270 source: Box::new(source),
271 }
272 .into()
273 }
274 })?
275 .json::<Vec<Entry<T>>>()
276 .await
277 .map_err(|source| -> CryptoError {
278 RedactStorerError::InternalError {
279 source: Box::new(source),
280 }
281 .into()
282 })?),
283 Err(source) => Err(RedactStorerError::InternalError {
284 source: Box::new(source),
285 }
286 .into()),
287 }
288 }
289}
290
291#[async_trait]
292impl Storer for RedactStorer {
293 async fn get<T: StorableType>(&self, path: &str) -> Result<Entry<T>, CryptoError> {
294 self.get_indexed::<T>(path, &T::get_index()).await
295 }
296
297 async fn create<T: StorableType>(&self, entry: Entry<T>) -> Result<Entry<T>, CryptoError> {
298 let value = serde_json::to_value(&entry).map_err(|e| RedactStorerError::InternalError {
299 source: Box::new(e),
300 })?;
301 let http_client = RedactStorer::get_http_client()?;
302
303 http_client
304 .post(&format!("{}/", self.url))
305 .json(&value)
306 .send()
307 .await
308 .and_then(|res| res.error_for_status().map(|_| entry))
309 .map_err(|e| {
310 if let Some(status) = e.status() {
311 if status == StatusCode::NOT_FOUND {
312 RedactStorerError::NotFound.into()
313 } else {
314 RedactStorerError::InternalError {
315 source: Box::new(e),
316 }
317 .into()
318 }
319 } else {
320 RedactStorerError::InternalError {
321 source: Box::new(e),
322 }
323 .into()
324 }
325 })
326 }
327}