rename crate to cargo-credential-libsecret

This commit is contained in:
Arlo Siemsen 2023-08-17 15:59:33 -05:00
parent 3d8e8d32cd
commit 763edbab09
10 changed files with 243 additions and 246 deletions

4
Cargo.lock generated
View File

@ -263,7 +263,7 @@ dependencies = [
"base64",
"bytesize",
"cargo-credential",
"cargo-credential-gnome-secret",
"cargo-credential-libsecret",
"cargo-credential-macos-keychain",
"cargo-credential-wincred",
"cargo-platform 0.1.4",
@ -355,7 +355,7 @@ dependencies = [
]
[[package]]
name = "cargo-credential-gnome-secret"
name = "cargo-credential-libsecret"
version = "0.3.1"
dependencies = [
"anyhow",

View File

@ -20,7 +20,7 @@ base64 = "0.21.2"
bytesize = "1.2"
cargo = { path = "" }
cargo-credential = { version = "0.3.0", path = "credential/cargo-credential" }
cargo-credential-gnome-secret = { version = "0.3.1", path = "credential/cargo-credential-gnome-secret" }
cargo-credential-libsecret = { version = "0.3.1", path = "credential/cargo-credential-libsecret" }
cargo-credential-wincred = { version = "0.3.0", path = "credential/cargo-credential-wincred" }
cargo-credential-macos-keychain = { version = "0.3.0", path = "credential/cargo-credential-macos-keychain" }
cargo-platform = { path = "crates/cargo-platform", version = "0.1.4" }
@ -124,7 +124,7 @@ base64.workspace = true
bytesize.workspace = true
cargo-platform.workspace = true
cargo-credential.workspace = true
cargo-credential-gnome-secret.workspace = true
cargo-credential-libsecret.workspace = true
cargo-credential-macos-keychain.workspace = true
cargo-credential-wincred.workspace = true
cargo-util.workspace = true

View File

@ -153,7 +153,7 @@ fn bump_check(args: &clap::ArgMatches, config: &mut cargo::util::Config) -> Carg
"--exclude",
"cargo-credential-1password",
"--exclude",
"cargo-credential-gnome-secret",
"cargo-credential-libsecret",
"--exclude",
"cargo-credential-macos-keychain",
"--exclude",

View File

@ -1,6 +0,0 @@
#[cfg(target_os = "linux")]
mod libsecret;
#[cfg(not(target_os = "linux"))]
pub use cargo_credential::UnsupportedCredential as GnomeSecret;
#[cfg(target_os = "linux")]
pub use libsecret::GnomeSecret;

View File

@ -1,226 +0,0 @@
//! 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};
#[allow(non_camel_case_types)]
type gchar = c_char;
#[allow(non_camel_case_types)]
type gboolean = c_int;
type GQuark = u32;
#[repr(C)]
struct GError {
domain: GQuark,
code: c_int,
message: *mut gchar,
}
#[repr(C)]
struct GCancellable {
_private: [u8; 0],
}
#[repr(C)]
struct SecretSchema {
name: *const gchar,
flags: SecretSchemaFlags,
attributes: [SecretSchemaAttribute; 32],
}
#[repr(C)]
#[derive(Copy, Clone)]
struct SecretSchemaAttribute {
name: *const gchar,
attr_type: SecretSchemaAttributeType,
}
#[repr(C)]
enum SecretSchemaFlags {
None = 0,
}
#[repr(C)]
#[derive(Copy, Clone)]
enum SecretSchemaAttributeType {
String = 0,
}
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;
fn label(index_url: &str) -> CString {
CString::new(format!("cargo-registry:{}", index_url)).unwrap()
}
fn schema() -> SecretSchema {
let mut attributes = [SecretSchemaAttribute {
name: null(),
attr_type: SecretSchemaAttributeType::String,
}; 32];
attributes[0] = SecretSchemaAttribute {
name: b"url\0".as_ptr() as *const gchar,
attr_type: SecretSchemaAttributeType::String,
};
SecretSchema {
name: b"org.rust-lang.cargo.registry\0".as_ptr() as *const gchar,
flags: SecretSchemaFlags::None,
attributes,
}
}
impl Credential for GnomeSecret {
fn perform(
&self,
registry: &RegistryInfo,
action: &Action,
_args: &[&str],
) -> Result<CredentialResponse, Error> {
// Dynamically load libsecret to avoid users needing to install
// additional -dev packages when building this provider.
let lib;
let secret_password_lookup_sync: Symbol<SecretPasswordLookupSync>;
let secret_password_store_sync: Symbol<SecretPasswordStoreSync>;
let secret_password_clear_sync: Symbol<SecretPasswordClearSync>;
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(_) => {
let mut error: *mut GError = null_mut();
let attr_url = CString::new("url").unwrap();
let schema = schema();
unsafe {
let token_c = secret_password_lookup_sync(
&schema,
null_mut(),
&mut error,
attr_url.as_ptr(),
index_url_c.as_ptr(),
null() as *const gchar,
);
if !error.is_null() {
return Err(format!(
"failed to get token: {}",
CStr::from_ptr((*error).message)
.to_str()
.unwrap_or_default()
)
.into());
}
if token_c.is_null() {
return Err(Error::NotFound);
}
let token = Secret::from(
CStr::from_ptr(token_c)
.to_str()
.map_err(|e| format!("expected utf8 token: {}", e))?
.to_string(),
);
Ok(CredentialResponse::Get {
token,
cache: CacheControl::Session,
operation_independent: true,
})
}
}
cargo_credential::Action::Login(options) => {
let label = label(registry.name.unwrap_or(registry.index_url));
let token = CString::new(read_token(options, registry)?.expose()).unwrap();
let mut error: *mut GError = null_mut();
let attr_url = CString::new("url").unwrap();
let schema = schema();
unsafe {
secret_password_store_sync(
&schema,
b"default\0".as_ptr() as *const gchar,
label.as_ptr(),
token.as_ptr(),
null_mut(),
&mut error,
attr_url.as_ptr(),
index_url_c.as_ptr(),
null() as *const gchar,
);
if !error.is_null() {
return Err(format!(
"failed to store token: {}",
CStr::from_ptr((*error).message)
.to_str()
.unwrap_or_default()
)
.into());
}
}
Ok(CredentialResponse::Login)
}
cargo_credential::Action::Logout => {
let schema = schema();
let mut error: *mut GError = null_mut();
let attr_url = CString::new("url").unwrap();
unsafe {
secret_password_clear_sync(
&schema,
null_mut(),
&mut error,
attr_url.as_ptr(),
index_url_c.as_ptr(),
null() as *const gchar,
);
if !error.is_null() {
return Err(format!(
"failed to erase token: {}",
CStr::from_ptr((*error).message)
.to_str()
.unwrap_or_default()
)
.into());
}
}
Ok(CredentialResponse::Logout)
}
_ => Err(Error::OperationNotSupported),
}
}
}

View File

@ -1,5 +0,0 @@
//! Cargo registry gnome libsecret credential process.
fn main() {
cargo_credential::main(GnomeSecret);
}

View File

@ -1,5 +1,5 @@
[package]
name = "cargo-credential-gnome-secret"
name = "cargo-credential-libsecret"
version = "0.3.1"
edition.workspace = true
license.workspace = true
@ -10,4 +10,3 @@ description = "A Cargo credential process that stores tokens with GNOME libsecre
anyhow.workspace = true
cargo-credential.workspace = true
libloading.workspace = true

View File

@ -1,4 +1,4 @@
# cargo-credential-gnome-secret
# cargo-credential-libsecret
This is the implementation for the Cargo credential helper for [GNOME libsecret].
See the [credential-process] documentation for how to use this.

View File

@ -0,0 +1,235 @@
#[cfg(target_os = "linux")]
mod linux {
//! 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};
#[allow(non_camel_case_types)]
type gchar = c_char;
#[allow(non_camel_case_types)]
type gboolean = c_int;
type GQuark = u32;
#[repr(C)]
struct GError {
domain: GQuark,
code: c_int,
message: *mut gchar,
}
#[repr(C)]
struct GCancellable {
_private: [u8; 0],
}
#[repr(C)]
struct SecretSchema {
name: *const gchar,
flags: SecretSchemaFlags,
attributes: [SecretSchemaAttribute; 32],
}
#[repr(C)]
#[derive(Copy, Clone)]
struct SecretSchemaAttribute {
name: *const gchar,
attr_type: SecretSchemaAttributeType,
}
#[repr(C)]
enum SecretSchemaFlags {
None = 0,
}
#[repr(C)]
#[derive(Copy, Clone)]
enum SecretSchemaAttributeType {
String = 0,
}
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 LibSecretCredential;
fn label(index_url: &str) -> CString {
CString::new(format!("cargo-registry:{}", index_url)).unwrap()
}
fn schema() -> SecretSchema {
let mut attributes = [SecretSchemaAttribute {
name: null(),
attr_type: SecretSchemaAttributeType::String,
}; 32];
attributes[0] = SecretSchemaAttribute {
name: b"url\0".as_ptr() as *const gchar,
attr_type: SecretSchemaAttributeType::String,
};
SecretSchema {
name: b"org.rust-lang.cargo.registry\0".as_ptr() as *const gchar,
flags: SecretSchemaFlags::None,
attributes,
}
}
impl Credential for LibSecretCredential {
fn perform(
&self,
registry: &RegistryInfo,
action: &Action,
_args: &[&str],
) -> Result<CredentialResponse, Error> {
// Dynamically load libsecret to avoid users needing to install
// additional -dev packages when building this provider.
let lib;
let secret_password_lookup_sync: Symbol<SecretPasswordLookupSync>;
let secret_password_store_sync: Symbol<SecretPasswordStoreSync>;
let secret_password_clear_sync: Symbol<SecretPasswordClearSync>;
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(_) => {
let mut error: *mut GError = null_mut();
let attr_url = CString::new("url").unwrap();
let schema = schema();
unsafe {
let token_c = secret_password_lookup_sync(
&schema,
null_mut(),
&mut error,
attr_url.as_ptr(),
index_url_c.as_ptr(),
null() as *const gchar,
);
if !error.is_null() {
return Err(format!(
"failed to get token: {}",
CStr::from_ptr((*error).message)
.to_str()
.unwrap_or_default()
)
.into());
}
if token_c.is_null() {
return Err(Error::NotFound);
}
let token = Secret::from(
CStr::from_ptr(token_c)
.to_str()
.map_err(|e| format!("expected utf8 token: {}", e))?
.to_string(),
);
Ok(CredentialResponse::Get {
token,
cache: CacheControl::Session,
operation_independent: true,
})
}
}
cargo_credential::Action::Login(options) => {
let label = label(registry.name.unwrap_or(registry.index_url));
let token = CString::new(read_token(options, registry)?.expose()).unwrap();
let mut error: *mut GError = null_mut();
let attr_url = CString::new("url").unwrap();
let schema = schema();
unsafe {
secret_password_store_sync(
&schema,
b"default\0".as_ptr() as *const gchar,
label.as_ptr(),
token.as_ptr(),
null_mut(),
&mut error,
attr_url.as_ptr(),
index_url_c.as_ptr(),
null() as *const gchar,
);
if !error.is_null() {
return Err(format!(
"failed to store token: {}",
CStr::from_ptr((*error).message)
.to_str()
.unwrap_or_default()
)
.into());
}
}
Ok(CredentialResponse::Login)
}
cargo_credential::Action::Logout => {
let schema = schema();
let mut error: *mut GError = null_mut();
let attr_url = CString::new("url").unwrap();
unsafe {
secret_password_clear_sync(
&schema,
null_mut(),
&mut error,
attr_url.as_ptr(),
index_url_c.as_ptr(),
null() as *const gchar,
);
if !error.is_null() {
return Err(format!(
"failed to erase token: {}",
CStr::from_ptr((*error).message)
.to_str()
.unwrap_or_default()
)
.into());
}
}
Ok(CredentialResponse::Logout)
}
_ => Err(Error::OperationNotSupported),
}
}
}
}
#[cfg(not(target_os = "linux"))]
pub use cargo_credential::UnsupportedCredential as LibSecretCredential;
#[cfg(target_os = "linux")]
pub use linux::LibSecretCredential;

View File

@ -451,7 +451,7 @@ fn credential_action(
"cargo:token-from-stdout" => Box::new(BasicProcessCredential {}),
"cargo:wincred" => Box::new(cargo_credential_wincred::WindowsCredential {}),
"cargo:macos-keychain" => Box::new(cargo_credential_macos_keychain::MacKeychain {}),
"cargo:libsecret" => Box::new(cargo_credential_gnome_secret::GnomeSecret {}),
"cargo:libsecret" => Box::new(cargo_credential_libsecret::LibSecretCredential {}),
process => Box::new(CredentialProcessCredential::new(process)),
};
config.shell().verbose(|c| {