Skip to content

Commit 778bc50

Browse files
committed
Implement demo cipher store
1 parent 713bffc commit 778bc50

File tree

5 files changed

+138
-3
lines changed

5 files changed

+138
-3
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/bitwarden-uniffi/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ chrono = { workspace = true, features = ["std"] }
3232
env_logger = "0.11.1"
3333
log = { workspace = true }
3434
schemars = { workspace = true, optional = true }
35+
serde_json = { workspace = true }
3536
thiserror = { workspace = true }
3637
uniffi = { workspace = true }
3738
uuid = { workspace = true }

crates/bitwarden-uniffi/src/platform/mod.rs

+66-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
use bitwarden_core::platform::FingerprintRequest;
1+
use std::sync::Arc;
2+
3+
use bitwarden_core::{platform::FingerprintRequest, Client};
24
use bitwarden_fido::ClientFido2Ext;
5+
use bitwarden_vault::Cipher;
36

47
use crate::error::{Error, Result};
58

@@ -38,4 +41,66 @@ impl PlatformClient {
3841
pub fn fido2(&self) -> fido2::ClientFido2 {
3942
fido2::ClientFido2(self.0.fido2())
4043
}
44+
45+
pub fn store(&self) -> StoreClient {
46+
StoreClient(self.0.clone())
47+
}
48+
}
49+
50+
#[derive(uniffi::Object)]
51+
pub struct StoreClient(Client);
52+
53+
#[uniffi::export(with_foreign)]
54+
#[async_trait::async_trait]
55+
pub trait CipherStore: Send + Sync {
56+
async fn get(&self, id: String) -> Option<Cipher>;
57+
async fn list(&self) -> Vec<Cipher>;
58+
async fn set(&self, id: String, value: Cipher);
59+
async fn remove(&self, id: String);
60+
}
61+
62+
impl<T> std::fmt::Debug for UniffiTraitBridge<T> {
63+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64+
f.debug_struct("UniffiTraitBridge").finish()
65+
}
66+
}
67+
68+
struct UniffiTraitBridge<T>(T);
69+
70+
#[async_trait::async_trait]
71+
impl bitwarden_core::client::data_store::DataStore<Cipher>
72+
for UniffiTraitBridge<Arc<dyn CipherStore>>
73+
{
74+
async fn get(&self, key: String) -> Option<Cipher> {
75+
self.0.get(key).await
76+
}
77+
async fn list(&self) -> Vec<Cipher> {
78+
self.0.list().await
79+
}
80+
async fn set(&self, key: String, value: Cipher) {
81+
self.0.set(key, value).await
82+
}
83+
async fn remove(&self, key: String) {
84+
self.0.remove(key).await
85+
}
86+
}
87+
88+
#[uniffi::export(async_runtime = "tokio")]
89+
impl StoreClient {
90+
pub async fn print_the_ciphers(&self) -> String {
91+
let store = self.0.internal.get_data_store::<Cipher>().expect("msg");
92+
let mut result = String::new();
93+
let ciphers = store.list().await;
94+
for cipher in ciphers {
95+
result.push_str(&serde_json::to_string(&cipher).expect("msg"));
96+
result.push('\n');
97+
}
98+
result
99+
}
100+
101+
pub fn register_cipher_store(&self, store: Arc<dyn CipherStore>) -> Result<()> {
102+
let store_internal = Arc::new(UniffiTraitBridge(store));
103+
self.0.internal.register_data_store(store_internal);
104+
Ok(())
105+
}
41106
}

crates/bitwarden-wasm-internal/Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ keywords.workspace = true
1616
crate-type = ["cdylib"]
1717

