Skip to content

credentials helper from globals #1427

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 23 additions & 19 deletions gix-config/src/file/init/comfort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,35 @@ use crate::{
impl File<'static> {
/// Open all global configuration files which involves the following sources:
///
/// * [system][crate::Source::System]
/// * [git][crate::Source::Git]
/// * [user][crate::Source::User]
/// * [git-installation](source::Kind::GitInstallation)
/// * [system](source::Kind::System)
/// * [globals](source::Kind::Global)
///
/// which excludes repository local configuration, as well as override-configuration from environment variables.
///
/// Note that the file might [be empty][File::is_void()] in case no configuration file was found.
pub fn from_globals() -> Result<File<'static>, init::from_paths::Error> {
let metas = [source::Kind::System, source::Kind::Global]
.iter()
.flat_map(|kind| kind.sources())
.filter_map(|source| {
let path = source
.storage_location(&mut gix_path::env::var)
.and_then(|p| p.is_file().then_some(p))
.map(Cow::into_owned);
let metas = [
source::Kind::GitInstallation,
source::Kind::System,
source::Kind::Global,
]
.iter()
.flat_map(|kind| kind.sources())
.filter_map(|source| {
let path = source
.storage_location(&mut gix_path::env::var)
.and_then(|p| p.is_file().then_some(p))
.map(Cow::into_owned);

Metadata {
path,
source: *source,
level: 0,
trust: gix_sec::Trust::Full,
}
.into()
});
Metadata {
path,
source: *source,
level: 0,
trust: gix_sec::Trust::Full,
}
.into()
});

let home = gix_path::env::home_dir();
let options = init::Options {
Expand Down
1 change: 1 addition & 0 deletions gix-credentials/src/protocol/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ mod mutate {
let url = gix_url::parse(self.url.as_ref().ok_or(protocol::Error::UrlMissing)?.as_ref())?;
self.protocol = Some(url.scheme.as_str().into());
self.username = url.user().map(ToOwned::to_owned);
self.password = url.password().map(ToOwned::to_owned);
self.host = url.host().map(ToOwned::to_owned).map(|mut host| {
if let Some(port) = url.port {
use std::fmt::Write;
Expand Down
9 changes: 9 additions & 0 deletions gix-credentials/tests/protocol/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ mod destructure_url_in_place {
assert_eq_parts("ssh://user@host:21/path", "ssh", "user", "host:21", "path", false);
assert_eq_parts("ssh://host.org/path", "ssh", None, "host.org", "path", true);
}

#[test]
fn passwords_are_placed_in_context_too() -> crate::Result {
let mut ctx = url_ctx("http://user:password@host/path");
ctx.destructure_url_in_place(false)?;
assert_eq!(ctx.password.as_deref(), Some("password"));
Ok(())
}

#[test]
fn http_and_https_ignore_the_path_by_default() {
assert_eq_parts(
Expand Down
62 changes: 42 additions & 20 deletions gix/src/config/cache/access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,25 +212,13 @@ impl Cache {
&self,
key: impl gix_config::AsKey,
) -> Option<Result<Cow<'_, std::path::Path>, gix_config::path::interpolate::Error>> {
let path = self
.resolved
.path_filter(&key, &mut self.filter_config_section.clone())?;

if self.lenient_config && path.is_empty() {
let _key = key.as_key();
gix_trace::info!(
"Ignored empty path at {section_name}.{subsection_name:?}.{name} due to lenient configuration",
section_name = _key.section_name,
subsection_name = _key.subsection_name,
name = _key.value_name
);
return None;
}

let install_dir = crate::path::install_dir().ok();
let home = self.home_dir();
let ctx = config::cache::interpolate_context(install_dir.as_deref(), home.as_deref());
Some(path.interpolate(ctx))
trusted_file_path(
&self.resolved,
key,
&mut self.filter_config_section.clone(),
self.lenient_config,
self.environment,
)
}

pub(crate) fn apply_leniency<T, E>(&self, res: Option<Result<T, E>>) -> Result<Option<T>, E> {
Expand Down Expand Up @@ -465,11 +453,45 @@ impl Cache {
///
/// We never fail for here even if the permission is set to deny as we `gix-config` will fail later
/// if it actually wants to use the home directory - we don't want to fail prematurely.
#[cfg(any(
feature = "blocking-http-transport-reqwest",
feature = "blocking-http-transport-curl"
))]
pub(crate) fn home_dir(&self) -> Option<PathBuf> {
gix_path::env::home_dir().and_then(|path| self.environment.home.check_opt(path))
home_dir(self.environment)
}
}

pub(crate) fn trusted_file_path<'config>(
config: &'config gix_config::File<'_>,
key: impl gix_config::AsKey,
filter: &mut gix_config::file::MetadataFilter,
lenient_config: bool,
environment: crate::open::permissions::Environment,
) -> Option<Result<Cow<'config, std::path::Path>, gix_config::path::interpolate::Error>> {
let path = config.path_filter(&key, filter)?;

if lenient_config && path.is_empty() {
let _key = key.as_key();
gix_trace::info!(
"Ignored empty path at {section_name}.{subsection_name:?}.{name} due to lenient configuration",
section_name = _key.section_name,
subsection_name = _key.subsection_name,
name = _key.value_name
);
return None;
}

let install_dir = crate::path::install_dir().ok();
let home = home_dir(environment);
let ctx = config::cache::interpolate_context(install_dir.as_deref(), home.as_deref());
Some(path.interpolate(ctx))
}

pub(crate) fn home_dir(environment: crate::open::permissions::Environment) -> Option<PathBuf> {
gix_path::env::home_dir().and_then(|path| environment.home.check_opt(path))
}

fn boolean(
me: &Cache,
full_key: &str,
Expand Down
2 changes: 1 addition & 1 deletion gix/src/config/cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ impl std::fmt::Debug for Cache {
}
}

mod access;
pub(crate) mod access;

pub(crate) mod util;

Expand Down
8 changes: 6 additions & 2 deletions gix/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ pub struct CommitAutoRollback<'repo> {
pub(crate) prev_config: crate::Config,
}

pub(crate) mod section {
///
#[allow(clippy::empty_docs)]
pub mod section {
/// A filter that returns `true` for `meta` if the meta-data attached to a configuration section can be trusted.
/// This is either the case if its file is fully trusted, or if it's a section from a system-wide file.
pub fn is_trusted(meta: &gix_config::file::Metadata) -> bool {
meta.trust == gix_sec::Trust::Full || meta.source.kind() != gix_config::source::Kind::Repository
}
Expand Down Expand Up @@ -627,7 +631,7 @@ pub(crate) struct Cache {
/// If true, we are on a case-insensitive file system.
pub ignore_case: bool,
/// If true, we should default what's possible if something is misconfigured, on case by case basis, to be more resilient.
/// Also available in options! Keep in sync!
/// Also, available in options! Keep in sync!
pub lenient_config: bool,
#[cfg_attr(not(feature = "worktree-mutation"), allow(dead_code))]
attributes: crate::open::permissions::Attributes,
Expand Down
Loading
Loading