Skip to main content

bitwarden_pm/
builder.rs

1use std::sync::Arc;
2
3use bitwarden_auth::token_management::PasswordManagerTokenHandler;
4use bitwarden_core::{ClientBuilder, client::tracing_middleware::ReqwestTracingMiddleware};
5
6use crate::PasswordManagerClient;
7
8/// Builder for constructing [`PasswordManagerClient`] instances with custom configuration.
9pub struct PasswordManagerClientBuilder {
10    settings: Option<bitwarden_core::ClientSettings>,
11    cookie_provider:
12        Option<std::sync::Arc<dyn bitwarden_server_communication_config::CookieProvider>>,
13}
14
15impl PasswordManagerClientBuilder {
16    /// Creates a new [`PasswordManagerClientBuilder`] with default settings.
17    pub fn new() -> Self {
18        Self {
19            settings: None,
20            cookie_provider: None,
21        }
22    }
23
24    /// Sets the [`ClientSettings`](bitwarden_core::ClientSettings) for the client being built.
25    pub fn with_settings(mut self, settings: bitwarden_core::ClientSettings) -> Self {
26        self.settings = Some(settings);
27        self
28    }
29
30    /// Sets an SSO load balancer cookie provider, enabling cookie middleware for
31    /// self-hosted deployments behind sticky-session load balancers.
32    pub fn with_server_communication_config(
33        mut self,
34        cookie_provider: std::sync::Arc<dyn bitwarden_server_communication_config::CookieProvider>,
35    ) -> Self {
36        self.cookie_provider = Some(cookie_provider);
37        self
38    }
39
40    /// Consumes the builder and constructs a [`PasswordManagerClient`].
41    pub fn build(self) -> PasswordManagerClient {
42        let mut builder = ClientBuilder::new()
43            .with_token_handler(Arc::new(PasswordManagerTokenHandler::default()));
44        if let Some(s) = self.settings {
45            builder = builder.with_settings(s);
46        }
47        let mut middleware: Vec<Arc<dyn reqwest_middleware::Middleware>> =
48            vec![Arc::new(ReqwestTracingMiddleware)];
49
50        if let Some(cookie_provider) = self.cookie_provider {
51            middleware.push(Arc::new(
52                bitwarden_server_communication_config::ServerCommunicationConfigMiddleware::new(
53                    cookie_provider,
54                ),
55            ));
56        }
57
58        builder = builder.with_middleware(middleware);
59        PasswordManagerClient(builder.build())
60    }
61}
62
63impl Default for PasswordManagerClientBuilder {
64    fn default() -> Self {
65        Self::new()
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn test_pm_builder_default_builds() {
75        let _client = PasswordManagerClientBuilder::new().build();
76    }
77
78    #[test]
79    fn test_pm_builder_with_settings_builds() {
80        let settings = bitwarden_core::ClientSettings::default();
81        let _client = PasswordManagerClientBuilder::new()
82            .with_settings(settings)
83            .build();
84    }
85
86    #[test]
87    fn test_pm_builder_with_server_communication_config_builds() {
88        struct MockCookieProvider;
89
90        #[async_trait::async_trait]
91        impl bitwarden_server_communication_config::CookieProvider for MockCookieProvider {
92            async fn cookies(&self, _hostname: &str) -> Vec<(String, String)> {
93                vec![]
94            }
95
96            async fn acquire_cookie(
97                &self,
98                _hostname: &str,
99            ) -> Result<(), bitwarden_server_communication_config::AcquireCookieError> {
100                Ok(())
101            }
102
103            async fn needs_bootstrap(&self, _hostname: &str) -> bool {
104                false
105            }
106        }
107
108        let _client = PasswordManagerClientBuilder::new()
109            .with_server_communication_config(Arc::new(MockCookieProvider))
110            .build();
111    }
112}