use keyring::{Entry, Error};
use thiserror::Error;
use tracing::warn;
const SERVICE_NAME: &str = "pawan";
const USER: &str = "api_keys";
#[derive(Error, Debug)]
pub enum CredentialError {
#[error("Failed to access credential store: {0}")]
StoreError(String),
#[error("Credential not found")]
NotFound,
#[error("Invalid credential data")]
InvalidData,
}
impl From<Error> for CredentialError {
fn from(err: Error) -> Self {
match err {
Error::PlatformFailure(e) => CredentialError::StoreError(format!("Platform error: {}", e)),
Error::NoEntry => CredentialError::NotFound,
_ => CredentialError::StoreError(format!("Credential store error: {}", err)),
}
}
}
pub fn store_api_key(key_name: &str, api_key: &str) -> Result<(), CredentialError> {
let entry = Entry::new(SERVICE_NAME, &format!("{}_{}", USER, key_name))?;
entry.set_password(api_key)?;
warn!("API key '{}' stored securely", key_name);
Ok(())
}
pub fn get_api_key(key_name: &str) -> Result<Option<String>, CredentialError> {
let entry = Entry::new(SERVICE_NAME, &format!("{}_{}", USER, key_name))?;
match entry.get_password() {
Ok(key) => Ok(Some(key)),
Err(Error::NoEntry) => Ok(None),
Err(e) => Err(e.into()),
}
}
pub fn delete_api_key(key_name: &str) -> Result<(), CredentialError> {
let entry = Entry::new(SERVICE_NAME, &format!("{}_{}", USER, key_name))?;
match entry.delete_credential() {
Ok(()) => {
warn!("API key '{}' deleted from secure store", key_name);
Ok(())
}
Err(Error::NoEntry) => Ok(()),
Err(e) => Err(e.into()),
}
}
pub fn is_secure_store_available() -> bool {
let test_entry = Entry::new(SERVICE_NAME, "test_check");
test_entry.is_ok()
}
pub fn store_nvidia_api_key(key: &str) -> Result<(), CredentialError> {
store_api_key("nvidia_api_key", key)
}
pub fn get_nvidia_api_key() -> Result<Option<String>, CredentialError> {
get_api_key("nvidia_api_key")
}
pub fn delete_nvidia_api_key() -> Result<(), CredentialError> {
delete_api_key("nvidia_api_key")
}
pub fn store_openai_api_key(key: &str) -> Result<(), CredentialError> {
store_api_key("openai_api_key", key)
}
pub fn get_openai_api_key() -> Result<Option<String>, CredentialError> {
get_api_key("openai_api_key")
}
pub fn delete_openai_api_key() -> Result<(), CredentialError> {
delete_api_key("openai_api_key")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore] fn test_store_and_get_key() {
let key_name = "test_key_12345";
let test_key = "test_api_key_value";
store_api_key(key_name, test_key).expect("Failed to store key");
let retrieved = get_api_key(key_name).expect("Failed to retrieve key");
assert_eq!(retrieved, Some(test_key.to_string()));
delete_api_key(key_name).expect("Failed to delete key");
}
#[test]
#[ignore] fn test_get_nonexistent_key() {
let key_name = "nonexistent_key_12345";
let _ = delete_api_key(key_name);
let retrieved = get_api_key(key_name).expect("Failed to retrieve key");
assert_eq!(retrieved, None);
}
#[test]
#[ignore] fn test_delete_key() {
let key_name = "test_delete_key_12345";
let test_key = "test_key_value";
store_api_key(key_name, test_key).expect("Failed to store");
assert!(get_api_key(key_name).expect("Failed to get") == Some(test_key.to_string()));
delete_api_key(key_name).expect("Failed to delete");
assert_eq!(get_api_key(key_name).expect("Failed to get"), None);
}
}