Files
Agent-Tools/crates/agentdock-secrets/src/keyring.rs
T

117 lines
4.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 生产密钥库后端:keyring → Windows Credential Manager(优先)/ Linux Secret Service
//! (架构 §4.1
//!
//! 错误映射时只保留中文说明,绝不把后端原始错误或密钥明文带入错误信息。
use crate::error::SecretStoreError;
use crate::store::SecretStore;
/// keyring 后端封装。
#[derive(Default)]
pub struct KeyringSecretStore;
impl KeyringSecretStore {
pub fn new() -> Self {
Self
}
}
#[cfg(windows)]
mod platform {
use keyring::Entry;
use crate::error::SecretStoreError;
pub fn set(service: &str, account: &str, secret: &str) -> Result<(), SecretStoreError> {
let entry = Entry::new(service, account).map_err(map_err)?;
entry.set_password(secret).map_err(map_err)
}
pub fn get(service: &str, account: &str) -> Result<String, SecretStoreError> {
let entry = Entry::new(service, account).map_err(map_err)?;
entry.get_password().map_err(map_err)
}
pub fn has(service: &str, account: &str) -> bool {
Entry::new(service, account)
.and_then(|e| e.get_password())
.is_ok()
}
pub fn delete(service: &str, account: &str) -> Result<(), SecretStoreError> {
let entry = Entry::new(service, account).map_err(map_err)?;
entry.delete_credential().map_err(map_err)
}
fn map_err(e: keyring::Error) -> SecretStoreError {
// NoEntry 表示无此条目;其余错误统一映射为后端失败,绝不回显原始错误文本。
if matches!(e, keyring::Error::NoEntry) {
SecretStoreError::NotFound
} else {
SecretStoreError::Backend("系统密钥库操作失败(Windows Credential Manager".into())
}
}
}
#[cfg(not(windows))]
mod platform {
use crate::error::SecretStoreError;
pub fn set(_service: &str, _account: &str, _secret: &str) -> Result<(), SecretStoreError> {
Err(SecretStoreError::Backend("Linux 密钥库后端自 Wave 2 起接入(Secret Service".into()))
}
pub fn get(_service: &str, _account: &str) -> Result<String, SecretStoreError> {
Err(SecretStoreError::Backend("Linux 密钥库后端自 Wave 2 起接入(Secret Service".into()))
}
pub fn has(_service: &str, _account: &str) -> bool {
false
}
pub fn delete(_service: &str, _account: &str) -> Result<(), SecretStoreError> {
Err(SecretStoreError::Backend("Linux 密钥库后端自 Wave 2 起接入(Secret Service".into()))
}
}
impl SecretStore for KeyringSecretStore {
fn set(&self, service: &str, account: &str, secret: &str) -> Result<(), SecretStoreError> {
platform::set(service, account, secret)
}
fn get(&self, service: &str, account: &str) -> Result<String, SecretStoreError> {
platform::get(service, account)
}
fn has(&self, service: &str, account: &str) -> bool {
platform::has(service, account)
}
fn delete(&self, service: &str, account: &str) -> Result<(), SecretStoreError> {
platform::delete(service, account)
}
}
#[cfg(test)]
mod tests {
use super::*;
/// 真机密钥库往返(Windows Credential Manager):写假 key → 读回一致 → 删除清理。
/// 运行:cargo test -p agentdock-secrets real_machine_keyring_roundtrip -- --ignored --nocapture
#[test]
#[ignore]
fn real_machine_keyring_roundtrip() {
let store = KeyringSecretStore::new();
let service = "agentdock.selftest";
let account = "api_key";
let fake = "sk-test-selftest-only";
store.set(service, account, fake).expect("写 Credential Manager 应成功");
assert!(store.has(service, account), "写后应可读到");
assert_eq!(store.get(service, account).unwrap(), fake, "读回应一致");
store.delete(service, account).expect("删除应成功");
assert!(!store.has(service, account), "删除后应不存在");
println!("真机密钥库往返通过(Windows Credential Manager,假 key 已清理)");
}
}