Skip to content

Commit

Permalink
Fix falling back to data.json when keyring is available (#12760)
Browse files Browse the repository at this point in the history
* fix: fallback to data.json on Linux

* fix: make keyring prompt more consistent for reads+writes, but less assertive when we delete credentials

* fix: explicitly ignore unused return value
  • Loading branch information
tangowithfoxtrot authored Feb 21, 2025
1 parent 1587f84 commit b86e587
Showing 1 changed file with 52 additions and 0 deletions.
52 changes: 52 additions & 0 deletions apps/desktop/desktop_native/core/src/password/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub async fn get_password(service: &str, account: &str) -> Result<String> {

async fn get_password_new(service: &str, account: &str) -> Result<String> {
let keyring = oo7::Keyring::new().await?;
let _ = try_prompt(&keyring).await;
let attributes = HashMap::from([("service", service), ("account", account)]);
let results = keyring.search_items(&attributes).await?;
let res = results.first();
Expand All @@ -29,6 +30,7 @@ async fn get_password_legacy(service: &str, account: &str) -> Result<String> {
let svc = dbus::Service::new().await?;
let collection = svc.default_collection().await?;
let keyring = oo7::Keyring::DBus(collection);
let _ = try_prompt(&keyring).await;
let attributes = HashMap::from([("service", service), ("account", account)]);
let results = keyring.search_items(&attributes).await?;
let res = results.first();
Expand All @@ -50,6 +52,7 @@ async fn get_password_legacy(service: &str, account: &str) -> Result<String> {

pub async fn set_password(service: &str, account: &str, password: &str) -> Result<()> {
let keyring = oo7::Keyring::new().await?;
let _ = try_prompt(&keyring).await;
let attributes = HashMap::from([("service", service), ("account", account)]);
keyring
.create_item(
Expand All @@ -62,13 +65,62 @@ pub async fn set_password(service: &str, account: &str, password: &str) -> Resul
Ok(())
}

/// Remove a credential from the OS keyring. This function will *not* automatically
/// prompt the user to unlock their keyring. If the keyring is locked when this
/// is called, it will fail silently.
pub async fn delete_password(service: &str, account: &str) -> Result<()> {
// We need to silently fail in the event that the user's keyring was
// locked while our application was in-use. Otherwise, when we
// force a de-auth because we can't access keys in secure storage,
// kwallet will notify the user that an application is "misbehaving". This
// seems to happen because we call [delete_password] many times when a forced
// de-auth occurs to clean up old keys.
if is_locked().await? {
println!("skipping deletion of old keys. OS keyring is locked.");
return Ok(());
}

let keyring = oo7::Keyring::new().await?;
let attributes = HashMap::from([("service", service), ("account", account)]);
keyring.delete(&attributes).await?;
Ok(())
}

/// Sends an OS notification prompt for the user to unlock/allow the application
/// to read and write keys.
async fn try_prompt(keyring: &oo7::Keyring) -> bool {
keyring.unlock().await.is_ok()
}

/// Keyrings on Linux cannnot be assumed to be unlocked while the user is
/// logged in to a desktop session. Therefore, before reading or writing
/// keys, you should check if the keyring is unlocked, and call
/// [try_prompt] if ignoring the lock state is not an option.
pub async fn is_locked() -> Result<bool> {
let keyring = oo7::Keyring::new().await?;

// No simple way to check keyring lock state, so we just try to list items
let items = keyring.items().await?;
if let Some(item) = items.first() {
return match item.is_locked().await {
Ok(is_locked) => {
println!("OS keyring is locked = {is_locked}");
Ok(is_locked)
}
Err(_) => {
println!("OS keyring is unlocked");
Ok(false)
}
};
}

// assume it's locked
Ok(true)
}

/// This will return true if a keyring is configured. However, on Linux, it does
/// NOT indicate if the keyring is _unlocked_. Use [is_locked] to check
/// the lock state before reading or writing keys.
pub async fn is_available() -> Result<bool> {
match oo7::Keyring::new().await {
Ok(_) => Ok(true),
Expand Down

0 comments on commit b86e587

Please sign in to comment.