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}