Skip to main content

aether_cli/
credentials.rs

1use 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}