new keyring, needs tests
This commit is contained in:
parent
0a7ebcb3be
commit
84b1ef5e9e
20
.vscode/launch.json
vendored
20
.vscode/launch.json
vendored
@ -106,6 +106,26 @@
|
||||
},
|
||||
"args": ["${selectedText}"],
|
||||
"cwd": "${workspaceFolder}/external/keyvaluedb/keyvaluedb-sqlite"
|
||||
},
|
||||
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug keyring unit test",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"test",
|
||||
"--no-run",
|
||||
"--manifest-path",
|
||||
"external/keyring-rs/Cargo.toml"
|
||||
],
|
||||
"filter": {
|
||||
"kind": "staticlib",
|
||||
"name": "keyring"
|
||||
}
|
||||
},
|
||||
"args": ["${selectedText}"],
|
||||
"cwd": "${workspaceFolder}/external/keyring-rs"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
28
Cargo.lock
generated
28
Cargo.lock
generated
@ -1407,6 +1407,16 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs4"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cef5c93884e5cef757f63446122c2f420713c3e03f85540d09485b9415983b4a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "1.1.0"
|
||||
@ -1831,6 +1841,7 @@ dependencies = [
|
||||
"core-foundation 0.9.2",
|
||||
"core-foundation-sys 0.8.3",
|
||||
"directories 4.0.1",
|
||||
"fs4",
|
||||
"jni",
|
||||
"keychain-services",
|
||||
"lazy_static",
|
||||
@ -1846,6 +1857,7 @@ dependencies = [
|
||||
"serde_cbor",
|
||||
"serial_test 0.5.1",
|
||||
"simplelog",
|
||||
"snailquote",
|
||||
"tempfile",
|
||||
"winapi",
|
||||
]
|
||||
@ -3372,6 +3384,16 @@ version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
|
||||
|
||||
[[package]]
|
||||
name = "snailquote"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec62a949bda7f15800481a711909f946e1204f2460f89210eaf7f57730f88f86"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"unicode_categories",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.2"
|
||||
@ -3682,6 +3704,12 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode_categories"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.4.1"
|
||||
|
2
external/keyring-rs
vendored
2
external/keyring-rs
vendored
@ -1 +1 @@
|
||||
Subproject commit dadfe40c70d7679a56e36e22240920c78acd0f06
|
||||
Subproject commit 8dd37fa4c629f80951cd78f8cc245317b44bdb05
|
@ -1,7 +1,5 @@
|
||||
mod table_db;
|
||||
mod user_secret;
|
||||
use crate::xx::*;
|
||||
pub use user_secret::*;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
mod wasm;
|
||||
|
@ -1,57 +1,159 @@
|
||||
use cfg_if::*;
|
||||
use keyring::{Keyring, KeyringError};
|
||||
use crate::xx::*;
|
||||
use crate::*;
|
||||
use data_encoding::BASE64URL_NOPAD;
|
||||
use keyring::*;
|
||||
use std::path::Path;
|
||||
use std::result::Result;
|
||||
|
||||
fn keyring_name(namespace: &str) -> String {
|
||||
if namespace.is_empty() {
|
||||
"veilid".to_owned()
|
||||
} else {
|
||||
format!("veilid_{}", namespace)
|
||||
}
|
||||
pub struct ProtectedStoreInner {
|
||||
keyring_manager: Option<KeyringManager>,
|
||||
}
|
||||
|
||||
fn get_keyring<'a>(krname: &'a str, key: &'a str) -> Keyring<'a> {
|
||||
cfg_if! {
|
||||
if #[cfg(target_os = "android")] {
|
||||
let agopt = super::utils::android::ANDROID_GLOBALS.lock();
|
||||
let ag = agopt.as_ref().unwrap();
|
||||
let vm = ag.vm.attach_current_thread().unwrap().get_java_vm().unwrap(); // cmon jni, no clone for javavm
|
||||
let ctx = ag.ctx.clone();
|
||||
Keyring::new("veilid", krname, key, (vm, ctx))
|
||||
} else {
|
||||
Keyring::new("veilid", krname, key)
|
||||
#[derive(Clone)]
|
||||
pub struct ProtectedStore {
|
||||
config: VeilidConfig,
|
||||
inner: Arc<Mutex<ProtectedStoreInner>>,
|
||||
}
|
||||
|
||||
impl ProtectedStore {
|
||||
fn new_inner() -> ProtectedStoreInner {
|
||||
ProtectedStoreInner {
|
||||
keyring_manager: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn save_user_secret_string(
|
||||
namespace: &str,
|
||||
key: &str,
|
||||
value: &str,
|
||||
) -> Result<bool, String> {
|
||||
let krname = keyring_name(namespace);
|
||||
let kr = get_keyring(krname.as_str(), key);
|
||||
let existed = kr.get_password().is_ok();
|
||||
kr.set_password(value)
|
||||
.map_err(|e| format!("Failed to save user secret: {}", e))?;
|
||||
Ok(existed)
|
||||
}
|
||||
pub fn new(config: VeilidConfig) -> Self {
|
||||
Self {
|
||||
config,
|
||||
inner: Arc::new(Mutex::new(Self::new_inner())),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn load_user_secret_string(namespace: &str, key: &str) -> Result<Option<String>, String> {
|
||||
let krname = keyring_name(namespace);
|
||||
let kr = get_keyring(krname.as_str(), key);
|
||||
match kr.get_password() {
|
||||
Ok(v) => Ok(Some(v)),
|
||||
Err(KeyringError::NoPasswordFound) => Ok(None),
|
||||
Err(e) => Err(format!("Failed to load user secret: {}", e)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn remove_user_secret_string(namespace: &str, key: &str) -> Result<bool, String> {
|
||||
let krname = keyring_name(namespace);
|
||||
let kr = get_keyring(krname.as_str(), key);
|
||||
match kr.delete_password() {
|
||||
Ok(_) => Ok(true),
|
||||
Err(KeyringError::NoPasswordFound) => Ok(false),
|
||||
Err(e) => Err(format!("Failed to remove user secret: {}", e)),
|
||||
pub async fn init(&self) -> Result<(), String> {
|
||||
let c = self.config.get();
|
||||
let mut inner = self.inner.lock();
|
||||
if !c.protected_store.always_use_insecure_storage {
|
||||
inner.keyring_manager = KeyringManager::new_secure(&c.program_name).ok();
|
||||
}
|
||||
if (c.protected_store.always_use_insecure_storage
|
||||
|| c.protected_store.allow_insecure_fallback)
|
||||
&& inner.keyring_manager.is_none()
|
||||
{
|
||||
let insecure_fallback_directory =
|
||||
Path::new(&c.protected_store.insecure_fallback_directory);
|
||||
let insecure_keyring_file = insecure_fallback_directory
|
||||
.to_owned()
|
||||
.join("insecure_keyring");
|
||||
inner.keyring_manager = Some(
|
||||
KeyringManager::new_insecure(&c.program_name, &insecure_keyring_file)
|
||||
.map_err(map_to_string)
|
||||
.map_err(logthru_pstore!(error))?,
|
||||
);
|
||||
}
|
||||
if inner.keyring_manager.is_none() {
|
||||
return Err("Could not initialize the protected store.".to_owned());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn terminate(&self) {
|
||||
*self.inner.lock() = Self::new_inner();
|
||||
}
|
||||
|
||||
fn service_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)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn save_user_secret_string(&self, key: &str, value: &str) -> Result<bool, String> {
|
||||
let inner = self.inner.lock();
|
||||
inner
|
||||
.keyring_manager
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Protected store not initialized".to_owned())?
|
||||
.with_keyring(&self.service_name(), key, |kr| {
|
||||
let existed = kr.get_value().is_ok();
|
||||
kr.set_value(value)
|
||||
.map_err(|e| format!("Failed to save user secret: {}", e))?;
|
||||
Ok(existed)
|
||||
})
|
||||
.map_err(map_to_string)
|
||||
.map_err(logthru_pstore!())
|
||||
}
|
||||
|
||||
pub async fn load_user_secret_string(&self, key: &str) -> Result<Option<String>, String> {
|
||||
let inner = self.inner.lock();
|
||||
match inner
|
||||
.keyring_manager
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Protected store not initialized".to_owned())?
|
||||
.with_keyring(&self.service_name(), key, |kr| kr.get_value())
|
||||
.map_err(logthru_pstore!())
|
||||
{
|
||||
Ok(v) => Ok(Some(v)),
|
||||
Err(KeyringError::NoPasswordFound) => Ok(None),
|
||||
Err(e) => Err(format!("Failed to load user secret: {}", e)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn remove_user_secret_string(&self, key: &str) -> Result<bool, String> {
|
||||
let inner = self.inner.lock();
|
||||
match inner
|
||||
.keyring_manager
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Protected store not initialized".to_owned())?
|
||||
.with_keyring(&self.service_name(), key, |kr| kr.delete_value())
|
||||
.map_err(logthru_pstore!())
|
||||
{
|
||||
Ok(_) => Ok(true),
|
||||
Err(KeyringError::NoPasswordFound) => Ok(false),
|
||||
Err(e) => Err(format!("Failed to remove user secret: {}", e)),
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
if s.pop() != Some('!') {
|
||||
return Err("User secret is not a buffer".to_owned());
|
||||
}
|
||||
|
||||
let mut bytes = Vec::<u8>::new();
|
||||
let res = BASE64URL_NOPAD.decode_len(s.len());
|
||||
match res {
|
||||
Ok(l) => {
|
||||
bytes.resize(l, 0u8);
|
||||
}
|
||||
Err(_) => {
|
||||
return Err("Failed to decode".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()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn remove_user_secret(&self, key: &str) -> Result<bool, String> {
|
||||
self.remove_user_secret_string(key).await
|
||||
}
|
||||
}
|
||||
|
@ -5,25 +5,25 @@ use keyvaluedb_sqlite::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
struct TableStoreInner {
|
||||
config: VeilidConfig,
|
||||
opened: BTreeMap<String, Weak<Mutex<TableDBInner>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TableStore {
|
||||
config: VeilidConfig,
|
||||
inner: Arc<Mutex<TableStoreInner>>,
|
||||
}
|
||||
|
||||
impl TableStore {
|
||||
fn new_inner(config: VeilidConfig) -> TableStoreInner {
|
||||
fn new_inner() -> TableStoreInner {
|
||||
TableStoreInner {
|
||||
config,
|
||||
opened: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn new(config: VeilidConfig) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(Mutex::new(Self::new_inner(config))),
|
||||
config,
|
||||
inner: Arc::new(Mutex::new(Self::new_inner())),
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,15 +45,15 @@ impl TableStore {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_dbpath(inner: &TableStoreInner, table: &str) -> Result<PathBuf, String> {
|
||||
fn get_dbpath(&self, table: &str) -> Result<PathBuf, String> {
|
||||
if !table
|
||||
.chars()
|
||||
.all(|c| char::is_alphanumeric(c) || c == '_' || c == '-')
|
||||
{
|
||||
return Err(format!("table name '{}' is invalid", table));
|
||||
}
|
||||
let c = inner.config.get();
|
||||
let tablestoredir = c.tablestore.directory.clone();
|
||||
let c = self.config.get();
|
||||
let tablestoredir = c.table_store.directory.clone();
|
||||
std::fs::create_dir_all(&tablestoredir)
|
||||
.map_err(|e| format!("failed to create tablestore path: {}", e))?;
|
||||
|
||||
@ -61,14 +61,14 @@ impl TableStore {
|
||||
Ok(dbpath)
|
||||
}
|
||||
|
||||
fn get_table_name(inner: &TableStoreInner, table: &str) -> Result<String, String> {
|
||||
fn get_table_name(&self, table: &str) -> Result<String, String> {
|
||||
if !table
|
||||
.chars()
|
||||
.all(|c| char::is_alphanumeric(c) || c == '_' || c == '-')
|
||||
{
|
||||
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.is_empty() {
|
||||
table.to_string()
|
||||
@ -78,9 +78,9 @@ impl TableStore {
|
||||
}
|
||||
|
||||
pub async fn open(&self, name: &str, column_count: u32) -> Result<TableDB, String> {
|
||||
let mut inner = self.inner.lock();
|
||||
let table_name = Self::get_table_name(&*inner, name)?;
|
||||
let table_name = self.get_table_name(name)?;
|
||||
|
||||
let mut inner = self.inner.lock();
|
||||
if let Some(table_db_weak_inner) = inner.opened.get(&table_name) {
|
||||
match TableDB::try_new_from_weak_inner(table_db_weak_inner.clone()) {
|
||||
Some(tdb) => {
|
||||
@ -92,7 +92,7 @@ impl TableStore {
|
||||
};
|
||||
}
|
||||
|
||||
let dbpath = Self::get_dbpath(&inner, &table_name)?;
|
||||
let dbpath = self.get_dbpath(&table_name)?;
|
||||
let cfg = DatabaseConfig::with_columns(column_count);
|
||||
let db =
|
||||
Database::open(&dbpath, cfg).map_err(|e| format!("failed to open tabledb: {}", e))?;
|
||||
@ -110,13 +110,13 @@ impl TableStore {
|
||||
}
|
||||
|
||||
pub async fn delete(&self, name: &str) -> Result<bool, String> {
|
||||
let inner = self.inner.lock();
|
||||
let table_name = Self::get_table_name(&*inner, name)?;
|
||||
let table_name = self.get_table_name(name)?;
|
||||
|
||||
let inner = self.inner.lock();
|
||||
if inner.opened.contains_key(&table_name) {
|
||||
return Err("Not deleting table that is still opened".to_owned());
|
||||
}
|
||||
let dbpath = Self::get_dbpath(&inner, &table_name)?;
|
||||
let dbpath = self.get_dbpath(&table_name)?;
|
||||
let ret = std::fs::remove_file(dbpath).is_ok();
|
||||
Ok(ret)
|
||||
}
|
||||
|
@ -1,43 +0,0 @@
|
||||
use super::*;
|
||||
use data_encoding::BASE64URL_NOPAD;
|
||||
|
||||
pub async fn save_user_secret(namespace: &str, key: &str, value: &[u8]) -> Result<bool, String> {
|
||||
let mut s = BASE64URL_NOPAD.encode(value);
|
||||
s.push('!');
|
||||
|
||||
save_user_secret_string(namespace, key, s.as_str()).await
|
||||
}
|
||||
|
||||
pub async fn load_user_secret(namespace: &str, key: &str) -> Result<Option<Vec<u8>>, String> {
|
||||
let mut s = match load_user_secret_string(namespace, key).await? {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
if s.pop() != Some('!') {
|
||||
return Err("User secret is not a buffer".to_owned());
|
||||
}
|
||||
|
||||
let mut bytes = Vec::<u8>::new();
|
||||
let res = BASE64URL_NOPAD.decode_len(s.len());
|
||||
match res {
|
||||
Ok(l) => {
|
||||
bytes.resize(l, 0u8);
|
||||
}
|
||||
Err(_) => {
|
||||
return Err("Failed to decode".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()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn remove_user_secret(namespace: &str, key: &str) -> Result<bool, String> {
|
||||
remove_user_secret_string(namespace, key).await
|
||||
}
|
@ -4,25 +4,25 @@ use crate::*;
|
||||
use keyvaluedb_web::*;
|
||||
|
||||
struct TableStoreInner {
|
||||
config: VeilidConfig,
|
||||
opened: BTreeMap<String, Weak<Mutex<TableDBInner>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TableStore {
|
||||
config: VeilidConfig,
|
||||
inner: Arc<Mutex<TableStoreInner>>,
|
||||
}
|
||||
|
||||
impl TableStore {
|
||||
fn new_inner(config: VeilidConfig) -> TableStoreInner {
|
||||
fn new_inner() -> TableStoreInner {
|
||||
TableStoreInner {
|
||||
config,
|
||||
opened: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn new(config: VeilidConfig) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(Mutex::new(Self::new_inner(config))),
|
||||
config,
|
||||
inner: Arc::new(Mutex::new(Self::new_inner())),
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,7 +47,7 @@ impl TableStore {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_table_name(inner: &TableStoreInner, table: &str) -> Result<String, String> {
|
||||
fn get_table_name(&self, table: &str) -> Result<String, String> {
|
||||
if !table
|
||||
.chars()
|
||||
.all(|c| char::is_alphanumeric(c) || c == '_' || c == '-')
|
||||
@ -64,9 +64,9 @@ impl TableStore {
|
||||
}
|
||||
|
||||
pub async fn open(&self, name: &str, column_count: u32) -> Result<TableDB, String> {
|
||||
let mut inner = self.inner.lock();
|
||||
let table_name = Self::get_table_name(&*inner, name)?;
|
||||
let table_name = self.get_table_name(name)?;
|
||||
|
||||
let mut inner = self.inner.lock();
|
||||
if let Some(table_db_weak_inner) = inner.opened.get(&table_name) {
|
||||
match TableDB::try_new_from_weak_inner(table_db_weak_inner.clone()) {
|
||||
Some(tdb) => {
|
||||
@ -91,9 +91,9 @@ impl TableStore {
|
||||
|
||||
pub async fn delete(&self, name: &str) -> Result<bool, String> {
|
||||
trace!("TableStore::delete {}", name);
|
||||
let table_name = self.get_table_name(name)?;
|
||||
|
||||
let inner = self.inner.lock();
|
||||
let table_name = Self::get_table_name(&*inner, name)?;
|
||||
|
||||
if inner.opened.contains_key(&table_name) {
|
||||
trace!(
|
||||
"TableStore::delete {}: Not deleting, still open.",
|
||||
|
@ -432,6 +432,10 @@ pub async fn test_split_url() {
|
||||
pub async fn test_protected_store() {
|
||||
info!("testing protected store");
|
||||
|
||||
|
||||
xxx move into its own test
|
||||
|
||||
|
||||
let _ = intf::remove_user_secret("test", "_test_key").await;
|
||||
let _ = intf::remove_user_secret("test", "_test_broken").await;
|
||||
|
||||
|
@ -94,11 +94,20 @@ cfg_if! {
|
||||
out
|
||||
}
|
||||
|
||||
pub fn get_tablestore_path() -> String {
|
||||
pub fn get_table_store_path() -> String {
|
||||
let mut out = get_data_dir();
|
||||
std::fs::create_dir_all(&out).unwrap();
|
||||
|
||||
out.push("tablestore");
|
||||
out.push("table_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();
|
||||
|
||||
out.push("protected_store");
|
||||
|
||||
out.into_os_string().into_string().unwrap()
|
||||
}
|
||||
@ -149,6 +158,7 @@ pub fn setup_veilid_core() -> VeilidCoreSetup {
|
||||
|
||||
pub fn config_callback(key: String) -> Result<Box<dyn core::any::Any>, String> {
|
||||
match key.as_str() {
|
||||
"program_name" => Ok(Box::new(String::from("Veilid"))),
|
||||
"namespace" => Ok(Box::new(String::from(""))),
|
||||
"capabilities.protocol_udp" => Ok(Box::new(true)),
|
||||
"capabilities.protocol_connect_tcp" => Ok(Box::new(true)),
|
||||
@ -157,7 +167,10 @@ pub fn config_callback(key: String) -> Result<Box<dyn core::any::Any>, String> {
|
||||
"capabilities.protocol_accept_ws" => Ok(Box::new(true)),
|
||||
"capabilities.protocol_connect_wss" => Ok(Box::new(true)),
|
||||
"capabilities.protocol_accept_wss" => Ok(Box::new(true)),
|
||||
"tablestore.directory" => Ok(Box::new(get_tablestore_path())),
|
||||
"tablestore.directory" => Ok(Box::new(get_table_store_path())),
|
||||
"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())),
|
||||
"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())),
|
||||
@ -240,6 +253,7 @@ pub async fn test_config() {
|
||||
}
|
||||
}
|
||||
let inner = vc.get();
|
||||
assert_eq!(inner.program_name, String::from("Veilid"));
|
||||
assert_eq!(inner.namespace, String::from(""));
|
||||
assert_eq!(inner.capabilities.protocol_udp, true);
|
||||
assert_eq!(inner.capabilities.protocol_connect_tcp, true);
|
||||
@ -248,7 +262,13 @@ pub async fn test_config() {
|
||||
assert_eq!(inner.capabilities.protocol_accept_ws, true);
|
||||
assert_eq!(inner.capabilities.protocol_connect_wss, true);
|
||||
assert_eq!(inner.capabilities.protocol_accept_wss, true);
|
||||
assert_eq!(inner.tablestore.directory, get_tablestore_path());
|
||||
assert_eq!(inner.table_store.directory, get_table_store_path());
|
||||
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.network.max_connections, 16);
|
||||
assert_eq!(inner.network.connection_initial_timeout, 2_000_000u64);
|
||||
assert!(inner.network.node_id.valid);
|
||||
|
@ -143,6 +143,13 @@ pub struct VeilidConfigTableStore {
|
||||
pub directory: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct VeilidConfigProtectedStore {
|
||||
pub allow_insecure_fallback: bool,
|
||||
pub always_use_insecure_storage: bool,
|
||||
pub insecure_fallback_directory: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct VeilidConfigCapabilities {
|
||||
pub protocol_udp: bool,
|
||||
@ -156,9 +163,11 @@ pub struct VeilidConfigCapabilities {
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct VeilidConfigInner {
|
||||
pub program_name: String,
|
||||
pub namespace: String,
|
||||
pub capabilities: VeilidConfigCapabilities,
|
||||
pub tablestore: VeilidConfigTableStore,
|
||||
pub protected_store: VeilidConfigProtectedStore,
|
||||
pub table_store: VeilidConfigTableStore,
|
||||
pub network: VeilidConfigNetwork,
|
||||
}
|
||||
|
||||
@ -197,6 +206,7 @@ impl VeilidConfig {
|
||||
|
||||
{
|
||||
let mut inner = self.inner.write();
|
||||
get_config!(inner.program_name);
|
||||
get_config!(inner.namespace);
|
||||
get_config!(inner.capabilities.protocol_udp);
|
||||
get_config!(inner.capabilities.protocol_connect_tcp);
|
||||
@ -205,7 +215,10 @@ impl VeilidConfig {
|
||||
get_config!(inner.capabilities.protocol_accept_ws);
|
||||
get_config!(inner.capabilities.protocol_connect_wss);
|
||||
get_config!(inner.capabilities.protocol_accept_wss);
|
||||
get_config!(inner.tablestore.directory);
|
||||
get_config!(inner.table_store.directory);
|
||||
get_config!(inner.protected_store.allow_insecure_fallback);
|
||||
get_config!(inner.protected_store.always_use_insecure_storage);
|
||||
get_config!(inner.protected_store.insecure_fallback_directory);
|
||||
get_config!(inner.network.node_id);
|
||||
get_config!(inner.network.node_id_secret);
|
||||
get_config!(inner.network.max_connections);
|
||||
@ -271,11 +284,6 @@ impl VeilidConfig {
|
||||
get_config!(inner.network.leases.max_client_signal_leases);
|
||||
get_config!(inner.network.leases.max_client_relay_leases);
|
||||
}
|
||||
|
||||
// Initialize node id as early as possible because it is used
|
||||
// for encryption purposes all over the program
|
||||
self.init_node_id().await?;
|
||||
|
||||
// Validate settings
|
||||
self.validate().await?;
|
||||
|
||||
@ -292,6 +300,11 @@ impl VeilidConfig {
|
||||
|
||||
async fn validate(&self) -> Result<(), String> {
|
||||
let inner = self.inner.read();
|
||||
|
||||
if inner.program_name.is_empty() {
|
||||
return Err("Program name must not be empty in 'program_name'".to_owned());
|
||||
}
|
||||
|
||||
// if inner.network.protocol.udp.enabled {
|
||||
// // Validate UDP settings
|
||||
// }
|
||||
@ -367,16 +380,16 @@ impl VeilidConfig {
|
||||
}
|
||||
|
||||
// Get the node id from config if one is specified
|
||||
async fn init_node_id(&self) -> Result<(), String> {
|
||||
// Must be done -after- protected store startup
|
||||
pub async fn init_node_id(&self, protected_store: intf::ProtectedStore) -> Result<(), String> {
|
||||
let mut inner = self.inner.write();
|
||||
|
||||
let namespace = inner.namespace.clone();
|
||||
let mut node_id = inner.network.node_id;
|
||||
let mut node_id_secret = inner.network.node_id_secret;
|
||||
// See if node id was previously stored in the protected store
|
||||
if !node_id.valid {
|
||||
debug!("pulling node id from storage");
|
||||
if let Some(s) = intf::load_user_secret_string(namespace.as_str(), "node_id").await? {
|
||||
if let Some(s) = protected_store.load_user_secret_string("node_id").await? {
|
||||
debug!("node id found in storage");
|
||||
node_id = key::DHTKey::try_decode(s.as_str())?
|
||||
} else {
|
||||
@ -387,8 +400,9 @@ impl VeilidConfig {
|
||||
// See if node id secret was previously stored in the protected store
|
||||
if !node_id_secret.valid {
|
||||
debug!("pulling node id secret from storage");
|
||||
if let Some(s) =
|
||||
intf::load_user_secret_string(namespace.as_str(), "node_id_secret").await?
|
||||
if let Some(s) = protected_store
|
||||
.load_user_secret_string("node_id_secret")
|
||||
.await?
|
||||
{
|
||||
debug!("node id secret found in storage");
|
||||
node_id_secret = key::DHTKeySecret::try_decode(s.as_str())?
|
||||
@ -416,14 +430,12 @@ impl VeilidConfig {
|
||||
// info!("Node Id Secret is {}", node_id_secret.encode());
|
||||
|
||||
// Save the node id / secret in storage
|
||||
intf::save_user_secret_string(namespace.as_str(), "node_id", node_id.encode().as_str())
|
||||
protected_store
|
||||
.save_user_secret_string("node_id", node_id.encode().as_str())
|
||||
.await?;
|
||||
protected_store
|
||||
.save_user_secret_string("node_id_secret", node_id_secret.encode().as_str())
|
||||
.await?;
|
||||
intf::save_user_secret_string(
|
||||
namespace.as_str(),
|
||||
"node_id_secret",
|
||||
node_id_secret.encode().as_str(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
inner.network.node_id = node_id;
|
||||
inner.network.node_id_secret = node_id_secret;
|
||||
|
@ -33,6 +33,7 @@ pub struct VeilidCoreSetup {
|
||||
|
||||
struct VeilidCoreInner {
|
||||
config: Option<VeilidConfig>,
|
||||
protected_store: Option<ProtectedStore>,
|
||||
table_store: Option<TableStore>,
|
||||
crypto: Option<Crypto>,
|
||||
attachment_manager: Option<AttachmentManager>,
|
||||
@ -55,6 +56,7 @@ impl VeilidCore {
|
||||
VeilidCoreInner {
|
||||
config: None,
|
||||
table_store: None,
|
||||
protected_store: None,
|
||||
crypto: None,
|
||||
attachment_manager: None,
|
||||
api: VeilidAPIWeak::default(),
|
||||
@ -110,8 +112,17 @@ impl VeilidCore {
|
||||
config.init(setup.config_callback).await?;
|
||||
inner.config = Some(config.clone());
|
||||
|
||||
// Set up protected store
|
||||
trace!("VeilidCore::internal_startup init protected store");
|
||||
let protected_store = ProtectedStore::new(config.clone());
|
||||
protected_store.init().await?;
|
||||
inner.protected_store = Some(protected_store.clone());
|
||||
|
||||
// Init node id from config now that protected store is set up
|
||||
config.init_node_id(protected_store).await?;
|
||||
|
||||
// Set up tablestore
|
||||
trace!("VeilidCore::internal_startup init tablestore");
|
||||
trace!("VeilidCore::internal_startup init table store");
|
||||
let table_store = TableStore::new(config.clone());
|
||||
table_store.init().await?;
|
||||
inner.table_store = Some(table_store.clone());
|
||||
@ -187,12 +198,18 @@ impl VeilidCore {
|
||||
inner.crypto = None;
|
||||
}
|
||||
|
||||
// Shut down tablestore
|
||||
// Shut down table store
|
||||
if let Some(table_store) = &inner.table_store {
|
||||
table_store.terminate().await;
|
||||
inner.table_store = None;
|
||||
}
|
||||
|
||||
// Shut down protected store
|
||||
if let Some(protected_store) = &inner.protected_store {
|
||||
protected_store.terminate().await;
|
||||
inner.protected_store = None;
|
||||
}
|
||||
|
||||
// Shut down config
|
||||
if let Some(config) = &inner.config {
|
||||
config.terminate().await;
|
||||
|
@ -134,7 +134,6 @@ macro_rules! logthru_rpc {
|
||||
logthru!($($level)? "rpc", $fmt, $($arg),+)
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! logthru_rtab {
|
||||
($($level:ident)?) => {
|
||||
@ -147,6 +146,18 @@ macro_rules! logthru_rtab {
|
||||
logthru!($($level)? "rtab", $fmt, $($arg),+)
|
||||
}
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! logthru_pstore {
|
||||
($($level:ident)?) => {
|
||||
logthru!($($level)? "pstore")
|
||||
};
|
||||
($($level:ident)? $text:literal) => {
|
||||
logthru!($($level)? "pstore", $text)
|
||||
};
|
||||
($($level:ident)? $fmt:literal, $($arg:expr),+) => {
|
||||
logthru!($($level)? "pstore", $fmt, $($arg),+)
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! logthru {
|
||||
|
@ -35,8 +35,12 @@ logging:
|
||||
testing:
|
||||
subnode_index: 0
|
||||
core:
|
||||
tablestore:
|
||||
directory: "%TABLESTORE_DIRECTORY%"
|
||||
protected_store:
|
||||
allow_insecure_fallback: true,
|
||||
always_use_insecure_storage: false,
|
||||
insecure_fallback_directory: "%INSECURE_FALLBACK_DIRECTORY%",
|
||||
table_store:
|
||||
directory: "%TABLE_STORE_DIRECTORY%"
|
||||
network:
|
||||
max_connections: 16
|
||||
connection_initial_timeout: 2000000
|
||||
@ -117,8 +121,12 @@ core:
|
||||
"#,
|
||||
)
|
||||
.replace(
|
||||
"%TABLESTORE_DIRECTORY%",
|
||||
"%TABLE_STORE_DIRECTORY%",
|
||||
&Settings::get_default_table_store_path().to_string_lossy(),
|
||||
)
|
||||
.replace(
|
||||
"%INSECURE_FALLBACK_DIRECTORY%",
|
||||
&Settings::get_default_protected_store_insecure_fallback_directory().to_string_lossy(),
|
||||
);
|
||||
cfg.merge(config::File::from_str(
|
||||
&default_config,
|
||||
@ -526,9 +534,17 @@ pub struct TableStore {
|
||||
pub directory: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct ProtectedStore {
|
||||
pub allow_insecure_fallback: bool,
|
||||
pub always_use_insecure_storage: bool,
|
||||
pub insecure_fallback_directory: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Core {
|
||||
pub tablestore: TableStore,
|
||||
pub protected_store: ProtectedStore,
|
||||
pub table_store: TableStore,
|
||||
pub network: Network,
|
||||
}
|
||||
|
||||
@ -673,7 +689,21 @@ impl Settings {
|
||||
} else {
|
||||
default_config_path = PathBuf::from("./");
|
||||
}
|
||||
default_config_path.push("tablestore");
|
||||
default_config_path.push("table_store");
|
||||
|
||||
default_config_path
|
||||
}
|
||||
|
||||
pub fn get_default_protected_store_insecure_fallback_directory() -> PathBuf {
|
||||
// Get default configuration file location
|
||||
let mut default_config_path;
|
||||
|
||||
if let Some(my_proj_dirs) = ProjectDirs::from("org", "Veilid", "Veilid") {
|
||||
default_config_path = PathBuf::from(my_proj_dirs.data_local_dir());
|
||||
} else {
|
||||
default_config_path = PathBuf::from("./");
|
||||
}
|
||||
default_config_path.push("protected_store");
|
||||
|
||||
default_config_path
|
||||
}
|
||||
@ -684,6 +714,7 @@ impl Settings {
|
||||
Arc::new(move |key: String| {
|
||||
let inner = inner.read();
|
||||
let out: Result<Box<dyn core::any::Any>, String> = match key.as_str() {
|
||||
"program_name" => Ok(Box::new("veilid-server".to_owned())),
|
||||
"namespace" => Ok(Box::new(if inner.testing.subnode_index == 0 {
|
||||
"".to_owned()
|
||||
} else {
|
||||
@ -696,10 +727,24 @@ impl Settings {
|
||||
"capabilities.protocol_accept_ws" => Ok(Box::new(true)),
|
||||
"capabilities.protocol_connect_wss" => Ok(Box::new(true)),
|
||||
"capabilities.protocol_accept_wss" => Ok(Box::new(true)),
|
||||
"tablestore.directory" => Ok(Box::new(
|
||||
"protected_store.allow_insecure_fallback" => {
|
||||
Ok(Box::new(inner.core.protected_store.allow_insecure_fallback))
|
||||
}
|
||||
"protected_store.always_use_insecure_storage" => Ok(Box::new(
|
||||
inner.core.protected_store.always_use_insecure_storage,
|
||||
)),
|
||||
"protected_store.insecure_fallback_directory" => Ok(Box::new(
|
||||
inner
|
||||
.core
|
||||
.tablestore
|
||||
.protected_store
|
||||
.insecure_fallback_directory
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
)),
|
||||
"table_store.directory" => Ok(Box::new(
|
||||
inner
|
||||
.core
|
||||
.table_store
|
||||
.directory
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
@ -1027,9 +1072,15 @@ mod tests {
|
||||
assert_eq!(s.logging.client.level, LogLevel::Info);
|
||||
assert_eq!(s.testing.subnode_index, 0);
|
||||
assert_eq!(
|
||||
s.core.tablestore.directory,
|
||||
s.core.table_store.directory,
|
||||
Settings::get_default_table_store_path()
|
||||
);
|
||||
assert_eq!(s.core.protected_store.allow_insecure_fallback, true);
|
||||
assert_eq!(s.core.protected_store.always_use_insecure_storage, false);
|
||||
assert_eq!(
|
||||
s.protected_store.insecure_fallback_directory,
|
||||
Settings::get_default_protected_store_insecure_fallback_directory()
|
||||
);
|
||||
assert_eq!(s.core.network.max_connections, 16);
|
||||
assert_eq!(s.core.network.connection_initial_timeout, 2_000_000u64);
|
||||
assert_eq!(s.core.network.node_id, veilid_core::DHTKey::default());
|
||||
|
Loading…
Reference in New Issue
Block a user