mongodb_atlas_cli/secrets/
keyring.rs1use keyring::Entry;
2
3use super::{ApiKeys, Secret, SecretStore, SecretStoreError, ServiceAccount, UserAccount};
4use crate::{
5 config::AuthType,
6 secrets::encoding::{decode_password, encode_password},
7};
8
9const KEY_USER_ACCOUNT_ACCESS_TOKEN: &str = "access_token";
10const KEY_USER_ACCOUNT_REFRESH_TOKEN: &str = "refresh_token";
11const KEY_API_KEYS_PUBLIC_API_KEY: &str = "public_api_key";
12const KEY_API_KEYS_PRIVATE_API_KEY: &str = "private_api_key";
13const KEY_SERVICE_ACCOUNT_CLIENT_ID: &str = "client_id";
14const KEY_SERVICE_ACCOUNT_CLIENT_SECRET: &str = "client_secret";
15
16pub struct KeyringSecretStore {}
17
18impl KeyringSecretStore {
19 pub fn new() -> Option<Self> {
20 if keyring_entry("default", "dummy").is_ok() {
22 Some(Self {})
23 } else {
24 None
25 }
26 }
27}
28
29fn build_service_name(profile_name: &str) -> String {
30 format!("atlascli_{}", profile_name)
31}
32
33fn keyring_entry(profile_name: &str, property_name: &str) -> Result<Entry, SecretStoreError> {
34 Entry::new(&build_service_name(profile_name), property_name).map_err(|e| {
35 SecretStoreError::InvalidKeyStoreFormat {
36 reason: e.to_string(),
37 }
38 })
39}
40
41fn get_keyring_value(
42 profile_name: &str,
43 property_name: &str,
44) -> Result<Option<String>, SecretStoreError> {
45 let entry = keyring_entry(profile_name, property_name)?;
46 match entry.get_password() {
47 Ok(value) => Ok(decode_password(value)?),
48 Err(e) => match e {
49 keyring::Error::NoEntry => Ok(None),
50 e => Err(SecretStoreError::InvalidKeyStoreFormat {
51 reason: e.to_string(),
52 }),
53 },
54 }
55}
56
57fn try_delete_entry(profile_name: &str, property_name: &str) {
58 if let Ok(entry) = keyring_entry(profile_name, property_name) {
59 _ = entry.delete_credential();
60 }
61}
62
63fn get_base_secret<S: Into<Secret>>(
64 profile_name: &str,
65 property_1: &str,
66 property_2: &str,
67 constructor: impl FnOnce(String, String) -> S,
68) -> Result<Option<Secret>, SecretStoreError> {
69 let Some(value_1) = get_keyring_value(profile_name, property_1)? else {
70 return Ok(None);
71 };
72 let Some(value_2) = get_keyring_value(profile_name, property_2)? else {
73 return Ok(None);
74 };
75 Ok(Some(constructor(value_1, value_2).into()))
76}
77
78fn set_keyring_value(
79 profile_name: &str,
80 property_name: &str,
81 value: &str,
82) -> Result<(), SecretStoreError> {
83 let entry = keyring_entry(profile_name, property_name)?;
84 entry
85 .set_password(encode_password(value).as_ref())
86 .map_err(|e| SecretStoreError::KeyStoreUnavailable {
87 reason: e.to_string(),
88 })
89}
90
91impl SecretStore for KeyringSecretStore {
92 fn get(
93 &self,
94 profile_name: &str,
95 auth_type: AuthType,
96 ) -> Result<Option<Secret>, SecretStoreError> {
97 Ok(match auth_type {
98 AuthType::UserAccount => get_base_secret(
99 profile_name,
100 KEY_USER_ACCOUNT_ACCESS_TOKEN,
101 KEY_USER_ACCOUNT_REFRESH_TOKEN,
102 UserAccount::new,
103 )?,
104 AuthType::ApiKeys => get_base_secret(
105 profile_name,
106 KEY_API_KEYS_PUBLIC_API_KEY,
107 KEY_API_KEYS_PRIVATE_API_KEY,
108 ApiKeys::new,
109 )?,
110 AuthType::ServiceAccount => get_base_secret(
111 profile_name,
112 KEY_SERVICE_ACCOUNT_CLIENT_ID,
113 KEY_SERVICE_ACCOUNT_CLIENT_SECRET,
114 ServiceAccount::new,
115 )?,
116 })
117 }
118
119 fn set(&mut self, profile_name: &str, secret: Secret) -> Result<(), SecretStoreError> {
120 match secret {
121 Secret::ApiKeys(api_keys) => {
122 set_keyring_value(
123 profile_name,
124 KEY_API_KEYS_PUBLIC_API_KEY,
125 &api_keys.public_api_key,
126 )?;
127 set_keyring_value(
128 profile_name,
129 KEY_API_KEYS_PRIVATE_API_KEY,
130 &api_keys.private_api_key,
131 )?;
132 Ok(())
133 }
134 Secret::ServiceAccount(service_account) => {
135 set_keyring_value(
136 profile_name,
137 KEY_SERVICE_ACCOUNT_CLIENT_ID,
138 &service_account.client_id,
139 )?;
140 set_keyring_value(
141 profile_name,
142 KEY_SERVICE_ACCOUNT_CLIENT_SECRET,
143 &service_account.client_secret,
144 )?;
145 Ok(())
146 }
147 Secret::UserAccount(user_account) => {
148 set_keyring_value(
149 profile_name,
150 KEY_USER_ACCOUNT_ACCESS_TOKEN,
151 &user_account.access_token,
152 )?;
153 set_keyring_value(
154 profile_name,
155 KEY_USER_ACCOUNT_REFRESH_TOKEN,
156 &user_account.refresh_token,
157 )?;
158 Ok(())
159 }
160 }
161 }
162
163 fn delete(&mut self, profile_name: &str) -> Result<(), SecretStoreError> {
164 try_delete_entry(profile_name, KEY_USER_ACCOUNT_ACCESS_TOKEN);
165 try_delete_entry(profile_name, KEY_USER_ACCOUNT_REFRESH_TOKEN);
166 try_delete_entry(profile_name, KEY_API_KEYS_PUBLIC_API_KEY);
167 try_delete_entry(profile_name, KEY_API_KEYS_PRIVATE_API_KEY);
168 try_delete_entry(profile_name, KEY_SERVICE_ACCOUNT_CLIENT_ID);
169 try_delete_entry(profile_name, KEY_SERVICE_ACCOUNT_CLIENT_SECRET);
170
171 Ok(())
172 }
173}