diff --git a/Cargo.lock b/Cargo.lock index 5d6b9d1a520..e6af8408edc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -356,10 +356,11 @@ dependencies = [ [[package]] name = "cargo-credential-gnome-secret" -version = "0.3.0" +version = "0.3.1" dependencies = [ + "anyhow", "cargo-credential", - "pkg-config", + "libloading", ] [[package]] @@ -1978,6 +1979,16 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libloading" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" +dependencies = [ + "cfg-if", + "windows-sys", +] + [[package]] name = "libm" version = "0.2.7" diff --git a/Cargo.toml b/Cargo.toml index 3016804277f..9a267f9bff0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ lazy_static = "1.4.0" lazycell = "1.3.0" libc = "0.2.147" libgit2-sys = "0.15.2" +libloading = "0.8.0" memchr = "2.5.0" miow = "0.6.0" opener = "0.6.1" diff --git a/credential/cargo-credential-gnome-secret/Cargo.toml b/credential/cargo-credential-gnome-secret/Cargo.toml index 3916bfc031d..e88790c542b 100644 --- a/credential/cargo-credential-gnome-secret/Cargo.toml +++ b/credential/cargo-credential-gnome-secret/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "cargo-credential-gnome-secret" -version = "0.3.0" +version = "0.3.1" edition.workspace = true license.workspace = true repository = "https://github.com/rust-lang/cargo" description = "A Cargo credential process that stores tokens with GNOME libsecret." [dependencies] +anyhow.workspace = true cargo-credential.workspace = true +libloading.workspace = true -[build-dependencies] -pkg-config.workspace = true diff --git a/credential/cargo-credential-gnome-secret/build.rs b/credential/cargo-credential-gnome-secret/build.rs deleted file mode 100644 index 8bb86ee434d..00000000000 --- a/credential/cargo-credential-gnome-secret/build.rs +++ /dev/null @@ -1,8 +0,0 @@ -fn main() { - if cfg!(target_os = "linux") { - // TODO: Consider ignoring errors when libsecret is not installed and - // switching the impl to UnsupportedCredential (possibly along with a - // warning?). - pkg_config::probe_library("libsecret-1").unwrap(); - } -} diff --git a/credential/cargo-credential-gnome-secret/src/libsecret.rs b/credential/cargo-credential-gnome-secret/src/libsecret.rs index 98a66fbd89b..44fc4dea4f5 100644 --- a/credential/cargo-credential-gnome-secret/src/libsecret.rs +++ b/credential/cargo-credential-gnome-secret/src/libsecret.rs @@ -1,8 +1,10 @@ //! Implementation of the libsecret credential helper. +use anyhow::Context; use cargo_credential::{ read_token, Action, CacheControl, Credential, CredentialResponse, Error, RegistryInfo, Secret, }; +use libloading::{Library, Symbol}; use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_int}; use std::ptr::{null, null_mut}; @@ -52,29 +54,27 @@ enum SecretSchemaAttributeType { String = 0, } -extern "C" { - fn secret_password_store_sync( - schema: *const SecretSchema, - collection: *const gchar, - label: *const gchar, - password: *const gchar, - cancellable: *mut GCancellable, - error: *mut *mut GError, - ... - ) -> gboolean; - fn secret_password_clear_sync( - schema: *const SecretSchema, - cancellable: *mut GCancellable, - error: *mut *mut GError, - ... - ) -> gboolean; - fn secret_password_lookup_sync( - schema: *const SecretSchema, - cancellable: *mut GCancellable, - error: *mut *mut GError, - ... - ) -> *mut gchar; -} +type SecretPasswordStoreSync = extern "C" fn( + schema: *const SecretSchema, + collection: *const gchar, + label: *const gchar, + password: *const gchar, + cancellable: *mut GCancellable, + error: *mut *mut GError, + ... +) -> gboolean; +type SecretPasswordClearSync = extern "C" fn( + schema: *const SecretSchema, + cancellable: *mut GCancellable, + error: *mut *mut GError, + ... +) -> gboolean; +type SecretPasswordLookupSync = extern "C" fn( + schema: *const SecretSchema, + cancellable: *mut GCancellable, + error: *mut *mut GError, + ... +) -> *mut gchar; pub struct GnomeSecret; @@ -105,6 +105,26 @@ impl Credential for GnomeSecret { action: &Action, _args: &[&str], ) -> Result { + // Dynamically load libsecret to avoid users needing to install + // additional -dev packages when building this provider. + let lib; + let secret_password_lookup_sync: Symbol; + let secret_password_store_sync: Symbol; + let secret_password_clear_sync: Symbol; + unsafe { + lib = Library::new("libsecret-1.so").context( + "failed to load libsecret: try installing the `libsecret` \ + or `libsecret-1-0` package with the system package manager", + )?; + secret_password_lookup_sync = lib + .get(b"secret_password_lookup_sync\0") + .map_err(Box::new)?; + secret_password_store_sync = + lib.get(b"secret_password_store_sync\0").map_err(Box::new)?; + secret_password_clear_sync = + lib.get(b"secret_password_clear_sync\0").map_err(Box::new)?; + } + let index_url_c = CString::new(registry.index_url).unwrap(); match action { cargo_credential::Action::Get(_) => {