rusty_commit/config/
secure_storage.rs1#[cfg(feature = "secure-storage")]
14use anyhow::anyhow;
15use anyhow::Result;
16
17#[cfg(feature = "secure-storage")]
18use keyring::Entry;
19
20#[allow(dead_code)]
21const SERVICE_NAME: &str = "rustycommit";
22
23pub fn store_secret(_key: &str, _value: &str) -> Result<()> {
33 if is_disabled_via_env() {
35 return Ok(());
36 }
37 #[cfg(feature = "secure-storage")]
38 {
39 match Entry::new(SERVICE_NAME, _key) {
40 Ok(entry) => {
41 entry
43 .set_password(_value)
44 .map_err(|e| anyhow!("Failed to store secret in secure storage: {e}"))?;
45 }
46 Err(e) => {
47 return Err(anyhow!(
49 "Secure storage not available on this platform: {e}"
50 ));
51 }
52 }
53 }
54 Ok(())
55}
56
57pub fn get_secret(_key: &str) -> Result<Option<String>> {
61 if is_disabled_via_env() {
63 return Ok(None);
64 }
65 #[cfg(feature = "secure-storage")]
66 {
67 match Entry::new(SERVICE_NAME, _key) {
68 Ok(entry) => match entry.get_password() {
69 Ok(password) => Ok(Some(password)),
70 Err(keyring::Error::NoEntry) => Ok(None),
71 Err(_) => Ok(None),
73 },
74 Err(_) => {
75 Ok(None)
77 }
78 }
79 }
80
81 #[cfg(not(feature = "secure-storage"))]
82 {
83 Ok(None)
84 }
85}
86
87pub fn delete_secret(_key: &str) -> Result<()> {
91 if is_disabled_via_env() {
93 return Ok(());
94 }
95 #[cfg(feature = "secure-storage")]
96 {
97 match Entry::new(SERVICE_NAME, _key) {
98 Ok(entry) => {
99 let _ = entry.delete_credential();
102 }
103 Err(_) => {
104 }
106 }
107 }
108
109 Ok(())
110}
111
112pub fn is_available() -> bool {
117 if is_disabled_via_env() {
119 return false;
120 }
121
122 #[cfg(feature = "secure-storage")]
123 {
124 match Entry::new(SERVICE_NAME, "test") {
126 Ok(entry) => {
127 matches!(entry.get_password(), Err(keyring::Error::NoEntry) | Ok(_))
129 }
130 Err(_) => false,
131 }
132 }
133
134 #[cfg(not(feature = "secure-storage"))]
135 {
136 false
137 }
138}
139
140fn is_disabled_via_env() -> bool {
142 std::env::var("RCO_DISABLE_SECURE_STORAGE")
143 .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
144 .unwrap_or(false)
145}
146
147pub fn get_platform_info() -> String {
149 #[cfg(all(feature = "secure-storage", target_os = "macos"))]
150 return "macOS Keychain".to_string();
151
152 #[cfg(all(feature = "secure-storage", target_os = "linux"))]
153 {
154 if std::env::var("GNOME_KEYRING_CONTROL").is_ok() {
156 "GNOME Keyring".to_string()
157 } else if std::env::var("KDE_FULL_SESSION").is_ok() {
158 "KWallet".to_string()
159 } else {
160 "Linux Secret Service".to_string()
161 }
162 }
163
164 #[cfg(all(feature = "secure-storage", target_os = "windows"))]
165 return "Windows Credential Manager".to_string();
166
167 #[cfg(all(feature = "secure-storage", target_os = "ios"))]
168 return "iOS Keychain".to_string();
169
170 #[cfg(all(feature = "secure-storage", target_os = "freebsd"))]
171 return "FreeBSD Secret Service".to_string();
172
173 #[cfg(all(feature = "secure-storage", target_os = "openbsd"))]
174 return "OpenBSD Secret Service".to_string();
175
176 #[cfg(not(feature = "secure-storage"))]
177 return "Not compiled with secure storage support".to_string();
178
179 #[cfg(all(
181 feature = "secure-storage",
182 not(any(
183 target_os = "macos",
184 target_os = "linux",
185 target_os = "windows",
186 target_os = "ios",
187 target_os = "freebsd",
188 target_os = "openbsd"
189 ))
190 ))]
191 return "Unknown platform".to_string();
192}
193
194pub fn status_message() -> String {
196 #[cfg(feature = "secure-storage")]
197 {
198 if is_available() {
199 format!("Secure storage is available via {}", get_platform_info())
200 } else {
201 format!(
202 "Secure storage feature is enabled but {} is not available",
203 get_platform_info()
204 )
205 }
206 }
207
208 #[cfg(not(feature = "secure-storage"))]
209 {
210 "Secure storage is not enabled (compile with --features secure-storage to enable)"
211 .to_string()
212 }
213}