Skip to main content

bitwarden_importers/
importer_client.rs

1use bitwarden_core::Client;
2
3use crate::{
4    Credentials, ImportError, ImportOptions, ImportSummary, OnePasswordImportSummary,
5    OnePasswordSignInMethods, OnePasswordSsoCredentials, OnePasswordSsoUi, OnePasswordTwoFactorUi,
6    SignInAddress,
7    import::{
8        import_kdbx, import_onepassword, import_onepassword_sso, onepassword_sign_in_methods,
9    },
10};
11
12#[allow(missing_docs)]
13#[bitwarden_ffi::wasm_object]
14pub struct ImporterClient {
15    client: Client,
16}
17
18#[bitwarden_ffi::wasm_export]
19impl ImporterClient {
20    fn new(client: Client) -> Self {
21        Self { client }
22    }
23
24    /// Import a KeePass KDBX (`.kdbx`) database and submit it to the server.
25    ///
26    /// Parses and decrypts the raw `.kdbx` bytes (unlocked with the password and/or key file),
27    /// encrypts the entries for the user's personal vault or the given organization, and submits
28    /// them to the import endpoint. Returns the counts of what was imported. Inputs larger than
29    /// 10 MiB are rejected with `KdbxFileTooLarge`.
30    pub async fn import_kdbx(
31        &self,
32        file: Vec<u8>,
33        password: Option<String>,
34        key_file: Option<Vec<u8>>,
35        options: ImportOptions,
36    ) -> Result<ImportSummary, ImportError> {
37        import_kdbx(&self.client, file, password, key_file, options).await
38    }
39
40    /// Asks 1Password how the account signs in: with the password and Secret Key, with SSO, or
41    /// either. It needs only the email and the sign-in address, so the UI can call it first and
42    /// then ask for the password and Secret Key or start
43    /// [`ImporterClient::import_onepassword_sso`]. Fails with
44    /// [`ImportError::OnePasswordInvalidEmail`] when 1Password refuses the email as malformed.
45    pub async fn onepassword_sign_in_methods(
46        &self,
47        username: String,
48        sign_in_address: SignInAddress,
49    ) -> Result<OnePasswordSignInMethods, ImportError> {
50        onepassword_sign_in_methods(&self.client, username, sign_in_address).await
51    }
52}
53
54#[cfg(feature = "wasm")]
55#[bitwarden_ffi::wasm_export]
56impl ImporterClient {
57    /// The same import as [`ImporterClient::import_onepassword`], for JavaScript, which passes the
58    /// two-factor prompt as an object rather than a `&dyn` callback.
59    #[wasm_bindgen(js_name = import_onepassword)]
60    pub async fn import_onepassword_wasm(
61        &self,
62        credentials: Credentials,
63        two_factor: crate::wasm::RawJsOnePasswordTwoFactorUi,
64        options: ImportOptions,
65    ) -> Result<OnePasswordImportSummary, ImportError> {
66        let two_factor = crate::wasm::JsOnePasswordTwoFactorUi::new(two_factor);
67        import_onepassword(&self.client, credentials, &two_factor, options).await
68    }
69
70    /// The same import as [`ImporterClient::import_onepassword_sso`], for JavaScript, which passes
71    /// the callbacks as objects rather than `&dyn` references.
72    #[wasm_bindgen(js_name = import_onepassword_sso)]
73    pub async fn import_onepassword_sso_wasm(
74        &self,
75        credentials: OnePasswordSsoCredentials,
76        ui: crate::wasm::RawJsOnePasswordSsoUi,
77        options: ImportOptions,
78    ) -> Result<OnePasswordImportSummary, ImportError> {
79        let ui = crate::wasm::JsOnePasswordSsoUi::new(ui);
80        import_onepassword_sso(&self.client, credentials, &ui, options).await
81    }
82}
83
84// Separate from the block above: `wasm_bindgen` cannot export the `&dyn` callbacks.
85impl ImporterClient {
86    /// Import a 1Password account directly from the 1Password servers.
87    ///
88    /// Signs in with the email, master password and Secret Key in `credentials`, asking
89    /// `two_factor` for a code when the account requires one, downloads and decrypts every vault
90    /// the account can open, and submits the result to the import endpoint. Each vault becomes a
91    /// folder. Returns the imported counts plus any vaults or items that could not be imported.
92    pub async fn import_onepassword(
93        &self,
94        credentials: Credentials,
95        two_factor: &dyn OnePasswordTwoFactorUi,
96        options: ImportOptions,
97    ) -> Result<OnePasswordImportSummary, ImportError> {
98        import_onepassword(&self.client, credentials, two_factor, options).await
99    }
100
101    /// The same import as [`ImporterClient::import_onepassword`], signing in with single sign-on.
102    ///
103    /// Signs in with the email and sign-in address in `credentials` through the account's identity
104    /// provider, which `ui` opens for the user. Every import is a new device for the account, so
105    /// the user approves it from another 1Password device and types the verification code it
106    /// shows, again through `ui`. A sign-in the user gives up on, at the identity provider or
107    /// during the approval, ends with `ImportError::OnePasswordCanceled`, one the approving device
108    /// denies with `ImportError::OnePasswordDenied`, and one where the user runs out of attempts at
109    /// the code with `ImportError::OnePasswordVerificationCodeFailed`.
110    pub async fn import_onepassword_sso(
111        &self,
112        credentials: OnePasswordSsoCredentials,
113        ui: &dyn OnePasswordSsoUi,
114        options: ImportOptions,
115    ) -> Result<OnePasswordImportSummary, ImportError> {
116        import_onepassword_sso(&self.client, credentials, ui, options).await
117    }
118}
119
120#[allow(missing_docs)]
121pub trait ImporterClientExt {
122    fn importers(&self) -> ImporterClient;
123}
124
125impl ImporterClientExt for Client {
126    fn importers(&self) -> ImporterClient {
127        ImporterClient::new(self.clone())
128    }
129}