vtcode_auth/credentials/
mod.rs1mod encryption;
14pub(crate) mod keyring;
15mod legacy;
16mod mode;
17mod storage;
18
19pub use mode::AuthCredentialsStoreMode;
20pub use storage::CredentialStorage;
21
22use std::collections::BTreeMap;
23
24use anyhow::Result;
25
26pub struct CustomApiKeyStorage {
31 provider: String,
32 storage: CredentialStorage,
33}
34
35impl CustomApiKeyStorage {
36 pub fn new(provider: &str) -> Self {
38 let normalized_provider = provider.to_lowercase();
39 Self {
40 provider: normalized_provider.clone(),
41 storage: CredentialStorage::new("vtcode", format!("api_key_{normalized_provider}")),
42 }
43 }
44
45 pub fn store(&self, api_key: &str, mode: AuthCredentialsStoreMode) -> Result<()> {
47 self.storage.store_with_mode(api_key, mode)?;
48 let _ = legacy::clear_for_provider(&self.provider);
49 Ok(())
50 }
51
52 pub fn load(&self, mode: AuthCredentialsStoreMode) -> Result<Option<String>> {
54 if let Some(key) = self.storage.load_with_mode(mode)? {
55 return Ok(Some(key));
56 }
57
58 self.load_legacy_auth_json(mode)
59 }
60
61 pub fn clear(&self, mode: AuthCredentialsStoreMode) -> Result<()> {
63 self.storage.clear_with_mode(mode)?;
64 let _ = legacy::clear_for_provider(&self.provider);
65 Ok(())
66 }
67
68 fn load_legacy_auth_json(&self, mode: AuthCredentialsStoreMode) -> Result<Option<String>> {
69 let Some(legacy_entry) = legacy::load_for_provider(&self.provider)? else {
70 return Ok(None);
71 };
72
73 if let Err(err) = self.storage.store_with_mode(&legacy_entry.api_key, mode) {
74 tracing::warn!(
75 "Failed to migrate legacy plaintext auth.json entry for provider '{}' into secure storage: {}",
76 self.provider,
77 err
78 );
79 return Ok(Some(legacy_entry.api_key));
80 }
81
82 let path = crate::storage_paths::legacy_auth_storage_path().ok();
83 if let Some(p) = path {
84 let _ = legacy::delete_file(&p);
85 }
86
87 tracing::warn!(
88 "Migrated legacy plaintext auth.json entry for provider '{}' into secure storage",
89 self.provider
90 );
91 Ok(Some(legacy_entry.api_key))
92 }
93}
94
95pub fn migrate_custom_api_keys(
99 custom_api_keys: &BTreeMap<String, String>,
100 mode: AuthCredentialsStoreMode,
101) -> Result<BTreeMap<String, bool>> {
102 let mut results = BTreeMap::new();
103
104 for (provider, api_key) in custom_api_keys {
105 let storage = CustomApiKeyStorage::new(provider);
106 match storage.store(api_key, mode) {
107 Ok(()) => {
108 tracing::info!("Migrated API key for provider '{provider}' to secure storage");
109 results.insert(provider.clone(), true);
110 }
111 Err(e) => {
112 tracing::warn!("Failed to migrate API key for provider '{provider}': {e}");
113 results.insert(provider.clone(), false);
114 }
115 }
116 }
117
118 Ok(results)
119}
120
121pub fn load_custom_api_keys(providers: &[String], mode: AuthCredentialsStoreMode) -> Result<BTreeMap<String, String>> {
125 let mut api_keys = BTreeMap::new();
126
127 for provider in providers {
128 let storage = CustomApiKeyStorage::new(provider);
129 if let Some(key) = storage.load(mode)? {
130 api_keys.insert(provider.clone(), key);
131 }
132 }
133
134 Ok(api_keys)
135}
136
137pub fn clear_custom_api_keys(providers: &[String], mode: AuthCredentialsStoreMode) -> Result<()> {
139 for provider in providers {
140 let storage = CustomApiKeyStorage::new(provider);
141 if let Err(e) = storage.clear(mode) {
142 tracing::warn!("Failed to clear API key for provider '{provider}': {e}");
143 }
144 }
145 Ok(())
146}