new keyring, needs tests
This commit is contained in:
@@ -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.",
|
||||
|
Reference in New Issue
Block a user