fix wasm, flutter work

This commit is contained in:
John Smith
2022-01-20 22:32:22 -05:00
parent effc4aeeac
commit 2eeb8e52f2
11 changed files with 4588 additions and 180 deletions

View File

@@ -1,5 +1,7 @@
use super::utils;
use crate::xx::*;
use crate::*;
use data_encoding::BASE64URL_NOPAD;
use js_sys::*;
use wasm_bindgen_futures::*;
use web_sys::*;
@@ -14,172 +16,246 @@ extern "C" {
fn keytar_deletePassword(service: &str, account: &str) -> Result<Promise, JsValue>;
}
fn keyring_name(namespace: &str) -> String {
if namespace.len() == 0 {
"veilid".to_owned()
} else {
format!("veilid_{}", namespace)
}
#[derive(Clone)]
pub struct ProtectedStore {
config: VeilidConfig,
}
fn browser_key_name(namespace: &str, key: &str) -> String {
if namespace.len() == 0 {
format!("__veilid_secret_{}", key)
} else {
format!("__veilid_{}_secret_{}", namespace, key)
}
}
impl ProtectedStore {
pub async fn save_user_secret_string(
namespace: &str,
key: &str,
value: &str,
) -> Result<bool, String> {
if utils::is_nodejs() {
let prev = match JsFuture::from(
keytar_getPassword(keyring_name(namespace).as_str(), key)
.map_err(|_| "exception thrown".to_owned())?,
)
.await
{
Ok(v) => v.is_truthy(),
Err(_) => false,
pub fn new(config: VeilidConfig) -> Self {
Self {
config,
}
}
pub async fn delete_all(&self) -> Result<(), String> {
// Delete all known keys
if self.remove_user_secret_string("node_id").await? {
debug!("deleted protected_store key 'node_id'");
}
if self.remove_user_secret_string("node_id_secret").await? {
debug!("deleted protected_store key 'node_id_secret'");
}
if self.remove_user_secret_string("_test_key").await? {
debug!("deleted protected_store key '_test_key'");
}
Ok(())
}
pub async fn init(&self) -> Result<(), String> {
Ok(())
}
pub async fn terminate(&self) {}
fn keyring_name(&self) -> String {
let c = self.config.get();
if c.namespace.is_empty() {
"veilid_protected_store".to_owned()
} else {
format!("veilid_protected_store_{}", c.namespace)
}
}
fn browser_key_name(&self, key: &str) -> String {
let c = self.config.get();
if c.namespace.is_empty() {
format!("__veilid_protected_store_{}", key)
} else {
format!("__veilid_protected_store_{}_{}", c.namespace, key)
}
}
pub async fn save_user_secret_string(&self, key: &str, value: &str) -> Result<bool, String> {
if utils::is_nodejs() {
let prev = match JsFuture::from(
keytar_getPassword(self.keyring_name().as_str(), key)
.map_err(|_| "exception thrown".to_owned())?,
)
.await
{
Ok(v) => v.is_truthy(),
Err(_) => false,
};
match JsFuture::from(
keytar_setPassword(self.keyring_name().as_str(), key, value)
.map_err(|_| "exception thrown".to_owned())?,
)
.await
{
Ok(_) => {}
Err(_) => return Err("Failed to set password".to_owned()),
}
Ok(prev)
} else if utils::is_browser() {
let win = match window() {
Some(w) => w,
None => {
return Err("failed to get window".to_owned());
}
};
let ls = match win
.local_storage()
.map_err(|_| "exception getting local storage".to_owned())?
{
Some(l) => l,
None => {
return Err("failed to get local storage".to_owned());
}
};
let vkey = self.browser_key_name(key);
let prev = match ls
.get_item(&vkey)
.map_err(|_| "exception_thrown".to_owned())?
{
Some(_) => true,
None => false,
};
ls.set_item(&vkey, value)
.map_err(|_| "exception_thrown".to_owned())?;
Ok(prev)
} else {
Err("unimplemented".to_owned())
}
}
pub async fn load_user_secret_string(&self, key: &str) -> Result<Option<String>, String> {
if utils::is_nodejs() {
let prev = match JsFuture::from(
keytar_getPassword(self.keyring_name().as_str(), key)
.map_err(|_| "exception thrown".to_owned())?,
)
.await
{
Ok(p) => p,
Err(_) => JsValue::UNDEFINED,
};
if prev.is_undefined() || prev.is_null() {
return Ok(None);
}
Ok(prev.as_string())
} else if utils::is_browser() {
let win = match window() {
Some(w) => w,
None => {
return Err("failed to get window".to_owned());
}
};
let ls = match win
.local_storage()
.map_err(|_| "exception getting local storage".to_owned())?
{
Some(l) => l,
None => {
return Err("failed to get local storage".to_owned());
}
};
let vkey = self.browser_key_name(key);
ls.get_item(&vkey)
.map_err(|_| "exception_thrown".to_owned())
} else {
Err("unimplemented".to_owned())
}
}
pub async fn remove_user_secret_string(&self, key: &str) -> Result<bool, String> {
if utils::is_nodejs() {
match JsFuture::from(
keytar_deletePassword(self.keyring_name().as_str(), key).map_err(|_| "exception thrown".to_owned())?,
)
.await
{
Ok(v) => Ok(v.is_truthy()),
Err(_) => Err("Failed to delete".to_owned()),
}
} else if utils::is_browser() {
let win = match window() {
Some(w) => w,
None => {
return Err("failed to get window".to_owned());
}
};
let ls = match win
.local_storage()
.map_err(|_| "exception getting local storage".to_owned())?
{
Some(l) => l,
None => {
return Err("failed to get local storage".to_owned());
}
};
let vkey = self.browser_key_name(key);
match ls
.get_item(&vkey)
.map_err(|_| "exception_thrown".to_owned())?
{
Some(_) => {
ls.delete(&vkey)
.map_err(|_| "exception_thrown".to_owned())?;
Ok(true)
}
None => Ok(false),
}
} else {
Err("unimplemented".to_owned())
}
}
pub async fn save_user_secret(&self, key: &str, value: &[u8]) -> Result<bool, String> {
let mut s = BASE64URL_NOPAD.encode(value);
s.push('!');
self.save_user_secret_string(key, s.as_str()).await
}
pub async fn load_user_secret(&self, key: &str) -> Result<Option<Vec<u8>>, String> {
let mut s = match self.load_user_secret_string(key).await? {
Some(s) => s,
None => {
return Ok(None);
}
};
match JsFuture::from(
keytar_setPassword(keyring_name(namespace).as_str(), key, value)
.map_err(|_| "exception thrown".to_owned())?,
)
.await
{
Ok(_) => {}
Err(_) => return Err("Failed to set password".to_owned()),
if s.pop() != Some('!') {
return Err("User secret is not a buffer".to_owned());
}
Ok(prev)
} else if utils::is_browser() {
let win = match window() {
Some(w) => w,
None => {
return Err("failed to get window".to_owned());
let mut bytes = Vec::<u8>::new();
let res = BASE64URL_NOPAD.decode_len(s.len());
match res {
Ok(l) => {
bytes.resize(l, 0u8);
}
};
let ls = match win
.local_storage()
.map_err(|_| "exception getting local storage".to_owned())?
{
Some(l) => l,
None => {
return Err("failed to get local storage".to_owned());
Err(_) => {
return Err("Failed to decode".to_owned());
}
};
let vkey = browser_key_name(namespace, key);
let prev = match ls
.get_item(&vkey)
.map_err(|_| "exception_thrown".to_owned())?
{
Some(_) => true,
None => false,
};
ls.set_item(&vkey, value)
.map_err(|_| "exception_thrown".to_owned())?;
Ok(prev)
} else {
Err("unimplemented".to_owned())
}
}
pub async fn load_user_secret_string(namespace: &str, key: &str) -> Result<Option<String>, String> {
if utils::is_nodejs() {
let prev = match JsFuture::from(
keytar_getPassword(keyring_name(namespace).as_str(), key)
.map_err(|_| "exception thrown".to_owned())?,
)
.await
{
Ok(p) => p,
Err(_) => JsValue::UNDEFINED,
};
if prev.is_undefined() || prev.is_null() {
return Ok(None);
}
Ok(prev.as_string())
} else if utils::is_browser() {
let win = match window() {
Some(w) => w,
None => {
return Err("failed to get window".to_owned());
}
};
let ls = match win
.local_storage()
.map_err(|_| "exception getting local storage".to_owned())?
{
Some(l) => l,
None => {
return Err("failed to get local storage".to_owned());
}
};
let vkey = browser_key_name(namespace, key);
ls.get_item(&vkey)
.map_err(|_| "exception_thrown".to_owned())
} else {
Err("unimplemented".to_owned())
}
}
pub async fn remove_user_secret_string(namespace: &str, key: &str) -> Result<bool, String> {
if utils::is_nodejs() {
match JsFuture::from(
keytar_deletePassword("veilid", key).map_err(|_| "exception thrown".to_owned())?,
)
.await
{
Ok(v) => Ok(v.is_truthy()),
Err(_) => Err("Failed to delete".to_owned()),
let res = BASE64URL_NOPAD.decode_mut(s.as_bytes(), &mut bytes);
match res {
Ok(_) => Ok(Some(bytes)),
Err(_) => Err("Failed to decode".to_owned()),
}
} else if utils::is_browser() {
let win = match window() {
Some(w) => w,
None => {
return Err("failed to get window".to_owned());
}
};
let ls = match win
.local_storage()
.map_err(|_| "exception getting local storage".to_owned())?
{
Some(l) => l,
None => {
return Err("failed to get local storage".to_owned());
}
};
let vkey = browser_key_name(namespace, key);
match ls
.get_item(&vkey)
.map_err(|_| "exception_thrown".to_owned())?
{
Some(_) => {
ls.delete(&vkey)
.map_err(|_| "exception_thrown".to_owned())?;
Ok(true)
}
None => Ok(false),
}
} else {
Err("unimplemented".to_owned())
}
}
pub async fn remove_user_secret(&self, key: &str) -> Result<bool, String> {
self.remove_user_secret_string(key).await
}
}

View File

@@ -54,7 +54,7 @@ impl TableStore {
{
return Err(format!("table name '{}' is invalid", table));
}
let c = inner.config.get();
let c = self.config.get();
let namespace = c.namespace.clone();
Ok(if namespace.len() == 0 {
format!("{}", table)

View File

@@ -64,7 +64,13 @@ wFAbkZY9eS/x6P7qrpd7dUA=
cfg_if! {
if #[cfg(target_arch = "wasm32")] {
pub fn get_tablestore_path() -> String {
pub fn get_table_store_path() -> String {
String::new()
}
pub fn get_block_store_path() -> String {
String::new()
}
pub fn get_protected_store_path() -> String {
String::new()
}
pub fn get_certfile_path() -> String {
@@ -103,6 +109,15 @@ cfg_if! {
out.into_os_string().into_string().unwrap()
}
pub fn get_block_store_path() -> String {
let mut out = get_data_dir();
std::fs::create_dir_all(&out).unwrap();
out.push("block_store");
out.into_os_string().into_string().unwrap()
}
pub fn get_protected_store_path() -> String {
let mut out = get_data_dir();
std::fs::create_dir_all(&out).unwrap();
@@ -156,7 +171,7 @@ pub fn setup_veilid_core() -> VeilidCoreSetup {
}
}
pub fn config_callback(key: String) -> Result<Box<dyn core::any::Any>, String> {
pub fn config_callback(key: String) -> ConfigCallbackReturn {
match key.as_str() {
"program_name" => Ok(Box::new(String::from("Veilid"))),
"namespace" => Ok(Box::new(String::from(""))),
@@ -168,9 +183,13 @@ pub fn config_callback(key: String) -> Result<Box<dyn core::any::Any>, String> {
"capabilities.protocol_connect_wss" => Ok(Box::new(true)),
"capabilities.protocol_accept_wss" => Ok(Box::new(true)),
"table_store.directory" => Ok(Box::new(get_table_store_path())),
"table_store.delete" => Ok(Box::new(false)),
"block_store.directory" => Ok(Box::new(get_block_store_path())),
"block_store.delete" => Ok(Box::new(false)),
"protected_store.allow_insecure_fallback" => Ok(Box::new(true)),
"protected_store.always_use_insecure_storage" => Ok(Box::new(false)),
"protected_store.insecure_fallback_directory" => Ok(Box::new(get_protected_store_path())),
"protected_store.delete" => Ok(Box::new(false)),
"network.max_connections" => Ok(Box::new(16u32)),
"network.connection_initial_timeout" => Ok(Box::new(2_000_000u64)),
"network.node_id" => Ok(Box::new(dht::key::DHTKey::default())),
@@ -264,12 +283,16 @@ pub async fn test_config() {
assert_eq!(inner.capabilities.protocol_connect_wss, true);
assert_eq!(inner.capabilities.protocol_accept_wss, true);
assert_eq!(inner.table_store.directory, get_table_store_path());
assert_eq!(inner.table_store.delete, false);
assert_eq!(inner.block_store.directory, get_block_store_path());
assert_eq!(inner.block_store.delete, false);
assert_eq!(inner.protected_store.allow_insecure_fallback, true);
assert_eq!(inner.protected_store.always_use_insecure_storage, false);
assert_eq!(
inner.protected_store.insecure_fallback_directory,
get_protected_store_path()
);
assert_eq!(inner.protected_store.delete, false);
assert_eq!(inner.network.max_connections, 16);
assert_eq!(inner.network.connection_initial_timeout, 2_000_000u64);
assert!(!inner.network.node_id.valid);

View File

@@ -4,9 +4,12 @@ use crate::xx::*;
cfg_if! {
if #[cfg(target_arch = "wasm32")] {
pub type ConfigCallback = Arc<dyn Fn(String) -> Result<Box<dyn core::any::Any>, String>>;
pub type ConfigCallbackReturn = Result<Box<dyn core::any::Any>, String>;
pub type ConfigCallback = Arc<dyn Fn(String) -> ConfigCallbackReturn>;
} else {
pub type ConfigCallback = Arc<dyn Fn(String) -> Result<Box<dyn core::any::Any>, String> + Send>;
pub type ConfigCallbackReturn = Result<Box<dyn core::any::Any + Send>, String>;
pub type ConfigCallback = Arc<dyn Fn(String) -> ConfigCallbackReturn + Send>;
}
}