aether_cli/
credentials.rs1use aether_auth::{
2 EncryptedFileOAuthCredentialStorage, FakeOAuthCredentialStore, OAuthCredentialStorage, OAuthError, OsKeyringStore,
3};
4use aether_project::CredentialsStoreConfig;
5use std::sync::Arc;
6
7pub fn oauth_credential_store_from_config(
8 config: Option<CredentialsStoreConfig>,
9) -> Result<Arc<dyn OAuthCredentialStorage>, OAuthError> {
10 match config {
11 Some(CredentialsStoreConfig::Memory) => Ok(Arc::new(FakeOAuthCredentialStore::new())),
12 Some(CredentialsStoreConfig::EncryptedFile { path, password_env }) => {
13 Ok(Arc::new(EncryptedFileOAuthCredentialStorage::from_settings(path, password_env.as_deref())?))
14 }
15 Some(CredentialsStoreConfig::Keyring) | None => Ok(Arc::new(OsKeyringStore::with_platform_store())),
16 }
17}
18
19#[cfg(test)]
20mod tests {
21 use super::*;
22 use crate::settings_args::SettingsSourceArgs;
23 use std::path::Path;
24
25 fn credentials_config_from_json(json: &str) -> Option<CredentialsStoreConfig> {
26 let source = SettingsSourceArgs { settings_json: Some(json.to_string()), settings_file: None };
27 source.load_settings(Path::new(".")).unwrap().credentials_store
28 }
29
30 #[test]
31 fn memory_store_selected_from_settings() {
32 let config = credentials_config_from_json(
33 r#"{
34 "credentialsStore": { "type": "memory" },
35 "agents": [{"name":"a","description":"a","model":"anthropic:claude-sonnet-4-5","userInvocable":true}]
36 }"#,
37 );
38
39 let store = oauth_credential_store_from_config(config).unwrap();
40 assert!(!store.has_credential("anything"));
41 }
42
43 #[test]
44 fn encrypted_file_from_settings_propagates_missing_passphrase_error() {
45 let config = credentials_config_from_json(
46 r#"{
47 "credentialsStore": { "type": "encryptedFile", "passwordEnv": "AETHER_DEFINITELY_UNSET_VAR_98765" },
48 "agents": [{"name":"a","description":"a","model":"anthropic:claude-sonnet-4-5","userInvocable":true}]
49 }"#,
50 );
51
52 let result = oauth_credential_store_from_config(config);
53 assert!(result.is_err(), "missing passphrase should surface as a clean error");
54 }
55}