1818
[dependencies]
19+
async-trait = ">=0.1.80, <0.2"
1920
bitwarden-core = { workspace = true, features = ["wasm", "internal"] }
2021
bitwarden-crypto = { workspace = true, features = ["wasm"] }
2122
bitwarden-error = { workspace = true }
23+
bitwarden-ffi-macros = { workspace = true }
2224
bitwarden-generators = { workspace = true, features = ["wasm"] }
2325
bitwarden-ipc = { workspace = true, features = ["wasm"] }
2426
bitwarden-ssh = { workspace = true, features = ["wasm"] }
@@ -29,6 +31,8 @@ console_log = { version = "1.0.0", features = ["color"] }
2931
js-sys = "0.3.68"
3032
log = "0.4.20"
3133
serde_json = ">=1.0.96, <2.0"
34+
tokio = { features = ["sync", "rt"], workspace = true }
35+
tsify-next = { workspace = true }
3236
# When upgrading wasm-bindgen, make sure to update the version in the workflows!
3337
wasm-bindgen = { version = "=0.2.100", features = ["serde-serialize"] }
3438
wasm-bindgen-futures = "0.4.41"

crates/bitwarden-wasm-internal/src/client.rs

+66-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
extern crate console_error_panic_hook;
2-
use std::fmt::Display;
2+
use std::{fmt::Display, sync::Arc};
33

4-
use bitwarden_core::{Client, ClientSettings};
4+
use bitwarden_core::{client::data_store::DataStore, Client, ClientSettings};
55
use bitwarden_error::bitwarden_error;
6+
use bitwarden_vault::Cipher;
67
use bitwarden_vault::VaultClientExt;
8+
use tokio::sync::mpsc;
9+
use tsify_next::serde_wasm_bindgen;
710
use wasm_bindgen::prelude::*;
811

912
use crate::{CryptoClient, GeneratorClient, VaultClient};
@@ -51,6 +54,10 @@ impl BitwardenClient {
5154
pub fn generator(&self) -> GeneratorClient {
5255
GeneratorClient::new(self.0.clone())
5356
}
57+
58+
pub fn store(&self) -> StoreClient {
59+
StoreClient::new(self.0.clone())
60+
}
5461
}
5562

5663
#[bitwarden_error(basic)]
@@ -61,3 +68,60 @@ impl Display for TestError {
6168
write!(f, "{}", self.0)
6269
}
6370
}
71+
72+
#[wasm_bindgen]
73+
pub struct StoreClient(Client);
74+
75+
impl StoreClient {
76+
pub fn new(client: Client) -> Self {
77+
Self(client)
78+
}
79+
}
80+
81+
#[wasm_bindgen(typescript_custom_section)]
82+
const CIPHER_STORE_CUSTOM_TS_TYPE: &'static str = r#"
83+
export interface CipherStore {
84+
get(id: string): Promise<Cipher | null>;
85+
list(): Promise<Cipher[]>;
86+
set(id: string, value: Cipher): Promise<void>;
87+
remove(id: string): Promise<void>;
88+
}
89+
"#;
90+
91+
#[bitwarden_ffi_macros::extern_wasm_channel(trait_impl = "DataStore<Cipher>", async_trait = true)]
92+
#[wasm_bindgen]
93+
extern "C" {
94+
#[wasm_bindgen(js_name = CipherStore, typescript_type = "CipherStore")]
95+
pub type JSCipherStore;
96+
97+
#[wasm_bindgen(method)]
98+
async fn get(this: &JSCipherStore, id: String) -> Option<Cipher>;
99+
100+
#[wasm_bindgen(method)]
101+
async fn list(this: &JSCipherStore) -> Vec<Cipher>;
102+
103+
#[wasm_bindgen(method)]
104+
async fn set(this: &JSCipherStore, id: String, value: Cipher);
105+
106+
#[wasm_bindgen(method)]
107+
async fn remove(this: &JSCipherStore, id: String);
108+
}
109+
110+
#[wasm_bindgen]
111+
impl StoreClient {
112+
pub async fn print_the_ciphers(&self) -> String {
113+
let store = self.0.internal.get_data_store::<Cipher>().expect("msg");
114+
let mut result = String::new();
115+
let ciphers = store.list().await;
116+
for cipher in ciphers {
117+
result.push_str(format!("{:?}", cipher).as_str());
118+
result.push('\n');
119+
}
120+
result
121+
}
122+
123+
pub fn register_cipher_store(&self, store: JSCipherStore) {
124+
let store = store.create_channel_impl();
125+
self.0.internal.register_data_store(Arc::new(store));
126+
}
127+
}

0 commit comments

Comments
 (0)