Skip to main content

bitwarden_uniffi/tools/
mod.rs

1use std::sync::Arc;
2
3use bitwarden_collections::collection::Collection;
4use bitwarden_exporters::{Account, ExportFormat};
5use bitwarden_generators::{
6    PassphraseGeneratorRequest, PasswordGeneratorRequest, UsernameGeneratorRequest,
7};
8use bitwarden_importers::{
9    Credentials, ImportOptions, ImportSummary, OnePasswordImportSummary, OnePasswordTotpResult,
10    OnePasswordTwoFactorUi,
11};
12use bitwarden_vault::{Cipher, EncryptionContext, Folder};
13
14use crate::error::Result;
15
16mod sends;
17pub use sends::SendClient;
18
19mod ssh;
20pub use ssh::SshClient;
21
22#[derive(uniffi::Object)]
23pub struct GeneratorClients(pub(crate) bitwarden_generators::GeneratorClient);
24
25#[uniffi::export(async_runtime = "tokio")]
26impl GeneratorClients {
27    /// Generate Password
28    pub fn password(&self, settings: PasswordGeneratorRequest) -> Result<String> {
29        Ok(self.0.password(settings)?)
30    }
31
32    /// Generate Passphrase
33    pub fn passphrase(&self, settings: PassphraseGeneratorRequest) -> Result<String> {
34        Ok(self.0.passphrase(settings)?)
35    }
36
37    /// Parses an HTML `passwordrules` attribute string into a [`PasswordGeneratorRequest`].
38    pub fn password_rules(&self, rules: String) -> Result<PasswordGeneratorRequest> {
39        Ok(self.0.password_rules(rules)?)
40    }
41
42    /// Generate Username
43    pub async fn username(&self, settings: UsernameGeneratorRequest) -> Result<String> {
44        Ok(self.0.username(settings).await?)
45    }
46}
47
48#[derive(uniffi::Object)]
49pub struct ExporterClient(pub(crate) bitwarden_exporters::ExporterClient);
50
51#[uniffi::export(async_runtime = "tokio")]
52impl ExporterClient {
53    /// Export user vault
54    pub async fn export_vault(
55        &self,
56        folders: Vec<Folder>,
57        ciphers: Vec<Cipher>,
58        format: ExportFormat,
59    ) -> Result<String> {
60        Ok(self.0.export_vault(folders, ciphers, format).await?)
61    }
62
63    /// Export organization vault
64    pub fn export_organization_vault(
65        &self,
66        collections: Vec<Collection>,
67        ciphers: Vec<Cipher>,
68        format: ExportFormat,
69    ) -> Result<String> {
70        Ok(self
71            .0
72            .export_organization_vault(collections, ciphers, format)?)
73    }
74
75    /// Credential Exchange Format (CXF)
76    ///
77    /// *Warning:* Expect this API to be unstable, and it will change in the future.
78    ///
79    /// For use with Apple using [ASCredentialExportManager](https://developer.apple.com/documentation/authenticationservices/ascredentialexportmanager).
80    /// Ideally the output should be immediately deserialized to [ASImportableAccount](https://developer.apple.com/documentation/authenticationservices/asimportableaccount).
81    pub fn export_cxf(&self, account: Account, ciphers: Vec<Cipher>) -> Result<String> {
82        Ok(self.0.export_cxf(account, ciphers)?)
83    }
84
85    /// Credential Exchange Format (CXF)
86    ///
87    /// *Warning:* Expect this API to be unstable, and it will change in the future.
88    ///
89    /// For use with Apple using [ASCredentialExportManager](https://developer.apple.com/documentation/authenticationservices/ascredentialexportmanager).
90    /// Ideally the input should be immediately serialized from [ASImportableAccount](https://developer.apple.com/documentation/authenticationservices/asimportableaccount).
91    pub fn import_cxf(&self, payload: String) -> Result<Vec<EncryptionContext>> {
92        Ok(self.0.import_cxf(payload)?)
93    }
94}
95
96#[derive(uniffi::Object)]
97pub struct ImporterClient(pub(crate) bitwarden_importers::ImporterClient);
98
99#[uniffi::export(async_runtime = "tokio")]
100impl ImporterClient {
101    /// Import a KeePass KDBX (`.kdbx`) database and submit it to the server.
102    pub async fn import_kdbx(
103        &self,
104        file: Vec<u8>,
105        password: Option<String>,
106        key_file: Option<Vec<u8>>,
107        options: ImportOptions,
108    ) -> Result<ImportSummary> {
109        Ok(self
110            .0
111            .import_kdbx(file, password, key_file, options)
112            .await?)
113    }
114
115    /// Import a 1Password account directly from the 1Password servers.
116    ///
117    /// Signs in, asks `two_factor` for a code when the account requires one, downloads every vault
118    /// the account can open, and submits the result. Each vault becomes a folder; the result also
119    /// reports vaults and items that could not be imported.
120    pub async fn import_onepassword(
121        &self,
122        credentials: Credentials,
123        two_factor: Arc<dyn OnePasswordTwoFactorPrompt>,
124        options: ImportOptions,
125    ) -> Result<OnePasswordImportSummary> {
126        Ok(self
127            .0
128            .import_onepassword(credentials, &TwoFactorPromptBridge(two_factor), options)
129            .await?)
130    }
131}
132
133/// Asks the user for a 1Password two-factor code.
134#[uniffi::export(with_foreign)]
135#[async_trait::async_trait]
136pub trait OnePasswordTwoFactorPrompt: Send + Sync {
137    /// Returns the code the user entered, or `None` if they cancelled. Each rejected code restarts
138    /// the login, so `attempt` grows as the user retries.
139    async fn provide_totp(&self, attempt: u32) -> Result<Option<String>>;
140}
141
142/// Adapts the foreign prompt to the importer's trait, which uniffi cannot export from another
143/// crate.
144struct TwoFactorPromptBridge(Arc<dyn OnePasswordTwoFactorPrompt>);
145
146#[async_trait::async_trait]
147impl OnePasswordTwoFactorUi for TwoFactorPromptBridge {
148    async fn provide_totp(&self, attempt: u32) -> OnePasswordTotpResult {
149        // A prompt that throws produces no code either, so it reads as the user cancelling.
150        match self.0.provide_totp(attempt).await {
151            Ok(Some(code)) => OnePasswordTotpResult::Code(code),
152            Ok(None) | Err(_) => OnePasswordTotpResult::Cancel,
153        }
154    }
155}