bitwarden_uniffi/
lib.rs

1#![doc = include_str!("../README.md")]
2
3uniffi::setup_scaffolding!();
4
5use std::sync::{Arc, Once};
6
7use auth::AuthClient;
8use bitwarden_core::{ClientSettings, client::internal::ClientManagedTokens};
9
10#[allow(missing_docs)]
11pub mod auth;
12#[allow(missing_docs)]
13pub mod crypto;
14mod error;
15#[allow(missing_docs)]
16pub mod platform;
17#[allow(missing_docs)]
18pub mod tool;
19mod uniffi_support;
20#[allow(missing_docs)]
21pub mod vault;
22
23#[cfg(target_os = "android")]
24mod android_support;
25
26use crypto::CryptoClient;
27use error::{Error, Result};
28use platform::PlatformClient;
29use tool::{ExporterClient, GeneratorClients, SendClient, SshClient};
30use vault::VaultClient;
31
32#[allow(missing_docs)]
33#[derive(uniffi::Object)]
34pub struct Client(pub(crate) bitwarden_pm::PasswordManagerClient);
35
36#[uniffi::export(async_runtime = "tokio")]
37impl Client {
38    /// Initialize a new instance of the SDK client
39    #[uniffi::constructor]
40    pub fn new(
41        token_provider: Arc<dyn ClientManagedTokens>,
42        settings: Option<ClientSettings>,
43    ) -> Self {
44        init_logger();
45        setup_error_converter();
46
47        #[cfg(target_os = "android")]
48        android_support::init();
49
50        Self(bitwarden_pm::PasswordManagerClient::new_with_client_tokens(
51            settings,
52            token_provider,
53        ))
54    }
55
56    /// Crypto operations
57    pub fn crypto(&self) -> CryptoClient {
58        CryptoClient(self.0.crypto())
59    }
60
61    /// Vault item operations
62    pub fn vault(&self) -> VaultClient {
63        VaultClient(self.0.vault())
64    }
65
66    #[allow(missing_docs)]
67    pub fn platform(&self) -> PlatformClient {
68        PlatformClient(self.0.0.clone())
69    }
70
71    /// Generator operations
72    pub fn generators(&self) -> GeneratorClients {
73        GeneratorClients(self.0.generator())
74    }
75
76    /// Exporters
77    pub fn exporters(&self) -> ExporterClient {
78        ExporterClient(self.0.exporters())
79    }
80
81    /// Sends operations
82    pub fn sends(&self) -> SendClient {
83        SendClient(self.0.sends())
84    }
85
86    /// SSH operations
87    pub fn ssh(&self) -> SshClient {
88        SshClient()
89    }
90
91    /// Auth operations
92    pub fn auth(&self) -> AuthClient {
93        AuthClient(self.0.0.clone())
94    }
95
96    /// Test method, echoes back the input
97    pub fn echo(&self, msg: String) -> String {
98        msg
99    }
100
101    /// Test method, calls http endpoint
102    pub async fn http_get(&self, url: String) -> Result<String> {
103        let client = self.0.0.internal.get_http_client();
104        let res = client
105            .get(&url)
106            .send()
107            .await
108            .map_err(|e| Error::Api(e.into()))?;
109
110        res.text().await.map_err(|e| Error::Api(e.into()))
111    }
112}
113
114static INIT: Once = Once::new();
115
116fn init_logger() {
117    use tracing_subscriber::{EnvFilter, layer::SubscriberExt as _, util::SubscriberInitExt as _};
118
119    INIT.call_once(|| {
120        // the log level prioritization is determined by:
121        //    1. if RUST_LOG is detected at runtime
122        //    2. if RUST_LOG is provided at compile time
123        //    3. default to INFO
124        let filter = EnvFilter::builder()
125            .with_default_directive(
126                option_env!("RUST_LOG")
127                    .unwrap_or("info")
128                    .parse()
129                    .expect("should provide valid log level at compile time."),
130            )
131            .from_env_lossy();
132
133        let fmtlayer = tracing_subscriber::fmt::layer()
134            .with_ansi(true)
135            .with_file(true)
136            .with_line_number(true)
137            .with_target(true)
138            .pretty();
139
140        #[cfg(target_os = "ios")]
141        {
142            const TAG: &str = "com.8bit.bitwarden";
143
144            tracing_subscriber::registry()
145                .with(fmtlayer)
146                .with(filter)
147                .with(tracing_oslog::OsLogger::new(TAG, "default"))
148                .init();
149        }
150
151        #[cfg(target_os = "android")]
152        {
153            const TAG: &str = "com.bitwarden.sdk";
154
155            tracing_subscriber::registry()
156                .with(fmtlayer)
157                .with(filter)
158                .with(
159                    tracing_android::layer(TAG)
160                        .expect("initialization of android logcat tracing layer"),
161                )
162                .init();
163        }
164
165        #[cfg(not(any(target_os = "android", target_os = "ios")))]
166        {
167            tracing_subscriber::registry()
168                .with(fmtlayer)
169                .with(filter)
170                .init();
171        }
172    });
173}
174
175/// Setup the error converter to ensure conversion errors don't cause panics
176/// Check [`bitwarden_uniffi_error`] for more details
177fn setup_error_converter() {
178    bitwarden_uniffi_error::set_error_to_uniffi_error(|e| {
179        crate::error::BitwardenError::Conversion(e.to_string()).into()
180    });
181}