Skip to main content

bitwarden_pm/
lib.rs

1#![doc = include_str!("../README.md")]
2
3#[cfg(feature = "bitwarden-license")]
4mod commercial;
5
6use std::sync::Arc;
7
8use bitwarden_auth::AuthClientExt as _;
9use bitwarden_core::{
10    FromClient,
11    auth::{ClientManagedTokenHandler, ClientManagedTokens},
12    client::persisted_state::SESSION_PROTECTED_USER_KEY,
13};
14use bitwarden_exporters::ExporterClientExt as _;
15use bitwarden_generators::GeneratorClientsExt as _;
16use bitwarden_policies::PoliciesClientExt as _;
17use bitwarden_send::SendClientExt as _;
18use bitwarden_sync::SyncClientExt as _;
19use bitwarden_user_crypto_management::UserCryptoManagementClientExt;
20use bitwarden_vault::{FolderSyncHandler, VaultClientExt as _};
21
22#[cfg(feature = "uniffi")]
23uniffi::setup_scaffolding!();
24
25/// Re-export subclients for easier access
26pub mod clients {
27    pub use bitwarden_auth::AuthClient;
28    pub use bitwarden_core::key_management::CryptoClient;
29    pub use bitwarden_exporters::ExporterClient;
30    pub use bitwarden_generators::GeneratorClient;
31    pub use bitwarden_policies::PolicyClient;
32    pub use bitwarden_send::SendClient;
33    pub use bitwarden_sync::SyncClient;
34    pub use bitwarden_vault::VaultClient;
35}
36#[cfg(feature = "bitwarden-license")]
37pub use commercial::CommercialPasswordManagerClient;
38
39mod builder;
40pub mod migrations;
41pub use bitwarden_core::{RehydrationError, SaveStateData};
42pub use builder::PasswordManagerClientBuilder;
43
44/// The main entry point for the Bitwarden Password Manager SDK
45pub struct PasswordManagerClient(pub bitwarden_core::Client);
46
47impl PasswordManagerClient {
48    /// Initialize a new instance of the SDK client
49    pub fn new(settings: Option<bitwarden_core::ClientSettings>) -> Self {
50        let mut builder = PasswordManagerClientBuilder::new();
51        if let Some(s) = settings {
52            builder = builder.with_settings(s);
53        }
54        builder.build()
55    }
56
57    /// Returns a [`PasswordManagerClientBuilder`] for constructing a new [`PasswordManagerClient`].
58    pub fn builder() -> PasswordManagerClientBuilder {
59        PasswordManagerClientBuilder::new()
60    }
61
62    /// Initialize a new instance of the SDK client with client-managed tokens
63    pub fn new_with_client_tokens(
64        settings: Option<bitwarden_core::ClientSettings>,
65        tokens: Arc<dyn ClientManagedTokens>,
66    ) -> Self {
67        Self(bitwarden_core::Client::new_with_token_handler(
68            settings,
69            ClientManagedTokenHandler::new(tokens),
70        ))
71    }
72
73    /// Initialize a new instance of the SDK client with SDK managed state and sync handlers
74    /// registered
75    ///
76    /// This will eventually replace `new` when the SDK fully owns sync on all clients.
77    pub fn new_with_sync(settings: Option<bitwarden_core::ClientSettings>) -> Self {
78        let client = Self::new(settings);
79
80        client
81            .sync()
82            .register_sync_handler(Arc::new(FolderSyncHandler::from_client(&client.0)));
83
84        // TODO: Add more sync handlers here!
85
86        client
87    }
88
89    /// Platform operations
90    pub fn platform(&self) -> bitwarden_core::platform::PlatformClient {
91        self.0.platform()
92    }
93
94    /// Auth operations
95    pub fn auth(&self) -> bitwarden_auth::AuthClient {
96        self.0.auth_new()
97    }
98
99    /// Bitwarden licensed operations
100    #[cfg(feature = "bitwarden-license")]
101    pub fn commercial(&self) -> CommercialPasswordManagerClient {
102        CommercialPasswordManagerClient::new(self.0.clone())
103    }
104
105    /// Crypto operations
106    pub fn crypto(&self) -> bitwarden_core::key_management::CryptoClient {
107        self.0.crypto()
108    }
109
110    /// Operations that manage the cryptographic machinery of a user account, including key-rotation
111    pub fn user_crypto_management(
112        &self,
113    ) -> bitwarden_user_crypto_management::UserCryptoManagementClient {
114        self.0.user_crypto_management()
115    }
116
117    /// Vault item operations
118    pub fn vault(&self) -> bitwarden_vault::VaultClient {
119        self.0.vault()
120    }
121
122    /// Exporter operations
123    pub fn exporters(&self) -> bitwarden_exporters::ExporterClient {
124        self.0.exporters()
125    }
126
127    /// Generator operations
128    pub fn generator(&self) -> bitwarden_generators::GeneratorClient {
129        self.0.generator()
130    }
131
132    /// Send operations
133    pub fn sends(&self) -> bitwarden_send::SendClient {
134        self.0.sends()
135    }
136
137    /// Policy operations
138    pub fn policies(&self) -> bitwarden_policies::PolicyClient {
139        self.0.policies()
140    }
141
142    /// Sync operations
143    pub fn sync(&self) -> bitwarden_sync::SyncClient {
144        self.0.sync()
145    }
146
147    /// Returns true when the user's symmetric key is loaded into the key store.
148    pub fn is_unlocked(&self) -> bool {
149        use bitwarden_core::key_management::SymmetricKeySlotId;
150        self.0
151            .internal
152            .get_key_store()
153            .context()
154            .has_symmetric_key(SymmetricKeySlotId::User)
155    }
156
157    /// Invalidate the persisted session key, locking the vault for future invocations.
158    ///
159    /// Removes [`SESSION_PROTECTED_USER_KEY`] from the database. Distinct from
160    /// `lock()` on long-lived clients (mobile, desktop), which clears keys from memory: the
161    /// CLI process exits between invocations, so locking must delete the persisted session
162    /// key rather than mutate in-memory state.
163    pub async fn invalidate_session_key(&self) -> Result<(), bitwarden_state::SettingsError> {
164        self.0
165            .platform()
166            .state()
167            .setting(SESSION_PROTECTED_USER_KEY)?
168            .delete()
169            .await
170    }
171
172    /// Write rehydration state to a StateRegistry.
173    ///
174    /// Delegates to [`Client::save_to_state`](bitwarden_core::Client::save_to_state).
175    pub async fn save_to_state(
176        data: SaveStateData,
177        reg: &bitwarden_state::registry::StateRegistry,
178    ) -> Result<(), RehydrationError> {
179        bitwarden_core::Client::save_to_state(data, reg).await
180    }
181
182    /// Reconstruct a locked PasswordManagerClient from a populated StateRegistry.
183    ///
184    /// Delegates to [`Client::load_from_state`](bitwarden_core::Client::load_from_state).
185    pub async fn load_from_state(
186        token_handler: std::sync::Arc<dyn bitwarden_core::auth::auth_tokens::TokenHandler>,
187        registry: bitwarden_state::registry::StateRegistry,
188    ) -> Result<Self, RehydrationError> {
189        let client = bitwarden_core::Client::load_from_state(token_handler, registry).await?;
190        Ok(PasswordManagerClient(client))
191    }
192}
193
194#[cfg(test)]
195mod tests {
196    use std::sync::Arc;
197
198    use super::*;
199
200    #[test]
201    fn new_with_server_communication_config_constructs() {
202        struct MockCookieProvider;
203
204        #[async_trait::async_trait]
205        impl bitwarden_server_communication_config::CookieProvider for MockCookieProvider {
206            async fn cookies(&self, _hostname: &str) -> Vec<(String, String)> {
207                vec![]
208            }
209
210            async fn acquire_cookie(
211                &self,
212                _hostname: &str,
213            ) -> Result<(), bitwarden_server_communication_config::AcquireCookieError> {
214                Ok(())
215            }
216
217            async fn needs_bootstrap(&self, _hostname: &str) -> bool {
218                false
219            }
220        }
221
222        let _client = PasswordManagerClient::builder()
223            .with_server_communication_config(Arc::new(MockCookieProvider))
224            .build();
225    }
226}