This commit is contained in:
1 parent
59cd8951f6
commit
b93ae852e0
10 files changed
+327
-36
No files matched your search
@@ -20,6 +20,15 @@ use security_framework::{
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use security_framework::{
|
||||
item::{ItemClass, ItemSearchOptions, Limit},
|
||||
os::macos::{
|
||||
keychain::SecKeychain,
|
||||
passwords::{SecAuthenticationType, SecProtocolType},
|
||||
},
|
||||
};
|
||||
|
||||
use keyring_core::{CredentialStore, Entry};
|
||||
|
||||
use super::{SecretLocator, SecretProtection, SecretStoreBackend, SecretStoreError};
|
||||
@@ -196,6 +205,95 @@ impl SecretStoreBackend for NativeSecretBackend {
|
||||
.delete_credential()
|
||||
.map_err(map_error)
|
||||
}
|
||||
|
||||
fn platform_https_git_credential(
|
||||
&self,
|
||||
endpoint: &url::Url,
|
||||
) -> Result<Option<(String, SecretBytes)>, SecretStoreError> {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
macos_https_git_credential(endpoint)
|
||||
}
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
{
|
||||
let _ = endpoint;
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn macos_https_git_credential(
|
||||
endpoint: &url::Url,
|
||||
) -> Result<Option<(String, SecretBytes)>, SecretStoreError> {
|
||||
let host = endpoint
|
||||
.host_str()
|
||||
.ok_or(SecretStoreError::InvalidReference)?;
|
||||
let path = endpoint.path();
|
||||
let mut search = ItemSearchOptions::new();
|
||||
search
|
||||
.class(ItemClass::internet_password())
|
||||
.load_attributes(true)
|
||||
.limit(Limit::All);
|
||||
let mut candidates = search
|
||||
.search()
|
||||
.map_err(map_security_error)?
|
||||
.into_iter()
|
||||
.filter_map(|item| item.simplify_dict())
|
||||
.filter_map(|attributes| {
|
||||
let stored_path = attributes.get("path").map_or("", String::as_str);
|
||||
if attributes.get("srvr").map(String::as_str) == Some(host)
|
||||
&& attributes.get("ptcl").map(String::as_str) == Some("htps")
|
||||
&& (stored_path.is_empty() || stored_path == path)
|
||||
{
|
||||
attributes
|
||||
.get("acct")
|
||||
.map(|account| (account.clone(), stored_path.to_owned()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
candidates.sort_unstable();
|
||||
candidates.dedup();
|
||||
let [(account, credential_path)] = candidates.as_slice() else {
|
||||
return if candidates.is_empty() {
|
||||
Err(SecretStoreError::Missing)
|
||||
} else {
|
||||
Err(SecretStoreError::Corrupted)
|
||||
};
|
||||
};
|
||||
let keychain = SecKeychain::default().map_err(map_security_error)?;
|
||||
let (password, _) = keychain
|
||||
.find_internet_password(
|
||||
host,
|
||||
None,
|
||||
account,
|
||||
credential_path,
|
||||
endpoint.port(),
|
||||
SecProtocolType::HTTPS,
|
||||
SecAuthenticationType::Default,
|
||||
)
|
||||
.or_else(|error| {
|
||||
if endpoint.port().is_some() && error.code() == -25300 {
|
||||
keychain.find_internet_password(
|
||||
host,
|
||||
None,
|
||||
account,
|
||||
credential_path,
|
||||
None,
|
||||
SecProtocolType::HTTPS,
|
||||
SecAuthenticationType::Default,
|
||||
)
|
||||
} else {
|
||||
Err(error)
|
||||
}
|
||||
})
|
||||
.map_err(map_security_error)?;
|
||||
Ok(Some((
|
||||
account.clone(),
|
||||
SecretBytes::new(password.as_ref().to_vec()),
|
||||
)))
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "ios", target_os = "macos"))]
|
||||
|
||||
Reference in new issue
Block a user