cloudpub_common/
config.rs

1use anyhow::{anyhow, Result};
2use serde::{Deserialize, Serialize};
3use std::fmt::{Debug, Formatter};
4use std::ops::Deref;
5use url::Url;
6
7pub use crate::protocol::Protocol;
8
9/// String with Debug implementation that emits "MASKED"
10/// Used to mask sensitive strings when logging
11#[derive(Serialize, Deserialize, Default, PartialEq, Eq, Clone)]
12pub struct MaskedString(pub String);
13
14impl Debug for MaskedString {
15    fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
16        if self.0.is_empty() {
17            f.write_str("EMPTY")
18        } else {
19            #[cfg(debug_assertions)]
20            f.write_str(&self.0)?;
21            #[cfg(not(debug_assertions))]
22            f.write_str("MASKED")?;
23            Ok(())
24        }
25    }
26}
27
28impl Deref for MaskedString {
29    type Target = str;
30    fn deref(&self) -> &Self::Target {
31        &self.0
32    }
33}
34
35impl From<&str> for MaskedString {
36    fn from(s: &str) -> MaskedString {
37        MaskedString(String::from(s))
38    }
39}
40
41#[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Default)]
42pub enum TransportType {
43    #[serde(rename = "websocket")]
44    #[default]
45    Websocket,
46    #[serde(rename = "tcp")]
47    Tcp,
48    #[cfg(feature = "rustls")]
49    #[serde(rename = "tls")]
50    Tls,
51}
52
53#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
54#[serde(deny_unknown_fields)]
55#[derive(Default)]
56pub struct TlsConfig {
57    pub hostname: Option<String>,
58    pub trusted_root: Option<String>,
59    pub pkcs12: Option<String>,
60    pub pkcs12_password: Option<MaskedString>,
61    pub danger_ignore_certificate_verification: Option<bool>,
62}
63
64#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
65#[serde(deny_unknown_fields)]
66pub struct WebsocketConfig {
67    pub tls: bool,
68}
69
70impl Default for WebsocketConfig {
71    fn default() -> Self {
72        Self { tls: true }
73    }
74}
75
76#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
77pub struct TcpConfig {
78    pub proxy: Option<Url>,
79}
80
81#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
82#[serde(deny_unknown_fields)]
83pub struct TransportConfig {
84    #[serde(rename = "type")]
85    pub transport_type: TransportType,
86    pub tcp: TcpConfig,
87    pub tls: Option<TlsConfig>,
88    pub websocket: Option<WebsocketConfig>,
89}
90
91impl Default for TransportConfig {
92    fn default() -> Self {
93        Self {
94            transport_type: TransportType::Websocket,
95            tcp: TcpConfig::default(),
96            tls: TlsConfig::default().into(),
97            websocket: WebsocketConfig::default().into(),
98        }
99    }
100}
101
102impl TransportConfig {
103    pub fn validate(config: &TransportConfig, _is_server: bool) -> Result<()> {
104        config
105            .tcp
106            .proxy
107            .as_ref()
108            .map_or(Ok(()), |u| match u.scheme() {
109                "socks5" => Ok(()),
110                "http" => Ok(()),
111                _ => Err(anyhow!(format!("Unknown proxy scheme: {}", u.scheme()))),
112            })?;
113        match config.transport_type {
114            TransportType::Tcp => Ok(()),
115            #[cfg(feature = "rustls")]
116            TransportType::Tls => {
117                let tls_config = config
118                    .tls
119                    .as_ref()
120                    .ok_or_else(|| anyhow!("Missing TLS configuration"))?;
121                if _is_server {
122                    tls_config
123                        .pkcs12
124                        .as_ref()
125                        .and(tls_config.pkcs12_password.as_ref())
126                        .ok_or_else(|| anyhow!("Missing `pkcs12` or `pkcs12_password`"))?;
127                }
128                Ok(())
129            }
130            TransportType::Websocket => Ok(()),
131        }
132    }
133
134    pub fn notls() -> Self {
135        Self {
136            transport_type: TransportType::Websocket,
137            tcp: TcpConfig::default(),
138            tls: None,
139            websocket: WebsocketConfig { tls: false }.into(),
140        }
141    }
142}