bitwarden_server_communication_config/
config.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize)]
5#[bitwarden_ffi::wasm_record]
6#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
7pub struct ServerCommunicationConfig {
8 pub bootstrap: BootstrapConfig,
10}
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14#[bitwarden_ffi::wasm_record]
15#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
16#[serde(tag = "type", rename_all = "camelCase")]
17pub enum BootstrapConfig {
18 Direct,
20 SsoCookieVendor(SsoCookieVendorConfig),
22}
23
24#[derive(Clone, Serialize, Deserialize)]
28#[serde(rename_all = "camelCase")]
29#[bitwarden_ffi::wasm_record]
30#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
31pub struct SsoCookieVendorConfig {
32 pub idp_login_url: Option<String>,
34 pub cookie_name: String,
36 pub cookie_domain: String,
38 pub vault_url: String,
43 pub cookie_value: Option<Vec<crate::AcquiredCookie>>,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
59#[bitwarden_ffi::wasm_record]
60#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
61pub struct SetCommunicationTypeRequest {
62 pub bootstrap: BootstrapConfigRequest,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
68#[bitwarden_ffi::wasm_record]
69#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
70#[serde(tag = "type", rename_all = "camelCase")]
71pub enum BootstrapConfigRequest {
72 Direct,
74 SsoCookieVendor(SsoCookieVendorConfigRequest),
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
82#[serde(rename_all = "camelCase")]
83#[bitwarden_ffi::wasm_record]
84#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
85pub struct SsoCookieVendorConfigRequest {
86 pub idp_login_url: Option<String>,
88 pub cookie_name: String,
90 pub cookie_domain: String,
92 pub vault_url: String,
97}
98
99impl std::fmt::Debug for SsoCookieVendorConfig {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 f.debug_struct("SsoCookieVendorConfig")
103 .field("idp_login_url", &self.idp_login_url)
104 .field("cookie_name", &self.cookie_name)
105 .field("cookie_domain", &self.cookie_domain)
106 .field("vault_url", &self.vault_url)
107 .field(
108 "cookie_value",
109 &self.cookie_value.as_ref().map(|_| "[REDACTED]"),
110 )
111 .finish()
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn direct_bootstrap_serialization() {
121 let config = ServerCommunicationConfig {
122 bootstrap: BootstrapConfig::Direct,
123 };
124
125 let json = serde_json::to_string(&config).unwrap();
126 assert!(json.contains("\"type\":\"direct\""));
127
128 let deserialized: ServerCommunicationConfig = serde_json::from_str(&json).unwrap();
129 assert!(matches!(deserialized.bootstrap, BootstrapConfig::Direct));
130 }
131
132 #[test]
133 fn sso_cookie_vendor_serialization() {
134 let config = ServerCommunicationConfig {
135 bootstrap: BootstrapConfig::SsoCookieVendor(SsoCookieVendorConfig {
136 idp_login_url: Some("https://timeloop-auth.acme.com/login".to_string()),
137 cookie_name: "ALBAuthSessionCookie".to_string(),
138 cookie_domain: "vault.example.com".to_string(),
139 vault_url: "https://vault.example.com".to_string(),
140 cookie_value: None,
141 }),
142 };
143
144 let json = serde_json::to_string(&config).unwrap();
145 assert!(json.contains("\"type\":\"ssoCookieVendor\""));
146 assert!(json.contains("timeloop-auth.acme.com"));
147 assert!(json.contains("ALBAuthSessionCookie"));
148
149 let server_json = r#"{"bootstrap":{"type":"ssoCookieVendor","idpLoginUrl":"https://idp.example.com/login","cookieName":"TestCookie","cookieDomain":"example.com","vaultUrl":"https://vault.example.com"}}"#;
151 let parsed = serde_json::from_str::<ServerCommunicationConfig>(server_json).unwrap();
152 if let BootstrapConfig::SsoCookieVendor(vendor) = parsed.bootstrap {
153 assert_eq!(
154 vendor.idp_login_url,
155 Some("https://idp.example.com/login".to_string())
156 );
157 assert_eq!(vendor.cookie_name, "TestCookie");
158 assert_eq!(vendor.cookie_domain, "example.com");
159 } else {
160 panic!("Expected SsoCookieVendor variant");
161 }
162
163 let deserialized: ServerCommunicationConfig = serde_json::from_str(&json).unwrap();
164 if let BootstrapConfig::SsoCookieVendor(vendor_config) = deserialized.bootstrap {
165 assert_eq!(
166 vendor_config.idp_login_url,
167 Some("https://timeloop-auth.acme.com/login".to_string())
168 );
169 assert_eq!(vendor_config.cookie_name, "ALBAuthSessionCookie");
170 assert_eq!(vendor_config.cookie_domain, "vault.example.com");
171 assert!(vendor_config.cookie_value.is_none());
172 } else {
173 panic!("Expected SsoCookieVendor variant");
174 }
175 }
176
177 #[test]
178 fn cookie_value_some_and_none() {
179 use crate::AcquiredCookie;
180
181 let config_none = SsoCookieVendorConfig {
183 idp_login_url: Some("https://example.com".to_string()),
184 cookie_name: "TestCookie".to_string(),
185 cookie_domain: "example.com".to_string(),
186 vault_url: "https://vault.example.com".to_string(),
187 cookie_value: None,
188 };
189
190 let json_none = serde_json::to_string(&config_none).unwrap();
191 let deserialized_none: SsoCookieVendorConfig = serde_json::from_str(&json_none).unwrap();
192 assert!(deserialized_none.cookie_value.is_none());
193
194 let config_some = SsoCookieVendorConfig {
196 idp_login_url: Some("https://example.com".to_string()),
197 cookie_name: "TestCookie".to_string(),
198 cookie_domain: "example.com".to_string(),
199 vault_url: "https://vault.example.com".to_string(),
200 cookie_value: Some(vec![AcquiredCookie {
201 name: "TestCookie".to_string(),
202 value: "eyJhbGciOiJFUzI1NiIsImtpZCI6Im...".to_string(),
203 }]),
204 };
205
206 let json_some = serde_json::to_string(&config_some).unwrap();
207 let deserialized_some: SsoCookieVendorConfig = serde_json::from_str(&json_some).unwrap();
208 assert_eq!(deserialized_some.cookie_value.as_ref().unwrap().len(), 1);
209 assert_eq!(
210 deserialized_some.cookie_value.as_ref().unwrap()[0].name,
211 "TestCookie"
212 );
213
214 let config_sharded = SsoCookieVendorConfig {
216 idp_login_url: Some("https://example.com".to_string()),
217 cookie_name: "TestCookie".to_string(),
218 cookie_domain: "example.com".to_string(),
219 vault_url: "https://vault.example.com".to_string(),
220 cookie_value: Some(vec![
221 AcquiredCookie {
222 name: "TestCookie-0".to_string(),
223 value: "shard1".to_string(),
224 },
225 AcquiredCookie {
226 name: "TestCookie-1".to_string(),
227 value: "shard2".to_string(),
228 },
229 AcquiredCookie {
230 name: "TestCookie-2".to_string(),
231 value: "shard3".to_string(),
232 },
233 ]),
234 };
235
236 let json_sharded = serde_json::to_string(&config_sharded).unwrap();
237 let deserialized_sharded: SsoCookieVendorConfig =
238 serde_json::from_str(&json_sharded).unwrap();
239 assert_eq!(deserialized_sharded.cookie_value.as_ref().unwrap().len(), 3);
240 assert_eq!(
241 deserialized_sharded.cookie_value.as_ref().unwrap()[0].name,
242 "TestCookie-0"
243 );
244 }
245
246 #[test]
247 fn enum_variants() {
248 let direct = BootstrapConfig::Direct;
249 assert!(matches!(direct, BootstrapConfig::Direct));
250
251 let vendor = BootstrapConfig::SsoCookieVendor(SsoCookieVendorConfig {
252 idp_login_url: Some("https://example.com".to_string()),
253 cookie_name: "Cookie".to_string(),
254 cookie_domain: "example.com".to_string(),
255 vault_url: "https://vault.example.com".to_string(),
256 cookie_value: None,
257 });
258 assert!(matches!(vendor, BootstrapConfig::SsoCookieVendor(_)));
259 }
260
261 #[test]
262 fn debug_output_redacts_cookie_value() {
263 use crate::AcquiredCookie;
264
265 let config_with_cookie = SsoCookieVendorConfig {
267 idp_login_url: Some("https://example.com/login".to_string()),
268 cookie_name: "SessionCookie".to_string(),
269 cookie_domain: "example.com".to_string(),
270 vault_url: "https://vault.example.com".to_string(),
271 cookie_value: Some(vec![AcquiredCookie {
272 name: "SessionCookie".to_string(),
273 value: "super-secret-cookie-value-abc123".to_string(),
274 }]),
275 };
276
277 let debug_output = format!("{:?}", config_with_cookie);
278
279 assert!(debug_output.contains("SsoCookieVendorConfig"));
281 assert!(debug_output.contains("example.com/login"));
282 assert!(debug_output.contains("SessionCookie"));
283 assert!(debug_output.contains("example.com"));
284
285 assert!(!debug_output.contains("super-secret-cookie-value-abc123"));
287 assert!(debug_output.contains("[REDACTED]"));
289 }
290}