-
Notifications
You must be signed in to change notification settings - Fork 41
Expand file tree
/
Copy pathstate_bridge.rs
More file actions
152 lines (133 loc) · 5.49 KB
/
Copy pathstate_bridge.rs
File metadata and controls
152 lines (133 loc) · 5.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//! The state bridge is a temporary layer that allows quickly transitioning
//! non-repository shaped state to be accessible from within the SDK.
//!
//! This is not a public API that should be used by other teams. It will be
//! replaced by a `bitwarden-state` implementation as soon as that gains support
//! for non-repository state.
use std::sync::{Arc, Mutex};
use bitwarden_crypto::{EncString, Kdf, SymmetricCryptoKey, safe::PasswordProtectedKeyEnvelope};
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;
use crate::{
Client,
key_management::{
MasterPasswordUnlockData, V2UpgradeToken,
account_cryptographic_state::WrappedAccountCryptographicState,
},
};
/// Thread-safe wrapper around the registered [`StateBridgeImpl`] instance.
pub struct StateBridge {
implementation: Mutex<Option<Arc<dyn StateBridgeImpl + Send + Sync>>>,
}
impl StateBridge {
/// Creates an empty bridge with no registered implementation.
pub fn new() -> Self {
Self {
implementation: Mutex::new(None),
}
}
/// Returns true if an implementation has been registered.
pub fn is_registered(&self) -> bool {
self.implementation
.lock()
.expect("Mutex is not poisoned")
.is_some()
}
/// Registers the host-supplied implementation. Replaces any prior registration.
pub fn register(&self, implementation: Box<dyn StateBridgeImpl + Send + Sync>) {
*self.implementation.lock().expect("Mutex is not poisoned") = Some(implementation.into());
}
}
impl Default for StateBridge {
fn default() -> Self {
Self::new()
}
}
/// Client for interacting with the key-management state bridge. This is used to read and write
/// state held by the clients
#[derive(Clone)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Object))]
#[cfg_attr(feature = "wasm", wasm_bindgen)]
pub struct StateBridgeClient {
pub(crate) client: crate::Client,
}
impl Client {
/// A temporary client to bridge KM state into the SDK.
pub fn km_state_bridge(&self) -> StateBridgeClient {
StateBridgeClient {
client: self.clone(),
}
}
}
impl StateBridgeClient {
/// Returns true if a state bridge implementation has been registered.
pub fn is_bridge_registered(&self) -> bool {
self.client.internal.state_bridge.is_registered()
}
/// Registers a bridge implementation used to read and write temporary key-management state.
pub fn register_bridge(&self, bridge_impl: Box<dyn StateBridgeImpl + Send + Sync>) {
self.client.internal.state_bridge.register(bridge_impl);
}
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
extern "C" {
/// Raw JavaScript-side state bridge implementation. The corresponding TypeScript
/// interface (`WasmStateBridge`) and the per-method extern bindings are generated
/// by the `state_bridge!` macro below.
#[wasm_bindgen(typescript_type = "WasmStateBridge")]
pub type RawWasmStateBridge;
}
#[cfg(target_arch = "wasm32")]
use bitwarden_threading::ThreadBoundRunner;
#[cfg(target_arch = "wasm32")]
/// Adapter that lets a JavaScript-supplied `WasmStateBridge` implement
/// [`StateBridgeImpl`]. The trait impl itself is generated by the
/// `state_bridge!` macro below.
pub struct WasmStateBridge(pub(crate) ThreadBoundRunner<RawWasmStateBridge>);
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
impl StateBridgeClient {
/// Registers a the state bridge implementation provided by the host environment.
pub fn register_bridge_impl(&self, bridge_impl: RawWasmStateBridge) {
self.client
.internal
.state_bridge
.register(Box::new(WasmStateBridge(ThreadBoundRunner::new(
bridge_impl,
))));
}
}
#[cfg(feature = "uniffi")]
/// Adapter that lets a foreign-supplied (Swift/Kotlin) implementation of
/// [`StateBridgeForeignImpl`] act as a [`StateBridgeImpl`]. The trait impl is
/// generated by the `state_bridge!` macro below.
pub struct UniffiStateBridge(pub(crate) Arc<dyn StateBridgeForeignImpl>);
#[cfg(feature = "uniffi")]
#[uniffi::export]
impl StateBridgeClient {
/// Registers the host-supplied state bridge implementation.
pub fn register_bridge_impl(&self, bridge_impl: Arc<dyn StateBridgeForeignImpl>) {
self.client
.internal
.state_bridge
.register(Box::new(UniffiStateBridge(bridge_impl)));
}
}
// Generates the full state bridge surface for the listed fields.
//
// Each field expands to three methods on each of [`StateBridgeImpl`], [`StateBridge`], and
// [`StateBridgeClient`] (`set_$name`, `get_$name`, `clear_$name`); WASM extern bindings on
// [`RawWasmStateBridge`]; a [`StateBridgeImpl`] forwarder impl for [`WasmStateBridge`]; the
// matching `WasmStateBridge` TypeScript interface; and a `#[cfg(test)] pub(crate) mod test_support`
// containing an `InMemoryStateBridge` test fixture.
bitwarden_state_bridge_macro::state_bridge! {
user_key: SymmetricCryptoKey as ts "SymmetricKey",
persistent_pin_envelope: PasswordProtectedKeyEnvelope as ts "PasswordProtectedKeyEnvelope",
ephemeral_pin_envelope: PasswordProtectedKeyEnvelope as ts "PasswordProtectedKeyEnvelope",
encrypted_pin: EncString as ts "EncString",
v2_upgrade_token: V2UpgradeToken as ts "V2UpgradeToken",
account_cryptographic_state: WrappedAccountCryptographicState as ts "WrappedAccountCryptographicState",
masterpassword_unlock_data: MasterPasswordUnlockData as ts "MasterPasswordUnlockData",
kdf_config: Kdf as ts "Kdf",
}