Modul-Split: Fachlogik nach domain/, OS-Code nach platform/, Tauri-Verdrahtung in app.rs/commands.rs
- lib.rs nur noch Prozessrollen-Dispatch + Panic-Tracer - KWin-Script als KDE-Pendant zur GNOME-Extension (Popup ans Zeiger- Rechteck), wird von deb/rpm mit ausgeliefert - SNI-Tray auf ksni (pure Rust), appindicator-Depends entfallen
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
//! Credential-Ablage der Pools. Der ApikeyStore abstrahiert den nativen
|
||||
//! Secret-Store (macOS-Keychain / Secret Service / Credential Manager) —
|
||||
//! die Implementierungen liegen in platform/.
|
||||
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
use crate::domain::paths::Paths;
|
||||
|
||||
/// Service-Name der Einträge; Account ist die Pool-ID. Unter Linux legt die
|
||||
/// keyring-Crate die Attribute service/username an — der apiKeyHelper liest
|
||||
/// mit denselben Attributen über secret-tool.
|
||||
pub(crate) const APIKEY_SERVICE: &str = "ai-control-apikey";
|
||||
|
||||
/// Fehler-Sentinel an die UI: Store nicht verfügbar und Datei-Ablage (noch)
|
||||
/// nicht erlaubt — die UI fragt dann nach und wiederholt mit allow_file.
|
||||
pub(crate) const KEYCHAIN_UNAVAILABLE: &str = "keychain-unavailable";
|
||||
|
||||
/// Ablage der API-Keys im nativen Secret-Store. Die Key-Datei im Pool-Ordner
|
||||
/// bleibt Fallback, wenn der Store beim Schreiben nicht verfügbar ist.
|
||||
pub(crate) trait ApikeyStore {
|
||||
fn set(&self, pool: &str, key: &str) -> Result<(), String>;
|
||||
fn has(&self, pool: &str) -> Result<bool, String>;
|
||||
fn delete(&self, pool: &str) -> Result<(), String>;
|
||||
}
|
||||
|
||||
/// Keychain-Service-Name des OAuth-Pools: claude legt pro CLAUDE_CONFIG_DIR
|
||||
/// einen suffixierten Eintrag an, Suffix = erste 8 Hex-Zeichen von SHA-256
|
||||
/// über den Pool-Pfad.
|
||||
pub(crate) fn keychain_service(paths: &Paths, pool: &str) -> String {
|
||||
let hash = Sha256::digest(paths.pool_dir(pool).to_string_lossy().as_bytes());
|
||||
let hex: String = hash.iter().map(|b| format!("{b:02x}")).collect();
|
||||
format!("Claude Code-credentials-{}", &hex[..8])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Referenzwert vom echten System: privateDefault → 096c4ef9
|
||||
/// (verifiziert 2026-07-03 gegen den von claude angelegten Eintrag).
|
||||
#[test]
|
||||
fn keychain_service_suffix() {
|
||||
let p = Paths { home: PathBuf::from("/Users/marcus.hinz") };
|
||||
assert_eq!(
|
||||
keychain_service(&p, "privateDefault"),
|
||||
"Claude Code-credentials-096c4ef9"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apikey_helper_kette_referenz() {
|
||||
let dir = PathBuf::from("/pools/abc");
|
||||
let cmd = crate::platform::apikey_helper_command(&dir, "abc");
|
||||
assert!(cmd.ends_with("|| cat '/pools/abc/apikey'"));
|
||||
#[cfg(target_os = "macos")]
|
||||
assert!(cmd.starts_with(
|
||||
"security find-generic-password -w -s ai-control-apikey -a abc 2>/dev/null"
|
||||
));
|
||||
#[cfg(target_os = "linux")]
|
||||
assert!(cmd.starts_with(
|
||||
"secret-tool lookup service ai-control-apikey username abc 2>/dev/null"
|
||||
));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user