mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Display ssh git username in Debug
implementation (#13806)
This updates the `Debug` implementation of `DisplaySafeUrl` to display the git username in a case like `git+ssh://git@github.com/org/repo`. It also factors out the git username check into a shared function and adds related unit tests to `DisplaySafeUrl`.
This commit is contained in:
parent
a96e766b55
commit
90aefe4a4a
1 changed files with 52 additions and 18 deletions
|
@ -91,10 +91,7 @@ impl DisplaySafeUrl {
|
||||||
pub fn remove_credentials(&mut self) {
|
pub fn remove_credentials(&mut self) {
|
||||||
// For URLs that use the `git` convention (i.e., `ssh://git@github.com/...`), avoid dropping the
|
// For URLs that use the `git` convention (i.e., `ssh://git@github.com/...`), avoid dropping the
|
||||||
// username.
|
// username.
|
||||||
if matches!(self.0.scheme(), "git+ssh" | "ssh")
|
if is_ssh_git_username(&self.0) {
|
||||||
&& self.0.username() == "git"
|
|
||||||
&& self.0.password().is_none()
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let _ = self.0.set_username("");
|
let _ = self.0.set_username("");
|
||||||
|
@ -131,7 +128,11 @@ impl Display for DisplaySafeUrl {
|
||||||
impl Debug for DisplaySafeUrl {
|
impl Debug for DisplaySafeUrl {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let url = &self.0;
|
let url = &self.0;
|
||||||
let (username, password) = if url.username() != "" && url.password().is_some() {
|
// For URLs that use the `git` convention (i.e., `ssh://git@github.com/...`), avoid masking the
|
||||||
|
// username.
|
||||||
|
let (username, password) = if is_ssh_git_username(url) {
|
||||||
|
(url.username(), None)
|
||||||
|
} else if url.username() != "" && url.password().is_some() {
|
||||||
(url.username(), Some("****"))
|
(url.username(), Some("****"))
|
||||||
} else if url.username() != "" {
|
} else if url.username() != "" {
|
||||||
("****", None)
|
("****", None)
|
||||||
|
@ -175,6 +176,10 @@ impl FromStr for DisplaySafeUrl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_ssh_git_username(url: &Url) -> bool {
|
||||||
|
matches!(url.scheme(), "ssh" | "git+ssh") && url.username() == "git" && url.password().is_none()
|
||||||
|
}
|
||||||
|
|
||||||
fn display_with_redacted_credentials(
|
fn display_with_redacted_credentials(
|
||||||
url: &Url,
|
url: &Url,
|
||||||
f: &mut std::fmt::Formatter<'_>,
|
f: &mut std::fmt::Formatter<'_>,
|
||||||
|
@ -185,10 +190,7 @@ fn display_with_redacted_credentials(
|
||||||
|
|
||||||
// For URLs that use the `git` convention (i.e., `ssh://git@github.com/...`), avoid dropping the
|
// For URLs that use the `git` convention (i.e., `ssh://git@github.com/...`), avoid dropping the
|
||||||
// username.
|
// username.
|
||||||
if matches!(url.scheme(), "git+ssh" | "ssh")
|
if is_ssh_git_username(url) {
|
||||||
&& url.username() == "git"
|
|
||||||
&& url.password().is_none()
|
|
||||||
{
|
|
||||||
return write!(f, "{url}");
|
return write!(f, "{url}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +233,7 @@ mod tests {
|
||||||
let log_safe_url = DisplaySafeUrl::from(url);
|
let log_safe_url = DisplaySafeUrl::from(url);
|
||||||
assert_eq!(log_safe_url.username(), "");
|
assert_eq!(log_safe_url.username(), "");
|
||||||
assert!(log_safe_url.password().is_none());
|
assert!(log_safe_url.password().is_none());
|
||||||
assert_eq!(format!("{log_safe_url}"), url_str);
|
assert_eq!(log_safe_url.to_string(), url_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -242,7 +244,7 @@ mod tests {
|
||||||
assert_eq!(log_safe_url.username(), "user");
|
assert_eq!(log_safe_url.username(), "user");
|
||||||
assert!(log_safe_url.password().is_some_and(|p| p == "pass"));
|
assert!(log_safe_url.password().is_some_and(|p| p == "pass"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{log_safe_url}"),
|
log_safe_url.to_string(),
|
||||||
"https://user:****@pypi-proxy.fly.dev/basic-auth/simple"
|
"https://user:****@pypi-proxy.fly.dev/basic-auth/simple"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -255,7 +257,7 @@ mod tests {
|
||||||
assert_eq!(log_safe_url.username(), "");
|
assert_eq!(log_safe_url.username(), "");
|
||||||
assert!(log_safe_url.password().is_some_and(|p| p == "pass"));
|
assert!(log_safe_url.password().is_some_and(|p| p == "pass"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{log_safe_url}"),
|
log_safe_url.to_string(),
|
||||||
"https://:****@pypi-proxy.fly.dev/basic-auth/simple"
|
"https://:****@pypi-proxy.fly.dev/basic-auth/simple"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -268,11 +270,26 @@ mod tests {
|
||||||
assert_eq!(log_safe_url.username(), "user");
|
assert_eq!(log_safe_url.username(), "user");
|
||||||
assert!(log_safe_url.password().is_none());
|
assert!(log_safe_url.password().is_none());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{log_safe_url}"),
|
log_safe_url.to_string(),
|
||||||
"https://****@pypi-proxy.fly.dev/basic-auth/simple"
|
"https://****@pypi-proxy.fly.dev/basic-auth/simple"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_url_git_username() {
|
||||||
|
let ssh_str = "ssh://git@github.com/org/repo";
|
||||||
|
let ssh_url = DisplaySafeUrl::parse(ssh_str).unwrap();
|
||||||
|
assert_eq!(ssh_url.username(), "git");
|
||||||
|
assert!(ssh_url.password().is_none());
|
||||||
|
assert_eq!(ssh_url.to_string(), ssh_str);
|
||||||
|
// Test again for the `git+ssh` scheme
|
||||||
|
let git_ssh_str = "git+ssh://git@github.com/org/repo";
|
||||||
|
let git_ssh_url = DisplaySafeUrl::parse(git_ssh_str).unwrap();
|
||||||
|
assert_eq!(git_ssh_url.username(), "git");
|
||||||
|
assert!(git_ssh_url.password().is_none());
|
||||||
|
assert_eq!(git_ssh_url.to_string(), git_ssh_str);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_url_string() {
|
fn parse_url_string() {
|
||||||
let url_str = "https://user:pass@pypi-proxy.fly.dev/basic-auth/simple";
|
let url_str = "https://user:pass@pypi-proxy.fly.dev/basic-auth/simple";
|
||||||
|
@ -280,7 +297,7 @@ mod tests {
|
||||||
assert_eq!(log_safe_url.username(), "user");
|
assert_eq!(log_safe_url.username(), "user");
|
||||||
assert!(log_safe_url.password().is_some_and(|p| p == "pass"));
|
assert!(log_safe_url.password().is_some_and(|p| p == "pass"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{log_safe_url}"),
|
log_safe_url.to_string(),
|
||||||
"https://user:****@pypi-proxy.fly.dev/basic-auth/simple"
|
"https://user:****@pypi-proxy.fly.dev/basic-auth/simple"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -293,17 +310,34 @@ mod tests {
|
||||||
assert_eq!(log_safe_url.username(), "");
|
assert_eq!(log_safe_url.username(), "");
|
||||||
assert!(log_safe_url.password().is_none());
|
assert!(log_safe_url.password().is_none());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{log_safe_url}"),
|
log_safe_url.to_string(),
|
||||||
"https://pypi-proxy.fly.dev/basic-auth/simple"
|
"https://pypi-proxy.fly.dev/basic-auth/simple"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn preserve_ssh_git_username_on_remove_credentials() {
|
||||||
|
let ssh_str = "ssh://git@pypi-proxy.fly.dev/basic-auth/simple";
|
||||||
|
let mut ssh_url = DisplaySafeUrl::parse(ssh_str).unwrap();
|
||||||
|
ssh_url.remove_credentials();
|
||||||
|
assert_eq!(ssh_url.username(), "git");
|
||||||
|
assert!(ssh_url.password().is_none());
|
||||||
|
assert_eq!(ssh_url.to_string(), ssh_str);
|
||||||
|
// Test again for `git+ssh` scheme
|
||||||
|
let git_ssh_str = "git+ssh://git@pypi-proxy.fly.dev/basic-auth/simple";
|
||||||
|
let mut git_shh_url = DisplaySafeUrl::parse(git_ssh_str).unwrap();
|
||||||
|
git_shh_url.remove_credentials();
|
||||||
|
assert_eq!(git_shh_url.username(), "git");
|
||||||
|
assert!(git_shh_url.password().is_none());
|
||||||
|
assert_eq!(git_shh_url.to_string(), git_ssh_str);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn displayable_with_credentials() {
|
fn displayable_with_credentials() {
|
||||||
let url_str = "https://user:pass@pypi-proxy.fly.dev/basic-auth/simple";
|
let url_str = "https://user:pass@pypi-proxy.fly.dev/basic-auth/simple";
|
||||||
let log_safe_url = DisplaySafeUrl::parse(url_str).unwrap();
|
let log_safe_url = DisplaySafeUrl::parse(url_str).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&log_safe_url.displayable_with_credentials().to_string(),
|
log_safe_url.displayable_with_credentials().to_string(),
|
||||||
url_str
|
url_str
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -313,7 +347,7 @@ mod tests {
|
||||||
let url_str = "https://token@example.com/abc/";
|
let url_str = "https://token@example.com/abc/";
|
||||||
let log_safe_url = DisplaySafeUrl::parse(url_str).unwrap();
|
let log_safe_url = DisplaySafeUrl::parse(url_str).unwrap();
|
||||||
let foo_url = log_safe_url.join("foo").unwrap();
|
let foo_url = log_safe_url.join("foo").unwrap();
|
||||||
assert_eq!(format!("{foo_url}"), "https://****@example.com/abc/foo");
|
assert_eq!(foo_url.to_string(), "https://****@example.com/abc/foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -324,7 +358,7 @@ mod tests {
|
||||||
assert_eq!(log_safe_url.username(), "user");
|
assert_eq!(log_safe_url.username(), "user");
|
||||||
assert!(log_safe_url.password().is_some_and(|p| p == "pass"));
|
assert!(log_safe_url.password().is_some_and(|p| p == "pass"));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{log_safe_url}"),
|
log_safe_url.to_string(),
|
||||||
"https://user:****@pypi-proxy.fly.dev/basic-auth/simple"
|
"https://user:****@pypi-proxy.fly.dev/basic-auth/simple"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue