bitwarden_server_communication_config/
config.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize)]
5#[cfg_attr(
6 feature = "wasm",
7 derive(tsify::Tsify),
8 tsify(into_wasm_abi, from_wasm_abi)
9)]
10pub struct ServerCommunicationConfig {
11 pub bootstrap: BootstrapConfig,
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17#[cfg_attr(
18 feature = "wasm",
19 derive(tsify::Tsify),
20 tsify(into_wasm_abi, from_wasm_abi)
21)]
22#[serde(tag = "type", rename_all = "camelCase")]
23pub enum BootstrapConfig {
24 Direct,
26 SsoCookieVendor(SsoCookieVendorConfig),
28}
29
30#[derive(Clone, Serialize, Deserialize)]
34#[cfg_attr(
35 feature = "wasm",
36 derive(tsify::Tsify),
37 tsify(into_wasm_abi, from_wasm_abi)
38)]
39pub struct SsoCookieVendorConfig {
40 pub idp_login_url: String,
42 pub cookie_name: String,
44 pub cookie_domain: String,
46 pub cookie_value: Option<String>,
48}
49
50impl std::fmt::Debug for SsoCookieVendorConfig {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 f.debug_struct("SsoCookieVendorConfig")
54 .field("idp_login_url", &self.idp_login_url)
55 .field("cookie_name", &self.cookie_name)
56 .field("cookie_domain", &self.cookie_domain)
57 .field("cookie_value", &"[REDACTED]")
58 .finish()
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn direct_bootstrap_serialization() {
68 let config = ServerCommunicationConfig {
69 bootstrap: BootstrapConfig::Direct,
70 };
71
72 let json = serde_json::to_string(&config).unwrap();
73 assert!(json.contains("\"type\":\"direct\""));
74
75 let deserialized: ServerCommunicationConfig = serde_json::from_str(&json).unwrap();
76 assert!(matches!(deserialized.bootstrap, BootstrapConfig::Direct));
77 }
78
79 #[test]
80 fn sso_cookie_vendor_serialization() {
81 let config = ServerCommunicationConfig {
82 bootstrap: BootstrapConfig::SsoCookieVendor(SsoCookieVendorConfig {
83 idp_login_url: "https://timeloop-auth.acme.com/login".to_string(),
84 cookie_name: "ALBAuthSessionCookie".to_string(),
85 cookie_domain: "vault.example.com".to_string(),
86 cookie_value: None,
87 }),
88 };
89
90 let json = serde_json::to_string(&config).unwrap();
91 assert!(json.contains("\"type\":\"ssoCookieVendor\""));
92 assert!(json.contains("timeloop-auth.acme.com"));
93 assert!(json.contains("ALBAuthSessionCookie"));
94
95 let deserialized: ServerCommunicationConfig = serde_json::from_str(&json).unwrap();
96 if let BootstrapConfig::SsoCookieVendor(vendor_config) = deserialized.bootstrap {
97 assert_eq!(
98 vendor_config.idp_login_url,
99 "https://timeloop-auth.acme.com/login"
100 );
101 assert_eq!(vendor_config.cookie_name, "ALBAuthSessionCookie");
102 assert_eq!(vendor_config.cookie_domain, "vault.example.com");
103 assert!(vendor_config.cookie_value.is_none());
104 } else {
105 panic!("Expected SsoCookieVendor variant");
106 }
107 }
108
109 #[test]
110 fn cookie_value_some_and_none() {
111 let config_none = SsoCookieVendorConfig {
113 idp_login_url: "https://example.com".to_string(),
114 cookie_name: "TestCookie".to_string(),
115 cookie_domain: "example.com".to_string(),
116 cookie_value: None,
117 };
118
119 let json_none = serde_json::to_string(&config_none).unwrap();
120 let deserialized_none: SsoCookieVendorConfig = serde_json::from_str(&json_none).unwrap();
121 assert!(deserialized_none.cookie_value.is_none());
122
123 let config_some = SsoCookieVendorConfig {
125 idp_login_url: "https://example.com".to_string(),
126 cookie_name: "TestCookie".to_string(),
127 cookie_domain: "example.com".to_string(),
128 cookie_value: Some("eyJhbGciOiJFUzI1NiIsImtpZCI6Im...".to_string()),
129 };
130
131 let json_some = serde_json::to_string(&config_some).unwrap();
132 let deserialized_some: SsoCookieVendorConfig = serde_json::from_str(&json_some).unwrap();
133 assert_eq!(
134 deserialized_some.cookie_value,
135 Some("eyJhbGciOiJFUzI1NiIsImtpZCI6Im...".to_string())
136 );
137 }
138
139 #[test]
140 fn enum_variants() {
141 let direct = BootstrapConfig::Direct;
142 assert!(matches!(direct, BootstrapConfig::Direct));
143
144 let vendor = BootstrapConfig::SsoCookieVendor(SsoCookieVendorConfig {
145 idp_login_url: "https://example.com".to_string(),
146 cookie_name: "Cookie".to_string(),
147 cookie_domain: "example.com".to_string(),
148 cookie_value: None,
149 });
150 assert!(matches!(vendor, BootstrapConfig::SsoCookieVendor(_)));
151 }
152
153 #[test]
154 fn debug_output_redacts_cookie_value() {
155 let config_with_cookie = SsoCookieVendorConfig {
157 idp_login_url: "https://example.com/login".to_string(),
158 cookie_name: "SessionCookie".to_string(),
159 cookie_domain: "example.com".to_string(),
160 cookie_value: Some("super-secret-cookie-value-abc123".to_string()),
161 };
162
163 let debug_output = format!("{:?}", config_with_cookie);
164
165 assert!(debug_output.contains("SsoCookieVendorConfig"));
167 assert!(debug_output.contains("example.com/login"));
168 assert!(debug_output.contains("SessionCookie"));
169 assert!(debug_output.contains("example.com"));
170
171 assert!(!debug_output.contains("super-secret-cookie-value-abc123"));
173 assert!(debug_output.contains("[REDACTED]"));
175 }
176}