diff --git a/crates/uv-keyring/src/macos.rs b/crates/uv-keyring/src/macos.rs index 4ca853b78..85a635cb6 100644 --- a/crates/uv-keyring/src/macos.rs +++ b/crates/uv-keyring/src/macos.rs @@ -56,9 +56,17 @@ impl CredentialApi for MacCredential { /// Since there is only one credential with a given _account_ and _user_ /// in any given keychain, there is no chance of ambiguity. async fn set_password(&self, password: &str) -> Result<()> { - get_keychain(self)? - .set_generic_password(&self.service, &self.account, password.as_bytes()) - .map_err(decode_error)?; + let service = self.service.clone(); + let account = self.account.clone(); + let domain = self.domain; + let password = password.to_string(); + tokio::task::spawn_blocking(move || { + get_keychain(domain)? + .set_generic_password(&service, &account, password.as_bytes()) + .map_err(decode_error) + }) + .await + .map_err(|e| ErrorCode::PlatformFailure(Box::new(e)))??; Ok(()) } @@ -68,9 +76,17 @@ impl CredentialApi for MacCredential { /// Since there is only one credential with a given _account_ and _user_ /// in any given keychain, there is no chance of ambiguity. async fn set_secret(&self, secret: &[u8]) -> Result<()> { - get_keychain(self)? - .set_generic_password(&self.service, &self.account, secret) - .map_err(decode_error)?; + let service = self.service.clone(); + let account = self.account.clone(); + let domain = self.domain; + let secret = secret.to_vec(); + tokio::task::spawn_blocking(move || { + get_keychain(domain)? + .set_generic_password(&service, &account, &secret) + .map_err(decode_error) + }) + .await + .map_err(|e| ErrorCode::PlatformFailure(Box::new(e)))??; Ok(()) } @@ -79,10 +95,20 @@ impl CredentialApi for MacCredential { /// Returns a [`NoEntry`](ErrorCode::NoEntry) error if there is no /// credential in the store. async fn get_password(&self) -> Result { - let (password_bytes, _) = - find_generic_password(Some(&[get_keychain(self)?]), &self.service, &self.account) + let service = self.service.clone(); + let account = self.account.clone(); + let domain = self.domain; + + let password_bytes = tokio::task::spawn_blocking(move || -> Result> { + let keychain = get_keychain(domain)?; + let (password_bytes, _) = find_generic_password(Some(&[keychain]), &service, &account) .map_err(decode_error)?; - decode_password(password_bytes.to_owned()) + Ok(password_bytes.to_owned()) + }) + .await + .map_err(|e| ErrorCode::PlatformFailure(Box::new(e)))??; + + decode_password(password_bytes) } /// Look up the secret for this entry, if any. @@ -90,10 +116,20 @@ impl CredentialApi for MacCredential { /// Returns a [`NoEntry`](ErrorCode::NoEntry) error if there is no /// credential in the store. async fn get_secret(&self) -> Result> { - let (password_bytes, _) = - find_generic_password(Some(&[get_keychain(self)?]), &self.service, &self.account) + let service = self.service.clone(); + let account = self.account.clone(); + let domain = self.domain; + + let password_bytes = tokio::task::spawn_blocking(move || -> Result> { + let keychain = get_keychain(domain)?; + let (password_bytes, _) = find_generic_password(Some(&[keychain]), &service, &account) .map_err(decode_error)?; - Ok(password_bytes.to_owned()) + Ok(password_bytes.to_owned()) + }) + .await + .map_err(|e| ErrorCode::PlatformFailure(Box::new(e)))??; + + Ok(password_bytes) } /// Delete the underlying generic credential for this entry, if any. @@ -101,10 +137,19 @@ impl CredentialApi for MacCredential { /// Returns a [`NoEntry`](ErrorCode::NoEntry) error if there is no /// credential in the store. async fn delete_credential(&self) -> Result<()> { - let (_, item) = - find_generic_password(Some(&[get_keychain(self)?]), &self.service, &self.account) + let service = self.service.clone(); + let account = self.account.clone(); + let domain = self.domain; + + tokio::task::spawn_blocking(move || { + let keychain = get_keychain(domain)?; + let (_, item) = find_generic_password(Some(&[keychain]), &service, &account) .map_err(decode_error)?; - item.delete(); + item.delete(); + Ok(()) + }) + .await + .map_err(|e| ErrorCode::PlatformFailure(Box::new(e)))??; Ok(()) } @@ -127,9 +172,11 @@ impl MacCredential { /// other than the ones we use to find the generic credential. /// But at least this checks whether the underlying credential exists. pub fn get_credential(&self) -> Result { - let (_, _) = - find_generic_password(Some(&[get_keychain(self)?]), &self.service, &self.account) - .map_err(decode_error)?; + let service = self.service.clone(); + let account = self.account.clone(); + let domain = self.domain; + let (_, _) = find_generic_password(Some(&[get_keychain(domain)?]), &service, &account) + .map_err(decode_error)?; Ok(self.clone()) } @@ -209,7 +256,7 @@ impl CredentialBuilderApi for MacCredentialBuilder { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] /// The four pre-defined Mac keychains. pub enum MacKeychainDomain { User, @@ -255,8 +302,8 @@ impl std::str::FromStr for MacKeychainDomain { } } -fn get_keychain(cred: &MacCredential) -> Result { - let domain = match cred.domain { +fn get_keychain(domain: MacKeychainDomain) -> Result { + let domain = match domain { MacKeychainDomain::User => SecPreferencesDomain::User, MacKeychainDomain::System => SecPreferencesDomain::System, MacKeychainDomain::Common => SecPreferencesDomain::Common,