Skip to content

Commit

Permalink
Add locking support to bitwarden-json to improve bindings thread safety
Browse files Browse the repository at this point in the history
  • Loading branch information
dani-garcia committed Feb 7, 2024
1 parent 1595306 commit 128f78d
Show file tree
Hide file tree
Showing 8 changed files with 37 additions and 38 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/bitwarden-c/src/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{box_ptr, ffi_ref};
#[tokio::main]
pub async extern "C" fn run_command(
c_str_ptr: *const c_char,
client_ptr: *mut Client,
client_ptr: *const Client,

Check warning on line 11 in crates/bitwarden-c/src/c.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-c/src/c.rs#L11

Added line #L11 was not covered by tests
) -> *mut c_char {
let client = unsafe { ffi_ref!(client_ptr) };
let input_str = str::from_utf8(unsafe { CStr::from_ptr(c_str_ptr).to_bytes() }).unwrap();
Expand Down
2 changes: 1 addition & 1 deletion crates/bitwarden-c/src/macros/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
macro_rules! ffi_ref {
($name:ident) => {{
assert!(!$name.is_null());
&mut *$name
&*$name
}};
}

Expand Down
1 change: 1 addition & 0 deletions crates/bitwarden-json/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ internal = ["bitwarden/internal"] # Internal testing methods
secrets = ["bitwarden/secrets"] # Secrets manager API

[dependencies]
async-lock = ">=3.3.0, <4.0"
log = ">=0.4.18, <0.5"
schemars = ">=0.8.12, <0.9"
serde = { version = ">=1.0, <2.0", features = ["derive"] }
Expand Down
43 changes: 23 additions & 20 deletions crates/bitwarden-json/src/client.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use async_lock::Mutex;
use bitwarden::client::client_settings::ClientSettings;

#[cfg(feature = "secrets")]
Expand All @@ -7,15 +8,15 @@ use crate::{
response::{Response, ResponseIntoString},
};

pub struct Client(bitwarden::Client);
pub struct Client(Mutex<bitwarden::Client>);

impl Client {
pub fn new(settings_input: Option<String>) -> Self {
let settings = Self::parse_settings(settings_input);
Self(bitwarden::Client::new(settings))
Self(Mutex::new(bitwarden::Client::new(settings)))

Check warning on line 16 in crates/bitwarden-json/src/client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-json/src/client.rs#L16

Added line #L16 was not covered by tests
}

pub async fn run_command(&mut self, input_str: &str) -> String {
pub async fn run_command(&self, input_str: &str) -> String {

Check warning on line 19 in crates/bitwarden-json/src/client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-json/src/client.rs#L19

Added line #L19 was not covered by tests
const SUBCOMMANDS_TO_CLEAN: &[&str] = &["Secrets"];
let mut cmd_value: serde_json::Value = match serde_json::from_str(input_str) {
Ok(cmd) => cmd,
Expand Down Expand Up @@ -44,41 +45,43 @@ impl Client {
}
};

let mut client = self.0.lock().await;

Check warning on line 48 in crates/bitwarden-json/src/client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-json/src/client.rs#L48

Added line #L48 was not covered by tests

match cmd {
#[cfg(feature = "internal")]
Command::PasswordLogin(req) => self.0.auth().login_password(&req).await.into_string(),
Command::PasswordLogin(req) => client.auth().login_password(&req).await.into_string(),

Check warning on line 52 in crates/bitwarden-json/src/client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-json/src/client.rs#L52

Added line #L52 was not covered by tests
#[cfg(feature = "secrets")]
Command::AccessTokenLogin(req) => {
self.0.auth().login_access_token(&req).await.into_string()
client.auth().login_access_token(&req).await.into_string()

Check warning on line 55 in crates/bitwarden-json/src/client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-json/src/client.rs#L55

Added line #L55 was not covered by tests
}
#[cfg(feature = "internal")]
Command::GetUserApiKey(req) => self.0.get_user_api_key(&req).await.into_string(),
Command::GetUserApiKey(req) => client.get_user_api_key(&req).await.into_string(),

Check warning on line 58 in crates/bitwarden-json/src/client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-json/src/client.rs#L58

Added line #L58 was not covered by tests
#[cfg(feature = "internal")]
Command::ApiKeyLogin(req) => self.0.auth().login_api_key(&req).await.into_string(),
Command::ApiKeyLogin(req) => client.auth().login_api_key(&req).await.into_string(),

Check warning on line 60 in crates/bitwarden-json/src/client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-json/src/client.rs#L60

Added line #L60 was not covered by tests
#[cfg(feature = "internal")]
Command::Sync(req) => self.0.sync(&req).await.into_string(),
Command::Sync(req) => client.sync(&req).await.into_string(),

Check warning on line 62 in crates/bitwarden-json/src/client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-json/src/client.rs#L62

Added line #L62 was not covered by tests
#[cfg(feature = "internal")]
Command::Fingerprint(req) => self.0.platform().fingerprint(&req).into_string(),
Command::Fingerprint(req) => client.platform().fingerprint(&req).into_string(),

Check warning on line 64 in crates/bitwarden-json/src/client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-json/src/client.rs#L64

Added line #L64 was not covered by tests

#[cfg(feature = "secrets")]
Command::Secrets(cmd) => match cmd {
SecretsCommand::Get(req) => self.0.secrets().get(&req).await.into_string(),
SecretsCommand::Get(req) => client.secrets().get(&req).await.into_string(),

Check warning on line 68 in crates/bitwarden-json/src/client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-json/src/client.rs#L68

Added line #L68 was not covered by tests
SecretsCommand::GetByIds(req) => {
self.0.secrets().get_by_ids(req).await.into_string()
client.secrets().get_by_ids(req).await.into_string()

Check warning on line 70 in crates/bitwarden-json/src/client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-json/src/client.rs#L70

Added line #L70 was not covered by tests
}
SecretsCommand::Create(req) => self.0.secrets().create(&req).await.into_string(),
SecretsCommand::List(req) => self.0.secrets().list(&req).await.into_string(),
SecretsCommand::Update(req) => self.0.secrets().update(&req).await.into_string(),
SecretsCommand::Delete(req) => self.0.secrets().delete(req).await.into_string(),
SecretsCommand::Create(req) => client.secrets().create(&req).await.into_string(),
SecretsCommand::List(req) => client.secrets().list(&req).await.into_string(),
SecretsCommand::Update(req) => client.secrets().update(&req).await.into_string(),
SecretsCommand::Delete(req) => client.secrets().delete(req).await.into_string(),

Check warning on line 75 in crates/bitwarden-json/src/client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-json/src/client.rs#L72-L75

Added lines #L72 - L75 were not covered by tests
},

#[cfg(feature = "secrets")]
Command::Projects(cmd) => match cmd {
ProjectsCommand::Get(req) => self.0.projects().get(&req).await.into_string(),
ProjectsCommand::Create(req) => self.0.projects().create(&req).await.into_string(),
ProjectsCommand::List(req) => self.0.projects().list(&req).await.into_string(),
ProjectsCommand::Update(req) => self.0.projects().update(&req).await.into_string(),
ProjectsCommand::Delete(req) => self.0.projects().delete(req).await.into_string(),
ProjectsCommand::Get(req) => client.projects().get(&req).await.into_string(),
ProjectsCommand::Create(req) => client.projects().create(&req).await.into_string(),
ProjectsCommand::List(req) => client.projects().list(&req).await.into_string(),
ProjectsCommand::Update(req) => client.projects().update(&req).await.into_string(),
ProjectsCommand::Delete(req) => client.projects().delete(req).await.into_string(),

Check warning on line 84 in crates/bitwarden-json/src/client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-json/src/client.rs#L80-L84

Added lines #L80 - L84 were not covered by tests
},
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/bitwarden-napi/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl BitwardenClient {
}

#[napi]
pub async unsafe fn run_command(&mut self, command_input: String) -> String {
pub async fn run_command(&self, command_input: String) -> String {

Check warning on line 41 in crates/bitwarden-napi/src/client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-napi/src/client.rs#L41

Added line #L41 was not covered by tests
self.0.run_command(&command_input).await
}
}
6 changes: 3 additions & 3 deletions crates/bitwarden-py/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ impl BitwardenClient {
}

#[pyo3(text_signature = "($self, command_input)")]
fn run_command(&mut self, command_input: String) -> String {
run_command(&mut self.0, &command_input)
fn run_command(&self, command_input: String) -> String {
run_command(&self.0, &command_input)

Check warning on line 17 in crates/bitwarden-py/src/client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-py/src/client.rs#L16-L17

Added lines #L16 - L17 were not covered by tests
}
}

#[tokio::main]
async fn run_command(client: &mut JsonClient, input_str: &str) -> String {
async fn run_command(client: &JsonClient, input_str: &str) -> String {

Check warning on line 22 in crates/bitwarden-py/src/client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-py/src/client.rs#L22

Added line #L22 was not covered by tests
client.run_command(input_str).await
}
18 changes: 6 additions & 12 deletions crates/bitwarden-wasm/src/client.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
extern crate console_error_panic_hook;
use std::{rc::Rc, sync::RwLock};
use std::rc::Rc;

use bitwarden_json::client::Client as JsonClient;
use js_sys::Promise;
Expand All @@ -26,10 +26,10 @@ fn convert_level(level: LogLevel) -> Level {
}
}

// Rc<RwLock<...>> is to avoid needing to take ownership of the Client during our async run_command
// Rc<...> is to avoid needing to take ownership of the Client during our async run_command
// function https://github.com/rustwasm/wasm-bindgen/issues/2195#issuecomment-799588401
#[wasm_bindgen]
pub struct BitwardenClient(Rc<RwLock<JsonClient>>);
pub struct BitwardenClient(Rc<JsonClient>);

#[wasm_bindgen]
impl BitwardenClient {
Expand All @@ -42,20 +42,14 @@ impl BitwardenClient {
panic!("failed to initialize logger: {:?}", e);
}

Self(Rc::new(RwLock::new(bitwarden_json::client::Client::new(
settings_input,
))))
Self(Rc::new(bitwarden_json::client::Client::new(settings_input)))

Check warning on line 45 in crates/bitwarden-wasm/src/client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-wasm/src/client.rs#L45

Added line #L45 was not covered by tests
}

#[wasm_bindgen]
pub fn run_command(&mut self, js_input: String) -> Promise {
pub fn run_command(&self, js_input: String) -> Promise {

Check warning on line 49 in crates/bitwarden-wasm/src/client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-wasm/src/client.rs#L49

Added line #L49 was not covered by tests
let rc = self.0.clone();
// TODO: We should probably switch to an async-aware RwLock here,
// but it probably doesn't matter much in a single threaded environment
#[allow(clippy::await_holding_lock)]
future_to_promise(async move {
let mut client = rc.write().unwrap();
let result = client.run_command(&js_input).await;
let result = rc.run_command(&js_input).await;

Check warning on line 52 in crates/bitwarden-wasm/src/client.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-wasm/src/client.rs#L52

Added line #L52 was not covered by tests
Ok(result.into())
})
}
Expand Down

0 comments on commit 128f78d

Please sign in to comment.