logiguard fork v3: full patch set on verified 8c74db0 tree
Includes prior-session patches (carry forward so the app compiles): - crates/gpui/build.rs: cross-compile manifest fix - crates/gpui/src/platform.rs: PlatformWindow::activate_with_token trait method - crates/gpui/src/window.rs: Window::activate_with_token public API - crates/gpui_linux/src/linux/wayland/window.rs: WaylandWindow::activate_with_token + activate() keyboard-serial fix Plus the focus-serial fix: - serial.rs: SerialKind::KeyboardEnter - client.rs: store wl_keyboard.enter serial; latest_serial_of() Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
22
crates/zed_credentials_provider/Cargo.toml
Normal file
22
crates/zed_credentials_provider/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "zed_credentials_provider"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
publish.workspace = true
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
path = "src/zed_credentials_provider.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
credentials_provider.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
paths.workspace = true
|
||||
release_channel.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
1
crates/zed_credentials_provider/LICENSE-GPL
Symbolic link
1
crates/zed_credentials_provider/LICENSE-GPL
Symbolic link
@@ -0,0 +1 @@
|
||||
../../LICENSE-GPL
|
||||
181
crates/zed_credentials_provider/src/zed_credentials_provider.rs
Normal file
181
crates/zed_credentials_provider/src/zed_credentials_provider.rs
Normal file
@@ -0,0 +1,181 @@
|
||||
use std::collections::HashMap;
|
||||
use std::future::Future;
|
||||
use std::path::PathBuf;
|
||||
use std::pin::Pin;
|
||||
use std::sync::{Arc, LazyLock};
|
||||
|
||||
use anyhow::Result;
|
||||
use credentials_provider::CredentialsProvider;
|
||||
use futures::FutureExt as _;
|
||||
use gpui::{App, AsyncApp, Global};
|
||||
use release_channel::ReleaseChannel;
|
||||
|
||||
/// An environment variable whose presence indicates that the system keychain
|
||||
/// should be used in development.
|
||||
///
|
||||
/// By default, running Zed in development uses the development credentials
|
||||
/// provider. Setting this environment variable allows you to interact with the
|
||||
/// system keychain (for instance, if you need to test something).
|
||||
///
|
||||
/// Only works in development. Setting this environment variable in other
|
||||
/// release channels is a no-op.
|
||||
static ZED_DEVELOPMENT_USE_KEYCHAIN: LazyLock<bool> = LazyLock::new(|| {
|
||||
std::env::var("ZED_DEVELOPMENT_USE_KEYCHAIN").is_ok_and(|value| !value.is_empty())
|
||||
});
|
||||
|
||||
pub struct ZedCredentialsProvider(pub Arc<dyn CredentialsProvider>);
|
||||
|
||||
impl Global for ZedCredentialsProvider {}
|
||||
|
||||
/// Returns the global [`CredentialsProvider`].
|
||||
pub fn init_global(cx: &mut App) {
|
||||
// The `CredentialsProvider` trait has `Send + Sync` bounds on it, so it
|
||||
// seems like this is a false positive from Clippy.
|
||||
#[allow(clippy::arc_with_non_send_sync)]
|
||||
let provider = new(cx);
|
||||
cx.set_global(ZedCredentialsProvider(provider));
|
||||
}
|
||||
|
||||
pub fn global(cx: &App) -> Arc<dyn CredentialsProvider> {
|
||||
cx.try_global::<ZedCredentialsProvider>()
|
||||
.map(|provider| provider.0.clone())
|
||||
.unwrap_or_else(|| new(cx))
|
||||
}
|
||||
|
||||
fn new(cx: &App) -> Arc<dyn CredentialsProvider> {
|
||||
let use_development_provider = match ReleaseChannel::try_global(cx) {
|
||||
Some(ReleaseChannel::Dev) => {
|
||||
// In development we default to using the development
|
||||
// credentials provider to avoid getting spammed by relentless
|
||||
// keychain access prompts.
|
||||
//
|
||||
// However, if the `ZED_DEVELOPMENT_USE_KEYCHAIN` environment
|
||||
// variable is set, we will use the actual keychain.
|
||||
!*ZED_DEVELOPMENT_USE_KEYCHAIN
|
||||
}
|
||||
Some(ReleaseChannel::Nightly | ReleaseChannel::Preview | ReleaseChannel::Stable) | None => {
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
if use_development_provider {
|
||||
Arc::new(DevelopmentCredentialsProvider::new())
|
||||
} else {
|
||||
Arc::new(KeychainCredentialsProvider)
|
||||
}
|
||||
}
|
||||
|
||||
/// A credentials provider that stores credentials in the system keychain.
|
||||
struct KeychainCredentialsProvider;
|
||||
|
||||
impl CredentialsProvider for KeychainCredentialsProvider {
|
||||
fn read_credentials<'a>(
|
||||
&'a self,
|
||||
url: &'a str,
|
||||
cx: &'a AsyncApp,
|
||||
) -> Pin<Box<dyn Future<Output = Result<Option<(String, Vec<u8>)>>> + '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<Box<dyn Future<Output = Result<()>> + '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<Box<dyn Future<Output = Result<()>> + 'a>> {
|
||||
async move { cx.update(move |cx| cx.delete_credentials(url)).await }.boxed_local()
|
||||
}
|
||||
}
|
||||
|
||||
/// A credentials provider that stores credentials in a local file.
|
||||
///
|
||||
/// This MUST only be used in development, as this is not a secure way of storing
|
||||
/// credentials on user machines.
|
||||
///
|
||||
/// Its existence is purely to work around the annoyance of having to constantly
|
||||
/// re-allow access to the system keychain when developing Zed.
|
||||
struct DevelopmentCredentialsProvider {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl DevelopmentCredentialsProvider {
|
||||
fn new() -> Self {
|
||||
let path = paths::config_dir().join("development_credentials");
|
||||
|
||||
Self { path }
|
||||
}
|
||||
|
||||
fn load_credentials(&self) -> Result<HashMap<String, (String, Vec<u8>)>> {
|
||||
let json = std::fs::read(&self.path)?;
|
||||
let credentials: HashMap<String, (String, Vec<u8>)> = serde_json::from_slice(&json)?;
|
||||
|
||||
Ok(credentials)
|
||||
}
|
||||
|
||||
fn save_credentials(&self, credentials: &HashMap<String, (String, Vec<u8>)>) -> Result<()> {
|
||||
let json = serde_json::to_string(credentials)?;
|
||||
std::fs::write(&self.path, json)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CredentialsProvider for DevelopmentCredentialsProvider {
|
||||
fn read_credentials<'a>(
|
||||
&'a self,
|
||||
url: &'a str,
|
||||
_cx: &'a AsyncApp,
|
||||
) -> Pin<Box<dyn Future<Output = Result<Option<(String, Vec<u8>)>>> + '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<Box<dyn Future<Output = Result<()>> + '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<Box<dyn Future<Output = Result<()>> + 'a>> {
|
||||
async move {
|
||||
let mut credentials = self.load_credentials()?;
|
||||
credentials.remove(url);
|
||||
|
||||
self.save_credentials(&credentials)
|
||||
}
|
||||
.boxed_local()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user