fix wasm, flutter work
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
@@ -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)
|
||||
|
@@ -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);
|
||||
|
@@ -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>;
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user