From 7cef383f685c8b7e1c0c20dd605e52c1c6252ea9 Mon Sep 17 00:00:00 2001 From: Daniel Olano Date: Tue, 29 Nov 2022 20:48:09 +0100 Subject: [PATCH] Refactor vault trait (#25) * Change Vault's unlock signature + remove get_root * Update vault implementations to match updated trait --- Cargo.toml | 2 +- examples/persisted_in_pass.rs | 18 +++++------------- src/lib.rs | 30 ++++++++++++++++++----------- src/vault/os.rs | 18 ++++++++---------- src/vault/pass.rs | 36 +++++++++++++++++++---------------- src/vault/simple.rs | 13 +++++++------ 6 files changed, 60 insertions(+), 57 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a6c9779..7c0bd73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ path = "examples/account_generation.rs" # name = "persisted_in_keyring" # path = "examples/persisted_in_keyring.rs" -# needs feature vault_pass dirs +# needs feature vault_pass # [[example]] # name = "persisted_in_pass" # path = "examples/persisted_in_pass.rs" diff --git a/examples/persisted_in_pass.rs b/examples/persisted_in_pass.rs index 86a2cf4..67df521 100644 --- a/examples/persisted_in_pass.rs +++ b/examples/persisted_in_pass.rs @@ -1,26 +1,18 @@ -use libwallet::{ - self, - vault::{Pass, PassCreds}, - Language, -}; use dirs::home_dir; -use std::{error::Error}; +use libwallet::{self, vault::Pass, Language}; +use std::error::Error; type Wallet = libwallet::Wallet; -const SECRET_PATH: &str = "pandres95"; - #[async_std::main] async fn main() -> Result<(), Box> { + // first argument is used as account + let account = std::env::args().skip(1).next().unwrap_or("default".into()); let mut store_path = home_dir().expect("Could not find home path"); store_path.push(".password-store"); let vault = Pass::new(store_path.to_str().unwrap(), Language::default()); let mut wallet = Wallet::new(vault); - wallet - .unlock(PassCreds { - secret_path: String::from(SECRET_PATH), - }) - .await?; + wallet.unlock(account).await?; let account = wallet.default_account(); println!("Default account: {}", account); diff --git a/src/lib.rs b/src/lib.rs index 3082cf1..13a4869 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,12 +37,12 @@ pub trait Vault { /// Use a set of credentials to make the guarded keys available to the user. /// It returns a `Future` to allow for vaults that might take an arbitrary amount - /// of time getting the secret ready like waiting for some user phisical interaction. - async fn unlock(&mut self, cred: impl Into) -> Result<(), Self::Error>; - - /// Get the root account container of the supported private key pairs - /// if the vault hasn't been unlocked it should return `None` - fn get_root(&self) -> Option<&RootAccount>; + /// of time getting the secret ready like waiting for some user physical interaction. + async fn unlock( + &mut self, + cred: &Self::Credentials, + cb: impl FnMut(&RootAccount) -> T, + ) -> Result; } /// The root account is a container of the key pairs stored in the vault and cannot be @@ -90,8 +90,9 @@ impl<'a> Derive for &'a RootAccount { /// /// Wallets also support queuing and bulk signing of messages in case transactions need to be reviewed before signing. #[derive(Debug)] -pub struct Wallet { +pub struct Wallet { vault: V, + cached_creeds: Option, default_account: Account, accounts: ArrayVec, pending_sign: ArrayVec<(Message, Option), M>, // message -> account index or default @@ -108,6 +109,7 @@ where default_account: Account::new(None), accounts: ArrayVec::new_const(), pending_sign: ArrayVec::new(), + cached_creeds: None, } } @@ -138,18 +140,24 @@ where credentials: impl Into, ) -> Result<(), Error> { if self.is_locked() { - self.vault - .unlock(credentials) + let vault = &mut self.vault; + let def = &mut self.default_account; + let creds = credentials.into(); + + vault + .unlock(&creds, |root| { + def.unlock(root); + }) .await .map_err(|e| Error::Vault(e))?; - self.default_account.unlock(self.vault.get_root().unwrap()); + self.cached_creeds = Some(creds); } Ok(()) } /// Check if the vault has been unlocked. pub fn is_locked(&self) -> bool { - self.vault.get_root().is_none() + self.cached_creeds.is_none() } /// Sign a message with the default account and return the signature. diff --git a/src/vault/os.rs b/src/vault/os.rs index 9978958..5a791c7 100644 --- a/src/vault/os.rs +++ b/src/vault/os.rs @@ -3,7 +3,6 @@ use crate::{ util::Pin, RootAccount, Vault, }; -use core::future::Ready; use keyring; const SERVICE: &str = "libwallet_account"; @@ -93,21 +92,20 @@ impl Vault for OSKeyring { type Credentials = Pin; type Error = Error; - async fn unlock(&mut self, pin: impl Into) -> Result<(), Self::Error> { - let pin = pin.into(); - self.get_key(&pin) + async fn unlock( + &mut self, + pin: &Self::Credentials, + mut cb: impl FnMut(&RootAccount) -> T, + ) -> Result { + self.get_key(pin) .or_else(|err| { self.auto_generate .ok_or(err) .and_then(|l| self.generate(&pin, l)) }) - .and_then(move |r| { + .and_then(|r| { self.root = Some(r); - Ok(()) + Ok(cb(self.root.as_ref().unwrap())) }) } - - fn get_root(&self) -> Option<&RootAccount> { - self.root.as_ref() - } } diff --git a/src/vault/pass.rs b/src/vault/pass.rs index 307963f..96e5bc7 100644 --- a/src/vault/pass.rs +++ b/src/vault/pass.rs @@ -1,4 +1,3 @@ -use core::future::Ready; use mnemonic::Language; use prs_lib::{ crypto::{self, IsContext, Proto}, @@ -15,7 +14,7 @@ pub struct Pass { auto_generate: Option, } -const SERVICE_NAME: &str = "libwallet_service/"; +const DEFAULT_DIR: &str = "libwallet_accounts/"; impl Pass { /// Create a new `Pass` vault in the given location. @@ -32,8 +31,8 @@ impl Pass { } fn get_key(&self, credentials: &PassCreds) -> Result { - let mut secret_path = String::from(SERVICE_NAME); - secret_path.push_str(&credentials.secret_path); + let mut secret_path = String::from(DEFAULT_DIR); + secret_path.push_str(&credentials.account); let secret = match self.store.find(Some(secret_path)) { FindSecret::Exact(secret) => Some(secret), @@ -63,8 +62,8 @@ impl Pass { let phrase = crate::util::gen_phrase(&mut rand_core::OsRng, lang); - let mut secret_path = String::from(SERVICE_NAME); - secret_path.push_str(&credentials.secret_path); + let mut secret_path = String::from(DEFAULT_DIR); + secret_path.push_str(&credentials.account); let secret_path = self .store .normalize_secret_path(secret_path, None, true) @@ -112,28 +111,33 @@ impl core::fmt::Display for Error { impl std::error::Error for Error {} pub struct PassCreds { - pub secret_path: String, + account: String, +} + +impl From for PassCreds { + fn from(account: String) -> Self { + PassCreds { account } + } } impl Vault for Pass { type Credentials = PassCreds; type Error = Error; - async fn unlock(&mut self, creds: impl Into) -> Result<(), Self::Error> { - let creds = creds.into(); - self.get_key(&creds) + async fn unlock( + &mut self, + creds: &Self::Credentials, + mut cb: impl FnMut(&RootAccount) -> T, + ) -> Result { + self.get_key(creds) .or_else(|err| { self.auto_generate .ok_or(err) .and_then(|l| self.generate(&creds, l)) }) - .and_then(move |r| { + .and_then(|r| { self.root = Some(r); - Ok(()) + Ok(cb(self.root.as_ref().unwrap())) }) } - - fn get_root(&self) -> Option<&RootAccount> { - self.root.as_ref() - } } diff --git a/src/vault/simple.rs b/src/vault/simple.rs index 68dd5ce..2c2b3d9 100644 --- a/src/vault/simple.rs +++ b/src/vault/simple.rs @@ -79,12 +79,13 @@ impl Vault for Simple { type Credentials = (); type Error = Error; - async fn unlock(&mut self, _cred: impl Into) -> Result<(), Self::Error> { + async fn unlock( + &mut self, + _cred: &Self::Credentials, + mut cb: impl FnMut(&RootAccount) -> T, + ) -> Result { self.unlocked = self.locked.take(); - Ok(()) - } - - fn get_root(&self) -> Option<&RootAccount> { - self.unlocked.as_ref() + let root_account = &self.unlocked.as_ref().unwrap(); + Ok(cb(root_account)) } }