diff --git a/Cargo.lock b/Cargo.lock index 7e641e4..265352e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1321,7 +1321,6 @@ dependencies = [ "gpui_windows", "indexset", "itertools 0.13.0", - "key_store", "log", "nostr-connect", "nostr-sdk", @@ -3509,22 +3508,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "key_store" -version = "0.3.0" -dependencies = [ - "anyhow", - "common", - "futures", - "gpui", - "log", - "nostr-sdk", - "serde", - "serde_json", - "smallvec", - "smol", -] - [[package]] name = "khronos-egl" version = "6.0.0" diff --git a/crates/coop/Cargo.toml b/crates/coop/Cargo.toml index ea93b45..ade8db1 100644 --- a/crates/coop/Cargo.toml +++ b/crates/coop/Cargo.toml @@ -34,7 +34,6 @@ theme = { path = "../theme" } common = { path = "../common" } state = { path = "../state" } device = { path = "../device" } -key_store = { path = "../key_store" } chat = { path = "../chat" } chat_ui = { path = "../chat_ui" } settings = { path = "../settings" } diff --git a/crates/coop/src/main.rs b/crates/coop/src/main.rs index 811cb73..0540bd4 100644 --- a/crates/coop/src/main.rs +++ b/crates/coop/src/main.rs @@ -79,9 +79,6 @@ fn main() { // Initialize theme registry theme::init(cx); - // Initialize backend for keys storage - key_store::init(cx); - // Initialize the nostr client state::init(window, cx); diff --git a/crates/key_store/Cargo.toml b/crates/key_store/Cargo.toml deleted file mode 100644 index 616a7f9..0000000 --- a/crates/key_store/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "key_store" -version.workspace = true -edition.workspace = true -publish.workspace = true - -[dependencies] -common = { path = "../common" } - -gpui.workspace = true -nostr-sdk.workspace = true - -anyhow.workspace = true -smallvec.workspace = true -smol.workspace = true -log.workspace = true -futures.workspace = true -serde.workspace = true -serde_json.workspace = true diff --git a/crates/key_store/src/backend.rs b/crates/key_store/src/backend.rs deleted file mode 100644 index 6e83dd7..0000000 --- a/crates/key_store/src/backend.rs +++ /dev/null @@ -1,211 +0,0 @@ -use std::any::Any; -use std::collections::HashMap; -use std::fmt::Display; -use std::future::Future; -use std::path::PathBuf; -use std::pin::Pin; - -use anyhow::Result; -use common::config_dir; -use futures::FutureExt as _; -use gpui::AsyncApp; -use nostr_sdk::prelude::*; - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Credential { - public_key: PublicKey, - secret: String, -} - -impl Credential { - pub fn new(user: String, secret: Vec) -> Self { - Self { - public_key: PublicKey::parse(&user).unwrap(), - secret: String::from_utf8(secret).unwrap(), - } - } - - pub fn public_key(&self) -> PublicKey { - self.public_key - } - - pub fn secret(&self) -> &str { - &self.secret - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum KeyItem { - User, - Bunker, -} - -impl Display for KeyItem { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::User => write!(f, "coop-user"), - Self::Bunker => write!(f, "coop-bunker"), - } - } -} - -impl From for String { - fn from(item: KeyItem) -> Self { - item.to_string() - } -} - -pub trait KeyBackend: Any + Send + Sync { - fn name(&self) -> &str; - - /// Reads the credentials from the provider. - #[allow(clippy::type_complexity)] - fn read_credentials<'a>( - &'a self, - url: &'a str, - cx: &'a AsyncApp, - ) -> Pin)>>> + 'a>>; - - /// Writes the credentials to the provider. - fn write_credentials<'a>( - &'a self, - url: &'a str, - username: &'a str, - password: &'a [u8], - cx: &'a AsyncApp, - ) -> Pin> + 'a>>; - - /// Deletes the credentials from the provider. - fn delete_credentials<'a>( - &'a self, - url: &'a str, - cx: &'a AsyncApp, - ) -> Pin> + 'a>>; -} - -/// A credentials provider that stores credentials in the system keychain. -pub struct KeyringProvider; - -impl KeyBackend for KeyringProvider { - fn name(&self) -> &str { - "keyring" - } - - fn read_credentials<'a>( - &'a self, - url: &'a str, - cx: &'a AsyncApp, - ) -> Pin)>>> + 'a>> { - async move { cx.update(|cx| cx.read_credentials(url)).await }.boxed_local() - } - - fn write_credentials<'a>( - &'a self, - url: &'a str, - username: &'a str, - password: &'a [u8], - cx: &'a AsyncApp, - ) -> Pin> + 'a>> { - async move { - cx.update(move |cx| cx.write_credentials(url, username, password)) - .await - } - .boxed_local() - } - - fn delete_credentials<'a>( - &'a self, - url: &'a str, - cx: &'a AsyncApp, - ) -> Pin> + 'a>> { - async move { cx.update(move |cx| cx.delete_credentials(url)).await }.boxed_local() - } -} - -/// A credentials provider that stores credentials in a local file. -pub struct FileProvider { - path: PathBuf, -} - -impl FileProvider { - pub fn new() -> Self { - let path = config_dir().join(".keys"); - - if let Some(parent) = path.parent() { - let _ = std::fs::create_dir_all(parent); - } - - Self { path } - } - - pub fn load_credentials(&self) -> Result)>> { - let json = std::fs::read(&self.path)?; - let credentials: HashMap)> = serde_json::from_slice(&json)?; - - Ok(credentials) - } - - pub fn save_credentials(&self, credentials: &HashMap)>) -> Result<()> { - let json = serde_json::to_string(credentials)?; - std::fs::write(&self.path, json)?; - - Ok(()) - } -} - -impl Default for FileProvider { - fn default() -> Self { - Self::new() - } -} - -impl KeyBackend for FileProvider { - fn name(&self) -> &str { - "file" - } - - fn read_credentials<'a>( - &'a self, - url: &'a str, - _cx: &'a AsyncApp, - ) -> Pin)>>> + 'a>> { - async move { - Ok(self - .load_credentials() - .unwrap_or_default() - .get(url) - .cloned()) - } - .boxed_local() - } - - fn write_credentials<'a>( - &'a self, - url: &'a str, - username: &'a str, - password: &'a [u8], - _cx: &'a AsyncApp, - ) -> Pin> + 'a>> { - async move { - let mut credentials = self.load_credentials().unwrap_or_default(); - credentials.insert(url.to_string(), (username.to_string(), password.to_vec())); - - self.save_credentials(&credentials) - } - .boxed_local() - } - - fn delete_credentials<'a>( - &'a self, - url: &'a str, - _cx: &'a AsyncApp, - ) -> Pin> + 'a>> { - async move { - let mut credentials = self.load_credentials()?; - credentials.remove(url); - - self.save_credentials(&credentials) - } - .boxed_local() - } -} diff --git a/crates/key_store/src/lib.rs b/crates/key_store/src/lib.rs deleted file mode 100644 index 4a096a6..0000000 --- a/crates/key_store/src/lib.rs +++ /dev/null @@ -1,94 +0,0 @@ -use std::sync::{Arc, LazyLock}; - -pub use backend::*; -use gpui::{App, AppContext, Context, Entity, Global, Task}; -use smallvec::{smallvec, SmallVec}; - -mod backend; - -static DISABLE_KEYRING: LazyLock = - LazyLock::new(|| std::env::var("DISABLE_KEYRING").is_ok_and(|value| !value.is_empty())); - -pub fn init(cx: &mut App) { - KeyStore::set_global(cx.new(KeyStore::new), cx); -} - -struct GlobalKeyStore(Entity); - -impl Global for GlobalKeyStore {} - -pub struct KeyStore { - /// Key Store for storing credentials - pub backend: Arc, - - /// Whether the keystore has been initialized - pub initialized: bool, - - /// Tasks for asynchronous operations - _tasks: SmallVec<[Task<()>; 1]>, -} - -impl KeyStore { - /// Retrieve the global keys state - pub fn global(cx: &App) -> Entity { - cx.global::().0.clone() - } - - /// Set the global keys instance - pub(crate) fn set_global(state: Entity, cx: &mut App) { - cx.set_global(GlobalKeyStore(state)); - } - - /// Create a new keys instance - pub(crate) fn new(cx: &mut Context) -> Self { - // Use the file system for keystore in development or when the user specifies it - let use_file_keystore = cfg!(debug_assertions) || *DISABLE_KEYRING; - - // Construct the key backend - let backend: Arc = if use_file_keystore { - Arc::new(FileProvider::default()) - } else { - Arc::new(KeyringProvider) - }; - - // Only used for testing keyring availability on the user's system - let read_credential = cx.read_credentials("Coop"); - let mut tasks = smallvec![]; - - tasks.push( - // Verify the keyring availability - cx.spawn(async move |this, cx| { - let result = read_credential.await; - - this.update(cx, |this, cx| { - if let Err(e) = result { - log::error!("Keyring error: {e}"); - // For Linux: - // The user has not installed secret service on their system - // Fall back to the file provider - this.backend = Arc::new(FileProvider::default()); - } - this.initialized = true; - cx.notify(); - }) - .ok(); - }), - ); - - Self { - backend, - initialized: false, - _tasks: tasks, - } - } - - /// Returns the key backend. - pub fn backend(&self) -> Arc { - Arc::clone(&self.backend) - } - - /// Returns true if the keystore is a file key backend. - pub fn is_using_file_keystore(&self) -> bool { - self.backend.name() == "file" - } -}