Skip to main content

bitwarden_uniffi/platform/
server_communication_config.rs

1use std::sync::Arc;
2
3use bitwarden_server_communication_config::{
4    AcquiredCookie, ServerCommunicationConfig, ServerCommunicationConfigPlatformApi,
5    SetCommunicationTypeRequest,
6};
7
8use crate::error::Result;
9
10type UniffiRepository = UniffiRepositoryBridge<Arc<dyn ServerCommunicationConfigRepository>>;
11type UniffiPlatformApi = UniffiPlatformApiBridge<Arc<dyn ServerCommunicationConfigPlatformApi>>;
12
13/// UniFFI wrapper for ServerCommunicationConfigClient
14#[derive(uniffi::Object)]
15pub struct ServerCommunicationConfigClient {
16    client: bitwarden_server_communication_config::ServerCommunicationConfigClient<
17        UniffiRepository,
18        UniffiPlatformApi,
19    >,
20}
21
22impl ServerCommunicationConfigClient {
23    /// Creates a new server communication configuration client
24    pub fn new(
25        repository: Arc<dyn ServerCommunicationConfigRepository>,
26        platform_api: Arc<dyn ServerCommunicationConfigPlatformApi>,
27    ) -> Arc<Self> {
28        Arc::new(Self {
29            client: bitwarden_server_communication_config::ServerCommunicationConfigClient::new(
30                UniffiRepositoryBridge(repository),
31                UniffiPlatformApiBridge(platform_api),
32            ),
33        })
34    }
35}
36
37#[uniffi::export(async_runtime = "tokio")]
38impl ServerCommunicationConfigClient {
39    /// Retrieves the server communication configuration for a domain
40    pub async fn get_config(&self, domain: String) -> Result<ServerCommunicationConfig> {
41        self.client.get_config(domain).await
42    }
43
44    /// Determines if cookie bootstrapping is needed for this domain
45    pub async fn needs_bootstrap(&self, domain: String) -> bool {
46        self.client.needs_bootstrap(domain).await
47    }
48
49    /// Returns all cookies that should be included in requests to this server
50    pub async fn cookies(&self, domain: String) -> Vec<AcquiredCookie> {
51        self.client
52            .cookies(domain)
53            .await
54            .into_iter()
55            .map(|(name, value)| AcquiredCookie { name, value })
56            .collect()
57    }
58
59    pub async fn get_cookies(&self, domain: String) -> Result<Vec<AcquiredCookie>> {
60        let cookies = self.client.get_cookies(domain).await?;
61        Ok(cookies)
62    }
63
64    /// Sets the server communication configuration for a domain
65    ///
66    /// This method saves the provided communication configuration to the repository.
67    /// Typically called when receiving the `/api/config` response from the server.
68    /// Previously acquired cookies are preserved automatically.
69    #[deprecated(
70        note = "Use set_communication_type_v2() instead, which extracts the domain from the config"
71    )]
72    pub async fn set_communication_type(
73        &self,
74        domain: String,
75        request: SetCommunicationTypeRequest,
76    ) -> Result<()> {
77        #[allow(deprecated)]
78        self.client.set_communication_type(domain, request).await?;
79        Ok(())
80    }
81
82    /// Sets the server communication configuration using the domain from the config
83    ///
84    /// Extracts the `cookie_domain` from the `SsoCookieVendor` config and uses it as the
85    /// storage key. If the config is `Direct` or `cookie_domain` is not set, the call
86    /// is silently ignored.
87    pub async fn set_communication_type_v2(
88        &self,
89        request: SetCommunicationTypeRequest,
90    ) -> Result<()> {
91        self.client.set_communication_type_v2(request).await?;
92        Ok(())
93    }
94
95    /// Acquires a cookie from the platform and saves it to the repository
96    pub async fn acquire_cookie(&self, domain: String) -> Result<()> {
97        self.client.acquire_cookie(&domain).await?;
98        Ok(())
99    }
100}
101
102/// UniFFI repository trait for server communication configuration
103#[uniffi::export(with_foreign)]
104#[async_trait::async_trait]
105pub trait ServerCommunicationConfigRepository: Send + Sync {
106    /// Get configuration for a domain
107    async fn get(&self, domain: String) -> Result<Option<ServerCommunicationConfig>>;
108
109    /// Save configuration for a domain
110    async fn save(&self, domain: String, config: ServerCommunicationConfig) -> Result<()>;
111}
112
113/// Bridge from UniFFI trait to internal repository trait
114pub struct UniffiRepositoryBridge<T>(pub T);
115
116impl<T: ?Sized> UniffiRepositoryBridge<Arc<T>> {
117    #[allow(dead_code)]
118    pub fn new(repo: Arc<T>) -> Arc<Self> {
119        Arc::new(UniffiRepositoryBridge(repo))
120    }
121}
122
123impl<T: std::fmt::Debug> std::fmt::Debug for UniffiRepositoryBridge<T> {
124    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125        self.0.fmt(f)
126    }
127}
128
129impl<'a> bitwarden_server_communication_config::ServerCommunicationConfigRepository
130    for UniffiRepositoryBridge<Arc<dyn ServerCommunicationConfigRepository + 'a>>
131{
132    type GetError = crate::error::Error;
133    type SaveError = crate::error::Error;
134
135    async fn get(
136        &self,
137        domain: String,
138    ) -> std::result::Result<Option<ServerCommunicationConfig>, Self::GetError> {
139        self.0.get(domain).await
140    }
141
142    async fn save(
143        &self,
144        domain: String,
145        config: ServerCommunicationConfig,
146    ) -> std::result::Result<(), Self::SaveError> {
147        self.0.save(domain, config).await
148    }
149}
150
151/// Bridge for platform API trait
152pub struct UniffiPlatformApiBridge<T>(pub T);
153
154impl<T: std::fmt::Debug> std::fmt::Debug for UniffiPlatformApiBridge<T> {
155    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156        self.0.fmt(f)
157    }
158}
159
160#[async_trait::async_trait]
161impl<'a> ServerCommunicationConfigPlatformApi
162    for UniffiPlatformApiBridge<Arc<dyn ServerCommunicationConfigPlatformApi + 'a>>
163{
164    async fn acquire_cookies(&self, vault_url: String) -> Option<Vec<AcquiredCookie>> {
165        self.0.acquire_cookies(vault_url).await
166    }
167}