1use keyring::{Entry, Error};
10use thiserror::Error;
11use tracing::warn;
12
13const SERVICE_NAME: &str = "pawan";
14const USER: &str = "api_keys";
15
16#[derive(Error, Debug)]
18pub enum CredentialError {
19 #[error("Failed to access credential store: {0}")]
20 StoreError(String),
21
22 #[error("Credential not found")]
23 NotFound,
24
25 #[error("Invalid credential data")]
26 InvalidData,
27}
28
29impl From<Error> for CredentialError {
30 fn from(err: Error) -> Self {
31 match err {
32 Error::PlatformFailure(e) => CredentialError::StoreError(format!("Platform error: {}", e)),
33 Error::NoEntry => CredentialError::NotFound,
34 _ => CredentialError::StoreError(format!("Credential store error: {}", err)),
35 }
36 }
37}
38
39pub fn store_api_key(key_name: &str, api_key: &str) -> Result<(), CredentialError> {
49 let entry = Entry::new(SERVICE_NAME, &format!("{}_{}", USER, key_name))?;
50 entry.set_password(api_key)?;
51 warn!("API key '{}' stored securely", key_name);
52 Ok(())
53}
54
55pub fn get_api_key(key_name: &str) -> Result<Option<String>, CredentialError> {
65 let entry = Entry::new(SERVICE_NAME, &format!("{}_{}", USER, key_name))?;
66
67 match entry.get_password() {
68 Ok(key) => Ok(Some(key)),
69 Err(Error::NoEntry) => Ok(None),
70 Err(e) => Err(e.into()),
71 }
72}
73
74pub fn delete_api_key(key_name: &str) -> Result<(), CredentialError> {
83 let entry = Entry::new(SERVICE_NAME, &format!("{}_{}", USER, key_name))?;
84
85 match entry.delete_credential() {
86 Ok(()) => {
87 warn!("API key '{}' deleted from secure store", key_name);
88 Ok(())
89 }
90 Err(Error::NoEntry) => Ok(()),
91 Err(e) => Err(e.into()),
92 }
93}
94
95pub fn is_secure_store_available() -> bool {
101 let test_entry = Entry::new(SERVICE_NAME, "test_check");
102 test_entry.is_ok()
103}
104
105pub fn store_nvidia_api_key(key: &str) -> Result<(), CredentialError> {
107 store_api_key("nvidia_api_key", key)
108}
109
110pub fn get_nvidia_api_key() -> Result<Option<String>, CredentialError> {
111 get_api_key("nvidia_api_key")
112}
113
114pub fn delete_nvidia_api_key() -> Result<(), CredentialError> {
115 delete_api_key("nvidia_api_key")
116}
117
118pub fn store_openai_api_key(key: &str) -> Result<(), CredentialError> {
120 store_api_key("openai_api_key", key)
121}
122
123pub fn get_openai_api_key() -> Result<Option<String>, CredentialError> {
124 get_api_key("openai_api_key")
125}
126
127pub fn delete_openai_api_key() -> Result<(), CredentialError> {
128 delete_api_key("openai_api_key")
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 #[ignore] fn test_store_and_get_key() {
138 let key_name = "test_key_12345";
139 let test_key = "test_api_key_value";
140
141 store_api_key(key_name, test_key).expect("Failed to store key");
143
144 let retrieved = get_api_key(key_name).expect("Failed to retrieve key");
146 assert_eq!(retrieved, Some(test_key.to_string()));
147
148 delete_api_key(key_name).expect("Failed to delete key");
150 }
151
152 #[test]
153 #[ignore] fn test_get_nonexistent_key() {
155 let key_name = "nonexistent_key_12345";
156
157 let _ = delete_api_key(key_name);
159
160 let retrieved = get_api_key(key_name).expect("Failed to retrieve key");
162 assert_eq!(retrieved, None);
163 }
164
165 #[test]
166 #[ignore] fn test_delete_key() {
168 let key_name = "test_delete_key_12345";
169 let test_key = "test_key_value";
170
171 store_api_key(key_name, test_key).expect("Failed to store");
173 assert!(get_api_key(key_name).expect("Failed to get") == Some(test_key.to_string()));
174
175 delete_api_key(key_name).expect("Failed to delete");
177 assert_eq!(get_api_key(key_name).expect("Failed to get"), None);
178 }
179}