1pub use async_trait::async_trait;
3
4use reqwest::{
5 header::{self, HeaderMap, HeaderValue},
6 Client, Response,
7};
8pub use reqwest::{Error, StatusCode};
9
10use std::ops::Deref;
11
12use crate::{
13 api_objects::{SclEvent, SclObject},
14 tls_config::TlsConfig,
15};
16
17use crate::LogError;
18
19use self::error::ClientError;
20
21pub mod error {
23 pub type Result<T> = std::result::Result<T, ClientError>;
25
26 #[derive(Debug)]
28 pub enum ClientError {
29 Custom(String),
31 Http(reqwest::Error),
33 Decode(serde_json::Error),
35 }
36
37 impl std::fmt::Display for ClientError {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 use ClientError::*;
40 match self {
41 Custom(e) => e.fmt(f),
42 Http(e) => e.fmt(f),
43 Decode(e) => e.fmt(f),
44 }
45 }
46 }
47
48 impl std::error::Error for ClientError {}
49
50 impl From<reqwest::Error> for ClientError {
51 fn from(e: reqwest::Error) -> Self {
52 Self::Http(e)
53 }
54 }
55
56 impl From<serde_json::Error> for ClientError {
57 fn from(e: serde_json::Error) -> Self {
58 Self::Decode(e)
59 }
60 }
61}
62
63#[async_trait]
66pub trait EventHandler {
67 type Item: SclObject;
68 async fn handle_event(&mut self, event: SclEvent<Self::Item>);
69}
70
71#[derive(Clone, Debug, Default)]
87pub struct SclRequest<'a, T: SclObject> {
88 pub obj_name: Option<String>,
89 pub sc_name: Option<String>,
90 pub body: Option<&'a T>,
91 pub query: &'a [(&'a str, &'a str)],
92}
93
94impl<'a, T: SclObject> SclRequest<'a, T> {
95 pub fn new() -> Self {
97 Self {
98 obj_name: None,
99 body: None,
100 sc_name: None,
101 query: &[],
102 }
103 }
104
105 pub fn obj_name(mut self, name: &str) -> Self {
107 self.obj_name = Some(name.into());
108 self
109 }
110
111 pub fn body(mut self, obj: &'a T) -> Self {
113 self.body = Some(obj);
114 self
115 }
116
117 pub fn sc(mut self, sc_name: &str) -> Self {
120 self.sc_name = Some(sc_name.into());
121 self
122 }
123
124 pub fn query(mut self, query: &'a [(&'a str, &'a str)]) -> Self {
126 self.query = query;
127 self
128 }
129
130 pub fn url_path(&self) -> String {
132 T::api_endpoint(self.sc_name.as_deref(), self.obj_name.as_deref())
133 }
134}
135
136#[derive(Clone)]
177pub struct SclClient {
178 pub api_url: String,
179 client: Client,
180}
181
182impl SclClient {
183 pub fn new(api_url: String, client: Client) -> Self {
185 Self { api_url, client }
186 }
187
188 pub async fn list<T: SclObject>(&self, req: &SclRequest<'_, T>) -> Result<Vec<T>, Error> {
191 let url = format!("{}{}", self.api_url, req.url_path());
192 self.client
193 .get(url)
194 .query(req.query)
195 .send()
196 .await
197 .log_err()?
198 .error_for_status()
199 .log_err()?
200 .json::<Vec<T>>()
201 .await
202 }
203
204 pub async fn show<T: SclObject>(&self, req: &SclRequest<'_, T>) -> Result<T, Error> {
206 let url = format!("{}{}", self.api_url, req.url_path());
207 self.client
208 .get(url)
209 .send()
210 .await
211 .log_err()?
212 .error_for_status()
213 .log_err()?
214 .json::<T>()
215 .await
216 }
217
218 pub async fn delete<T: SclObject>(&self, req: &SclRequest<'_, T>) -> Result<(), Error> {
220 let url = format!("{}{}", self.api_url, req.url_path());
221 self.client
222 .delete(url)
223 .send()
224 .await
225 .log_err()?
226 .error_for_status()
227 .log_err()?;
228 Ok(())
229 }
230
231 pub async fn create<T: SclObject>(&self, req: &SclRequest<'_, T>) -> Result<(), Error> {
233 let url = format!("{}{}", self.api_url, req.url_path());
234 match req.body {
235 Some(body) => self.client.post(url).json(body),
236 None => self.client.post(url),
237 }
238 .send()
239 .await
240 .log_err()?
241 .error_for_status()
242 .log_err()?;
243 Ok(())
244 }
245
246 pub async fn update<T: SclObject>(&self, req: &SclRequest<'_, T>) -> Result<(), Error> {
248 let url = format!("{}{}", self.api_url, req.url_path());
249 match req.body {
250 Some(body) => self.client.put(url).json(body),
251 None => self.client.put(url),
252 }
253 .send()
254 .await
255 .log_err()?
256 .error_for_status()
257 .log_err()?;
258 Ok(())
259 }
260
261 pub async fn watch<T: EventHandler>(&self, mut handler: T) -> Result<(), reqwest::Error> {
269 let url = format!(
270 "{}/watch{}",
271 self.api_url,
272 T::Item::api_endpoint(None, None)
273 );
274 let mut resp = self.client.get(url).send().await?.error_for_status()?;
275 while let Some(bytes) = resp.chunk().await.log_err()? {
276 match serde_json::from_slice::<SclEvent<T::Item>>(&bytes) {
277 Ok(event) => handler.handle_event(event).await,
278 Err(err) => log::error!("{}", err),
279 };
280 }
281 Ok(())
282 }
283
284 pub async fn recv_event<T: SclObject>(resp: &mut Response) -> error::Result<SclEvent<T>> {
288 if let Some(bytes) = resp.chunk().await.log_err()? {
289 Ok(serde_json::from_slice::<SclEvent<T>>(&bytes)?)
290 } else {
291 Err(error::ClientError::Custom(
292 "Empty Chunk: Response body has been exhausted.".to_string(),
293 ))
294 }
295 }
296}
297
298impl Deref for SclClient {
299 type Target = Client;
300
301 fn deref(&self) -> &Self::Target {
302 &self.client
303 }
304}
305
306pub fn reqwest_client(
307 additional_headers: Option<HeaderMap<HeaderValue>>,
308 tls: Option<&TlsConfig>,
309) -> Result<reqwest::Client, Box<dyn std::error::Error>> {
310 let mut headers = HeaderMap::new();
311 headers.insert(header::ACCEPT, HeaderValue::from_static("application/json"));
313 headers.insert(
314 header::CONTENT_TYPE,
315 HeaderValue::from_static("application/json"),
316 );
317 if let Some(additional_headers) = additional_headers {
319 for (key, value) in additional_headers.iter() {
320 headers.insert(key, value.clone());
321 }
322 }
323
324 let mut client_builder = Client::builder().default_headers(headers);
325
326 if let Some(tls) = tls {
327 use rustls::{
328 client::{ClientConfig, ServerCertVerifier},
329 Certificate, PrivateKey,
330 };
331 use std::sync::Arc;
332 let ca_certs: Vec<Certificate> = rustls_pemfile::certs(&mut tls.ca_cert.as_ref())?
337 .into_iter()
338 .map(Certificate)
339 .collect();
340
341 let client_cert = rustls_pemfile::certs(&mut tls.client_cert.as_ref())?
343 .into_iter()
344 .map(Certificate)
345 .collect();
346 let client_key = PrivateKey(
348 rustls_pemfile::pkcs8_private_keys(&mut tls.client_key.as_ref())?
349 .first()
350 .ok_or("No PKCS8-encoded private key found.".to_string())?
351 .to_vec(),
352 );
353 let crls = {
355 use rustls::{server::UnparsedCertRevocationList, CertRevocationListError};
356
357 rustls_pemfile::crls(&mut tls.crl.as_ref())
358 .map_err(|e| ClientError::Custom(format!("Failed to parse CRL file: {e:?}")))?
359 .into_iter()
360 .map(|crl| UnparsedCertRevocationList(crl).parse())
361 .collect::<Result<Vec<webpki::OwnedCertRevocationList>, CertRevocationListError>>()
362 .map_err(|e| format!("Failed to parse one or more CRL: {e:?}"))?
363 };
364
365 let custom_verifier = verify::WebPkiVerifierWithCrl::new(ca_certs, crls);
367 let verifier: Arc<dyn ServerCertVerifier> = Arc::new(custom_verifier);
368
369 let tls_config: ClientConfig = ClientConfig::builder()
373 .with_safe_defaults()
374 .with_custom_certificate_verifier(verifier)
375 .with_client_auth_cert(client_cert, client_key)?;
376
377 client_builder = client_builder.use_preconfigured_tls(tls_config);
378 }
379
380 Ok(client_builder.build()?)
381}
382
383mod verify {
384 use rustls::{
385 client::{verify_server_name, ServerCertVerified, ServerCertVerifier},
386 server::ParsedCertificate,
387 Certificate, Error,
388 };
389
390 mod rustls_verifier {
398 type SignatureAlgorithms = &'static [&'static webpki::SignatureAlgorithm];
399 pub(super) static SUPPORTED_SIG_ALGS: SignatureAlgorithms = &[
402 &webpki::ECDSA_P256_SHA256,
403 &webpki::ECDSA_P256_SHA384,
404 &webpki::ECDSA_P384_SHA256,
405 &webpki::ECDSA_P384_SHA384,
406 &webpki::ED25519,
407 &webpki::RSA_PSS_2048_8192_SHA256_LEGACY_KEY,
408 &webpki::RSA_PSS_2048_8192_SHA384_LEGACY_KEY,
409 &webpki::RSA_PSS_2048_8192_SHA512_LEGACY_KEY,
410 &webpki::RSA_PKCS1_2048_8192_SHA256,
411 &webpki::RSA_PKCS1_2048_8192_SHA384,
412 &webpki::RSA_PKCS1_2048_8192_SHA512,
413 &webpki::RSA_PKCS1_3072_8192_SHA384,
414 ];
415
416 pub(super) fn pki_error(error: webpki::Error) -> rustls::Error {
417 use rustls::{CertRevocationListError, CertificateError};
418 use std::sync::Arc;
419 use webpki::Error::*;
420 match error {
421 BadDer | BadDerTime => CertificateError::BadEncoding.into(),
422 CertNotValidYet => CertificateError::NotValidYet.into(),
423 CertExpired | InvalidCertValidity => CertificateError::Expired.into(),
424 UnknownIssuer => CertificateError::UnknownIssuer.into(),
425 CertNotValidForName => CertificateError::NotValidForName.into(),
426 CertRevoked => CertificateError::Revoked.into(),
427 IssuerNotCrlSigner => CertRevocationListError::IssuerInvalidForCrl.into(),
428
429 InvalidSignatureForPublicKey
430 | UnsupportedSignatureAlgorithm
431 | UnsupportedSignatureAlgorithmForPublicKey => {
432 CertificateError::BadSignature.into()
433 }
434
435 InvalidCrlSignatureForPublicKey
436 | UnsupportedCrlSignatureAlgorithm
437 | UnsupportedCrlSignatureAlgorithmForPublicKey => {
438 CertRevocationListError::BadSignature.into()
439 }
440
441 _ => CertificateError::Other(Arc::new(error)).into(),
442 }
443 }
444 }
445 pub struct WebPkiVerifierWithCrl {
449 roots: Vec<Certificate>,
450 crls: Vec<webpki::OwnedCertRevocationList>,
451 }
452
453 impl WebPkiVerifierWithCrl {
454 pub fn new(roots: Vec<Certificate>, crls: Vec<webpki::OwnedCertRevocationList>) -> Self {
455 Self { roots, crls }
456 }
457 }
458
459 impl ServerCertVerifier for WebPkiVerifierWithCrl {
460 fn verify_server_cert(
461 &self,
462 end_entity: &Certificate,
463 intermediates: &[Certificate],
464 server_name: &rustls::ServerName,
465 _scts: &mut dyn Iterator<Item = &[u8]>,
466 ocsp_response: &[u8],
467 now: std::time::SystemTime,
468 ) -> Result<ServerCertVerified, Error> {
469 let cert = webpki::EndEntityCert::try_from(end_entity.0.as_ref()).unwrap();
470
471 let chain: Vec<&[u8]> = intermediates.iter().map(|cert| cert.0.as_ref()).collect();
472 let trust_roots: Vec<webpki::TrustAnchor> = self
473 .roots
474 .iter()
475 .map(|crt| webpki::TrustAnchor::try_from_cert_der(&crt.0).unwrap())
476 .collect();
477 let webpki_now =
478 webpki::Time::try_from(now).map_err(|_| Error::FailedToGetCurrentTime)?;
479
480 #[allow(trivial_casts)] let crls = self
482 .crls
483 .iter()
484 .map(|crl| crl as &dyn webpki::CertRevocationList)
485 .collect::<Vec<_>>();
486
487 cert.verify_for_usage(
488 rustls_verifier::SUPPORTED_SIG_ALGS,
489 &trust_roots,
490 &chain,
491 webpki_now,
492 webpki::KeyUsage::server_auth(),
493 crls.as_slice(),
494 )
495 .map_err(rustls_verifier::pki_error)?;
496
497 if !ocsp_response.is_empty() {
498 log::trace!("Unvalidated OCSP response: {:?}", ocsp_response.to_vec());
499 }
500 let cert = ParsedCertificate::try_from(end_entity)?;
501
502 verify_server_name(&cert, server_name)?;
503 Ok(ServerCertVerified::assertion())
504 }
505 }
506}