impl linux credential storage
Signed-off-by: kernelkind <kernelkind@gmail.com>
This commit is contained in:
@@ -1,3 +1,7 @@
|
|||||||
|
use nostr::nips::nip49::EncryptedSecretKey;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::Pubkey;
|
use crate::Pubkey;
|
||||||
use crate::SecretKey;
|
use crate::SecretKey;
|
||||||
|
|
||||||
@@ -93,3 +97,34 @@ impl std::fmt::Display for FullKeypair {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct SerializableKeypair {
|
||||||
|
pub pubkey: Pubkey,
|
||||||
|
pub encrypted_secret_key: Option<EncryptedSecretKey>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SerializableKeypair {
|
||||||
|
pub fn from_keypair(kp: &Keypair, pass: &str, log_n: u8) -> Self {
|
||||||
|
Self {
|
||||||
|
pubkey: kp.pubkey.clone(),
|
||||||
|
encrypted_secret_key: kp
|
||||||
|
.secret_key
|
||||||
|
.clone()
|
||||||
|
.map(|s| {
|
||||||
|
EncryptedSecretKey::new(&s, pass, log_n, nostr::nips::nip49::KeySecurity::Weak)
|
||||||
|
.ok()
|
||||||
|
})
|
||||||
|
.flatten(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_keypair(&self, pass: &str) -> Keypair {
|
||||||
|
Keypair::new(
|
||||||
|
self.pubkey.clone(),
|
||||||
|
self.encrypted_secret_key
|
||||||
|
.map(|e| e.to_secret_key(pass).ok())
|
||||||
|
.flatten(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ pub use error::Error;
|
|||||||
pub use event::{Event, EventId};
|
pub use event::{Event, EventId};
|
||||||
pub use ewebsock;
|
pub use ewebsock;
|
||||||
pub use filter::Filter;
|
pub use filter::Filter;
|
||||||
pub use keypair::{FullKeypair, Keypair};
|
pub use keypair::{FullKeypair, Keypair, SerializableKeypair};
|
||||||
pub use nostr::SecretKey;
|
pub use nostr::SecretKey;
|
||||||
pub use note::NoteId;
|
pub use note::NoteId;
|
||||||
pub use profile::Profile;
|
pub use profile::Profile;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::cmp::Ordering;
|
|||||||
|
|
||||||
use enostr::Keypair;
|
use enostr::Keypair;
|
||||||
|
|
||||||
use crate::key_storage::KeyStorage;
|
use crate::key_storage::{KeyStorage, KeyStorageResponse, KeyStorageType};
|
||||||
pub use crate::user_account::UserAccount;
|
pub use crate::user_account::UserAccount;
|
||||||
|
|
||||||
/// The interface for managing the user's accounts.
|
/// The interface for managing the user's accounts.
|
||||||
@@ -10,12 +10,16 @@ pub use crate::user_account::UserAccount;
|
|||||||
pub struct AccountManager {
|
pub struct AccountManager {
|
||||||
currently_selected_account: Option<usize>,
|
currently_selected_account: Option<usize>,
|
||||||
accounts: Vec<UserAccount>,
|
accounts: Vec<UserAccount>,
|
||||||
key_store: KeyStorage,
|
key_store: KeyStorageType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AccountManager {
|
impl AccountManager {
|
||||||
pub fn new(currently_selected_account: Option<usize>, key_store: KeyStorage) -> Self {
|
pub fn new(currently_selected_account: Option<usize>, key_store: KeyStorageType) -> Self {
|
||||||
let accounts = key_store.get_keys().unwrap_or_default();
|
let accounts = if let KeyStorageResponse::ReceivedResult(res) = key_store.get_keys() {
|
||||||
|
res.unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
};
|
||||||
|
|
||||||
AccountManager {
|
AccountManager {
|
||||||
currently_selected_account,
|
currently_selected_account,
|
||||||
|
|||||||
@@ -723,7 +723,7 @@ impl Damus {
|
|||||||
// TODO: should pull this from settings
|
// TODO: should pull this from settings
|
||||||
None,
|
None,
|
||||||
// TODO: use correct KeyStorage mechanism for current OS arch
|
// TODO: use correct KeyStorage mechanism for current OS arch
|
||||||
crate::key_storage::KeyStorage::None,
|
crate::key_storage::KeyStorageType::None,
|
||||||
),
|
),
|
||||||
//compose: "".to_string(),
|
//compose: "".to_string(),
|
||||||
frame_history: FrameHistory::default(),
|
frame_history: FrameHistory::default(),
|
||||||
@@ -754,7 +754,7 @@ impl Damus {
|
|||||||
timelines,
|
timelines,
|
||||||
textmode: false,
|
textmode: false,
|
||||||
ndb: Ndb::new(data_path.as_ref().to_str().expect("db path ok"), &config).expect("ndb"),
|
ndb: Ndb::new(data_path.as_ref().to_str().expect("db path ok"), &config).expect("ndb"),
|
||||||
account_manager: AccountManager::new(None, crate::key_storage::KeyStorage::None),
|
account_manager: AccountManager::new(None, crate::key_storage::KeyStorageType::None),
|
||||||
frame_history: FrameHistory::default(),
|
frame_history: FrameHistory::default(),
|
||||||
show_account_switcher: false,
|
show_account_switcher: false,
|
||||||
show_global_popup: true,
|
show_global_popup: true,
|
||||||
|
|||||||
@@ -1,67 +1,88 @@
|
|||||||
use enostr::Keypair;
|
use enostr::Keypair;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use crate::linux_key_storage::LinuxKeyStorage;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use crate::macos_key_storage::MacOSKeyStorage;
|
use crate::macos_key_storage::MacOSKeyStorage;
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
pub const SERVICE_NAME: &str = "Notedeck";
|
pub const SERVICE_NAME: &str = "Notedeck";
|
||||||
|
|
||||||
pub enum KeyStorage {
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum KeyStorageType {
|
||||||
None,
|
None,
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
MacOS,
|
MacOS,
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
Linux,
|
||||||
// TODO:
|
// TODO:
|
||||||
// Linux,
|
|
||||||
// Windows,
|
// Windows,
|
||||||
// Android,
|
// Android,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyStorage {
|
#[allow(dead_code)]
|
||||||
pub fn get_keys(&self) -> Result<Vec<Keypair>, KeyStorageError> {
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum KeyStorageResponse<R> {
|
||||||
|
Waiting,
|
||||||
|
ReceivedResult(Result<R, KeyStorageError>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait KeyStorage {
|
||||||
|
fn get_keys(&self) -> KeyStorageResponse<Vec<Keypair>>;
|
||||||
|
fn add_key(&self, key: &Keypair) -> KeyStorageResponse<()>;
|
||||||
|
fn remove_key(&self, key: &Keypair) -> KeyStorageResponse<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyStorage for KeyStorageType {
|
||||||
|
fn get_keys(&self) -> KeyStorageResponse<Vec<Keypair>> {
|
||||||
match self {
|
match self {
|
||||||
Self::None => Ok(Vec::new()),
|
Self::None => KeyStorageResponse::ReceivedResult(Ok(Vec::new())),
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
Self::MacOS => Ok(MacOSKeyStorage::new(SERVICE_NAME).get_all_keypairs()),
|
Self::MacOS => MacOSKeyStorage::new(SERVICE_NAME).get_keys(),
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
Self::Linux => LinuxKeyStorage::new().get_keys(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_key(&self, key: &Keypair) -> Result<(), KeyStorageError> {
|
fn add_key(&self, key: &Keypair) -> KeyStorageResponse<()> {
|
||||||
let _ = key;
|
let _ = key;
|
||||||
match self {
|
match self {
|
||||||
Self::None => Ok(()),
|
Self::None => KeyStorageResponse::ReceivedResult(Ok(())),
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
Self::MacOS => MacOSKeyStorage::new(SERVICE_NAME).add_key(key),
|
Self::MacOS => MacOSKeyStorage::new(SERVICE_NAME).add_key(key),
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
Self::Linux => LinuxKeyStorage::new().add_key(key),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_key(&self, key: &Keypair) -> Result<(), KeyStorageError> {
|
fn remove_key(&self, key: &Keypair) -> KeyStorageResponse<()> {
|
||||||
let _ = key;
|
let _ = key;
|
||||||
match self {
|
match self {
|
||||||
Self::None => Ok(()),
|
Self::None => KeyStorageResponse::ReceivedResult(Ok(())),
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
Self::MacOS => MacOSKeyStorage::new(SERVICE_NAME).delete_key(&key.pubkey),
|
Self::MacOS => MacOSKeyStorage::new(SERVICE_NAME).remove_key(key),
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
Self::Linux => LinuxKeyStorage::new().remove_key(key),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum KeyStorageError {
|
pub enum KeyStorageError {
|
||||||
Retrieval,
|
Retrieval(String),
|
||||||
Addition(String),
|
Addition(String),
|
||||||
Removal(String),
|
Removal(String),
|
||||||
UnsupportedPlatform,
|
OSError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for KeyStorageError {
|
impl std::fmt::Display for KeyStorageError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Retrieval => write!(f, "Failed to retrieve keys."),
|
Self::Retrieval(e) => write!(f, "Failed to retrieve keys: {:?}", e),
|
||||||
Self::Addition(key) => write!(f, "Failed to add key: {:?}", key),
|
Self::Addition(key) => write!(f, "Failed to add key: {:?}", key),
|
||||||
Self::Removal(key) => write!(f, "Failed to remove key: {:?}", key),
|
Self::Removal(key) => write!(f, "Failed to remove key: {:?}", key),
|
||||||
Self::UnsupportedPlatform => write!(
|
Self::OSError(e) => write!(f, "OS had an error: {:?}", e),
|
||||||
f,
|
|
||||||
"Attempted to use a key storage impl from an unsupported platform."
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ mod user_account;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod test_utils;
|
mod test_utils;
|
||||||
|
mod linux_key_storage;
|
||||||
|
|
||||||
pub use app::Damus;
|
pub use app::Damus;
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
|
|||||||
209
src/linux_key_storage.rs
Normal file
209
src/linux_key_storage.rs
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
#![cfg(target_os = "linux")]
|
||||||
|
|
||||||
|
use enostr::{Keypair, SerializableKeypair};
|
||||||
|
use std::fs;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::{env, fs::File};
|
||||||
|
|
||||||
|
use crate::key_storage::{KeyStorage, KeyStorageError, KeyStorageResponse};
|
||||||
|
|
||||||
|
enum LinuxKeyStorageType {
|
||||||
|
BasicFileStorage,
|
||||||
|
// TODO(kernelkind): could use the secret service api, and maybe even allow password manager integration via a settings menu
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LinuxKeyStorage {}
|
||||||
|
|
||||||
|
// TODO(kernelkind): read from settings instead of hard-coding
|
||||||
|
static USE_MECHANISM: LinuxKeyStorageType = LinuxKeyStorageType::BasicFileStorage;
|
||||||
|
|
||||||
|
impl LinuxKeyStorage {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyStorage for LinuxKeyStorage {
|
||||||
|
fn get_keys(&self) -> KeyStorageResponse<Vec<enostr::Keypair>> {
|
||||||
|
match USE_MECHANISM {
|
||||||
|
LinuxKeyStorageType::BasicFileStorage => BasicFileStorage::new().get_keys(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_key(&self, key: &enostr::Keypair) -> KeyStorageResponse<()> {
|
||||||
|
match USE_MECHANISM {
|
||||||
|
LinuxKeyStorageType::BasicFileStorage => BasicFileStorage::new().add_key(key),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_key(&self, key: &enostr::Keypair) -> KeyStorageResponse<()> {
|
||||||
|
match USE_MECHANISM {
|
||||||
|
LinuxKeyStorageType::BasicFileStorage => BasicFileStorage::new().remove_key(key),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BasicFileStorage {
|
||||||
|
credential_dir_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BasicFileStorage {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
credential_dir_name: ".credentials".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mock() -> Self {
|
||||||
|
Self {
|
||||||
|
credential_dir_name: ".credentials_test".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_cred_dirpath(&self) -> Result<PathBuf, KeyStorageError> {
|
||||||
|
let home_dir = env::var("HOME")
|
||||||
|
.map_err(|_| KeyStorageError::OSError("HOME env variable not set".to_string()))?;
|
||||||
|
let home_path = std::path::PathBuf::from(home_dir);
|
||||||
|
let project_path_str = "notedeck";
|
||||||
|
|
||||||
|
let config_path = {
|
||||||
|
if let Some(xdg_config_str) = env::var_os("XDG_CONFIG_HOME") {
|
||||||
|
let xdg_path = PathBuf::from(xdg_config_str);
|
||||||
|
let xdg_path_config = if xdg_path.is_absolute() {
|
||||||
|
xdg_path
|
||||||
|
} else {
|
||||||
|
home_path.join(".config")
|
||||||
|
};
|
||||||
|
xdg_path_config.join(project_path_str)
|
||||||
|
} else {
|
||||||
|
home_path.join(format!(".{}", project_path_str))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.join(self.credential_dir_name.clone());
|
||||||
|
|
||||||
|
std::fs::create_dir_all(&config_path).map_err(|_| {
|
||||||
|
KeyStorageError::OSError(format!(
|
||||||
|
"could not create config path: {}",
|
||||||
|
config_path.display()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(config_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_key_internal(&self, key: &Keypair) -> Result<(), KeyStorageError> {
|
||||||
|
let mut file_path = self.get_cred_dirpath()?;
|
||||||
|
file_path.push(format!("{}", &key.pubkey));
|
||||||
|
|
||||||
|
let mut file = File::create(file_path)
|
||||||
|
.map_err(|_| KeyStorageError::Addition("could not create or open file".to_string()))?;
|
||||||
|
|
||||||
|
let json_str = serde_json::to_string(&SerializableKeypair::from_keypair(key, "", 7))
|
||||||
|
.map_err(|e| KeyStorageError::Addition(e.to_string()))?;
|
||||||
|
file.write_all(json_str.as_bytes()).map_err(|_| {
|
||||||
|
KeyStorageError::Addition("could not write keypair to file".to_string())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_keys_internal(&self) -> Result<Vec<Keypair>, KeyStorageError> {
|
||||||
|
let file_path = self.get_cred_dirpath()?;
|
||||||
|
let mut keys: Vec<Keypair> = Vec::new();
|
||||||
|
|
||||||
|
if !file_path.is_dir() {
|
||||||
|
return Err(KeyStorageError::Retrieval(
|
||||||
|
"path is not a directory".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let dir = fs::read_dir(file_path).map_err(|_| {
|
||||||
|
KeyStorageError::Retrieval("problem accessing credentials directory".to_string())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
for entry in dir {
|
||||||
|
let entry = entry.map_err(|_| {
|
||||||
|
KeyStorageError::Retrieval("problem accessing crediential file".to_string())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let path = entry.path();
|
||||||
|
|
||||||
|
if path.is_file() {
|
||||||
|
if let Some(path_str) = path.to_str() {
|
||||||
|
println!("{}", path_str);
|
||||||
|
let json_string = fs::read_to_string(path_str).map_err(|e| {
|
||||||
|
KeyStorageError::OSError(format!("File reading problem: {}", e))
|
||||||
|
})?;
|
||||||
|
let key: SerializableKeypair =
|
||||||
|
serde_json::from_str(&json_string).map_err(|e| {
|
||||||
|
KeyStorageError::OSError(format!(
|
||||||
|
"Deserialization problem: {}",
|
||||||
|
(e.to_string().as_str())
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
keys.push(key.to_keypair(""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_key_internal(&self, key: &Keypair) -> Result<(), KeyStorageError> {
|
||||||
|
let path = self.get_cred_dirpath()?;
|
||||||
|
|
||||||
|
let filepath = path.join(key.pubkey.to_string());
|
||||||
|
|
||||||
|
if filepath.exists() && filepath.is_file() {
|
||||||
|
fs::remove_file(&filepath)
|
||||||
|
.map_err(|e| KeyStorageError::OSError(format!("failed to remove file: {}", e)))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyStorage for BasicFileStorage {
|
||||||
|
fn get_keys(&self) -> crate::key_storage::KeyStorageResponse<Vec<enostr::Keypair>> {
|
||||||
|
KeyStorageResponse::ReceivedResult(self.get_keys_internal())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_key(&self, key: &enostr::Keypair) -> crate::key_storage::KeyStorageResponse<()> {
|
||||||
|
KeyStorageResponse::ReceivedResult(self.add_key_internal(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_key(&self, key: &enostr::Keypair) -> crate::key_storage::KeyStorageResponse<()> {
|
||||||
|
KeyStorageResponse::ReceivedResult(self.remove_key_internal(key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod tests {
|
||||||
|
use crate::key_storage::{KeyStorage, KeyStorageResponse};
|
||||||
|
|
||||||
|
use super::BasicFileStorage;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_basic() {
|
||||||
|
let kp = enostr::FullKeypair::generate().to_keypair();
|
||||||
|
let resp = BasicFileStorage::mock().add_key(&kp);
|
||||||
|
|
||||||
|
assert_eq!(resp, KeyStorageResponse::ReceivedResult(Ok(())));
|
||||||
|
assert_num_storage(1);
|
||||||
|
|
||||||
|
let resp = BasicFileStorage::mock().remove_key(&kp);
|
||||||
|
assert_eq!(resp, KeyStorageResponse::ReceivedResult(Ok(())));
|
||||||
|
assert_num_storage(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn assert_num_storage(n: usize) {
|
||||||
|
let resp = BasicFileStorage::mock().get_keys();
|
||||||
|
|
||||||
|
if let KeyStorageResponse::ReceivedResult(Ok(vec)) = resp {
|
||||||
|
assert_eq!(vec.len(), n);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ use enostr::{Keypair, Pubkey, SecretKey};
|
|||||||
use security_framework::item::{ItemClass, ItemSearchOptions, Limit, SearchResult};
|
use security_framework::item::{ItemClass, ItemSearchOptions, Limit, SearchResult};
|
||||||
use security_framework::passwords::{delete_generic_password, set_generic_password};
|
use security_framework::passwords::{delete_generic_password, set_generic_password};
|
||||||
|
|
||||||
use crate::key_storage::KeyStorageError;
|
use crate::key_storage::{KeyStorage, KeyStorageError, KeyStorageResponse};
|
||||||
|
|
||||||
pub struct MacOSKeyStorage<'a> {
|
pub struct MacOSKeyStorage<'a> {
|
||||||
pub service_name: &'a str,
|
pub service_name: &'a str,
|
||||||
@@ -16,7 +16,7 @@ impl<'a> MacOSKeyStorage<'a> {
|
|||||||
MacOSKeyStorage { service_name }
|
MacOSKeyStorage { service_name }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_key(&self, key: &Keypair) -> Result<(), KeyStorageError> {
|
fn add_key(&self, key: &Keypair) -> Result<(), KeyStorageError> {
|
||||||
match set_generic_password(
|
match set_generic_password(
|
||||||
self.service_name,
|
self.service_name,
|
||||||
key.pubkey.hex().as_str(),
|
key.pubkey.hex().as_str(),
|
||||||
@@ -52,7 +52,7 @@ impl<'a> MacOSKeyStorage<'a> {
|
|||||||
accounts
|
accounts
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pubkeys(&self) -> Vec<Pubkey> {
|
fn get_pubkeys(&self) -> Vec<Pubkey> {
|
||||||
self.get_pubkey_strings()
|
self.get_pubkey_strings()
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.filter_map(|pubkey_str| Pubkey::from_hex(pubkey_str.as_str()).ok())
|
.filter_map(|pubkey_str| Pubkey::from_hex(pubkey_str.as_str()).ok())
|
||||||
@@ -84,7 +84,7 @@ impl<'a> MacOSKeyStorage<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all_keypairs(&self) -> Vec<Keypair> {
|
fn get_all_keypairs(&self) -> Vec<Keypair> {
|
||||||
self.get_pubkeys()
|
self.get_pubkeys()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|pubkey| {
|
.map(|pubkey| {
|
||||||
@@ -94,7 +94,7 @@ impl<'a> MacOSKeyStorage<'a> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_key(&self, pubkey: &Pubkey) -> Result<(), KeyStorageError> {
|
fn delete_key(&self, pubkey: &Pubkey) -> Result<(), KeyStorageError> {
|
||||||
match delete_generic_password(self.service_name, pubkey.hex().as_str()) {
|
match delete_generic_password(self.service_name, pubkey.hex().as_str()) {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -105,6 +105,20 @@ impl<'a> MacOSKeyStorage<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> KeyStorage for MacOSKeyStorage<'a> {
|
||||||
|
fn add_key(&self, key: &Keypair) -> KeyStorageResponse<()> {
|
||||||
|
KeyStorageResponse::ReceivedResult(self.add_key(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_keys(&self) -> KeyStorageResponse<Vec<Keypair>> {
|
||||||
|
KeyStorageResponse::ReceivedResult(Ok(self.get_all_keypairs()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_key(&self, key: &Keypair) -> KeyStorageResponse<()> {
|
||||||
|
KeyStorageResponse::ReceivedResult(self.delete_key(&key.pubkey))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user