#![cfg(all(feature = "http", unix))]
#[allow(dead_code)]
#[path = "support/http_git.rs"]
mod http_git;
#[allow(dead_code)]
#[path = "support/pack_git.rs"]
mod pack_git;
use std::os::unix::fs::PermissionsExt;
use std::path::Path;
use std::process::{Command, Stdio};
use girt::Repository;
use girt::workflow::{Environment, NoCallbacks};
use http_git::Server;
use pack_git::{Fixture, git};
use rstest::rstest;
fn executable(path: &Path, body: &str) {
std::fs::write(path, format!("#!/bin/sh\n{body}\n")).unwrap();
std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o755)).unwrap();
}
fn scripts(root: &Path, password: &str) {
let answer = match password.strip_prefix("user:") {
Some(rest) => {
let (user, password) = rest.split_once('/').unwrap();
format!("username={user}\\npassword={password}")
}
None => format!("password={password}"),
};
let log = root.join("log");
executable(
&root.join("helper"),
&format!(
"input=$(cat)\nprintf '%s %s\\n' \"$1\" \"$(printf '%s\\n' \"$input\" | grep -E '^(username|host)=' | tr '\\n' ' ')\" >> '{}'\n\
[ \"$1\" = get ] && printf '{answer}\\n'\nexit 0",
log.display()
),
);
executable(
&root.join("askpass"),
&format!(
"printf 'ask %s\\n' \"$1\" >> '{}'\necho {password}",
log.display()
),
);
}
fn take_log(root: &Path, port: &str) -> Vec<String> {
let log = std::fs::read_to_string(root.join("log")).unwrap_or_default();
let _ = std::fs::remove_file(root.join("log"));
log.lines()
.map(|line| line.trim_end().replace(port, "PORT"))
.collect()
}
#[rstest]
#[case::helper("http://pu@{proxy}", true, false, "pp", true)]
#[case::askpass("http://pu@{proxy}", false, true, "pp", true)]
#[case::helper_username_replaces_url_user("http://pu@{proxy}", true, false, "user:other/pp", false)]
#[case::wrong_password("http://pu@{proxy}", true, false, "nope", false)]
#[case::no_user_asks_nobody("http://{proxy}", true, true, "pp", false)]
fn proxy_credentials_match_git(
#[case] proxy: &str,
#[case] helper: bool,
#[case] askpass: bool,
#[case] password: &str,
#[case] succeeds: bool,
) {
let scratch = tempfile::tempdir().unwrap();
scripts(scratch.path(), password);
let source = Fixture::new(girt::ObjectFormat::Sha1, true, 2);
let server = Server::new(source.root.path(), "proxy-auth", "", None);
let address = server
.url
.trim_start_matches("http://")
.trim_end_matches("/repo")
.to_owned();
let port = address.rsplit(':').next().unwrap().to_owned();
let proxy = proxy.replace("{proxy}", &address);
let local = tempfile::tempdir().unwrap();
git(local.path(), &["init", "--quiet", "."], b"");
git(
local.path(),
&["remote", "add", "origin", "http://example.invalid/repo"],
b"",
);
git(local.path(), &["config", "http.proxy", &proxy], b"");
let helper_path = scratch.path().join("helper");
let helper_value = if helper {
helper_path.to_str().unwrap()
} else {
""
};
git(
local.path(),
&["config", "credential.helper", helper_value],
b"",
);
let askpass_path = scratch.path().join("askpass");
let mut command = Command::new("git");
for (key, _) in std::env::vars_os() {
let key_text = key.to_string_lossy().to_ascii_lowercase();
if key_text.starts_with("git_") || key_text.ends_with("_proxy") || key_text == "ssh_askpass"
{
command.env_remove(key);
}
}
command
.current_dir(local.path())
.env("HOME", local.path())
.env("GIT_CONFIG_NOSYSTEM", "1")
.env("GIT_CONFIG_GLOBAL", local.path().join("absent-config"))
.env("GIT_TERMINAL_PROMPT", "0")
.env_remove("SSH_AUTH_SOCK")
.args(["ls-remote", "origin"])
.stdin(Stdio::null());
let mut environment = Environment::default()
.with("HOME", local.path())
.with("PATH", "/usr/bin:/bin");
if askpass {
command.env("GIT_ASKPASS", &askpass_path);
environment = environment.with("GIT_ASKPASS", &askpass_path);
}
let output = command.output().unwrap();
assert_eq!(
output.status.success(),
succeeds,
"git: {}",
String::from_utf8_lossy(&output.stderr)
);
let git_log = take_log(scratch.path(), &port);
let repository = Repository::open(local.path()).unwrap();
let result = girt::workflow::remote_head(&repository, "origin", &environment, &mut NoCallbacks);
assert_eq!(result.is_ok(), succeeds, "girt: {:?}", result.err());
assert_eq!(take_log(scratch.path(), &port), git_log);
}