#[cfg(feature = "secure-storage")]
use anyhow::anyhow;
use anyhow::Result;
#[cfg(feature = "secure-storage")]
use keyring::Entry;
#[allow(dead_code)]
const SERVICE_NAME: &str = "rustycommit";
pub fn store_secret(_key: &str, _value: &str) -> Result<()> {
if is_disabled_via_env() {
return Ok(());
}
#[cfg(feature = "secure-storage")]
{
match Entry::new(SERVICE_NAME, _key) {
Ok(entry) => {
entry
.set_password(_value)
.map_err(|e| anyhow!("Failed to store secret in secure storage: {e}"))?;
}
Err(e) => {
return Err(anyhow!(
"Secure storage not available on this platform: {e}"
));
}
}
}
Ok(())
}
pub fn get_secret(_key: &str) -> Result<Option<String>> {
if is_disabled_via_env() {
return Ok(None);
}
#[cfg(feature = "secure-storage")]
{
match Entry::new(SERVICE_NAME, _key) {
Ok(entry) => match entry.get_password() {
Ok(password) => Ok(Some(password)),
Err(keyring::Error::NoEntry) => Ok(None),
Err(_) => Ok(None),
},
Err(_) => {
Ok(None)
}
}
}
#[cfg(not(feature = "secure-storage"))]
{
Ok(None)
}
}
pub fn delete_secret(_key: &str) -> Result<()> {
if is_disabled_via_env() {
return Ok(());
}
#[cfg(feature = "secure-storage")]
{
match Entry::new(SERVICE_NAME, _key) {
Ok(entry) => {
let _ = entry.delete_credential();
}
Err(_) => {
}
}
}
Ok(())
}
pub fn is_available() -> bool {
if is_disabled_via_env() {
return false;
}
#[cfg(feature = "secure-storage")]
{
match Entry::new(SERVICE_NAME, "test") {
Ok(entry) => {
matches!(entry.get_password(), Err(keyring::Error::NoEntry) | Ok(_))
}
Err(_) => false,
}
}
#[cfg(not(feature = "secure-storage"))]
{
false
}
}
fn is_disabled_via_env() -> bool {
std::env::var("RCO_DISABLE_SECURE_STORAGE")
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false)
}
pub fn get_platform_info() -> String {
#[cfg(all(feature = "secure-storage", target_os = "macos"))]
return "macOS Keychain".to_string();
#[cfg(all(feature = "secure-storage", target_os = "linux"))]
{
if std::env::var("GNOME_KEYRING_CONTROL").is_ok() {
"GNOME Keyring".to_string()
} else if std::env::var("KDE_FULL_SESSION").is_ok() {
"KWallet".to_string()
} else {
"Linux Secret Service".to_string()
}
}
#[cfg(all(feature = "secure-storage", target_os = "windows"))]
return "Windows Credential Manager".to_string();
#[cfg(all(feature = "secure-storage", target_os = "ios"))]
return "iOS Keychain".to_string();
#[cfg(all(feature = "secure-storage", target_os = "freebsd"))]
return "FreeBSD Secret Service".to_string();
#[cfg(all(feature = "secure-storage", target_os = "openbsd"))]
return "OpenBSD Secret Service".to_string();
#[cfg(not(feature = "secure-storage"))]
return "Not compiled with secure storage support".to_string();
#[cfg(all(
feature = "secure-storage",
not(any(
target_os = "macos",
target_os = "linux",
target_os = "windows",
target_os = "ios",
target_os = "freebsd",
target_os = "openbsd"
))
))]
return "Unknown platform".to_string();
}
pub fn status_message() -> String {
#[cfg(feature = "secure-storage")]
{
if is_available() {
format!("Secure storage is available via {}", get_platform_info())
} else {
format!(
"Secure storage feature is enabled but {} is not available",
get_platform_info()
)
}
}
#[cfg(not(feature = "secure-storage"))]
{
"Secure storage is not enabled (compile with --features secure-storage to enable)"
.to_string()
}
}