From 4ea44bec0aafcc93860423fa627bbf29f06d6eec Mon Sep 17 00:00:00 2001 From: John Mumm Date: Thu, 15 May 2025 13:36:18 +0200 Subject: [PATCH] Ensure cached realm credentials are applied if no password is found for index URL (#13463) We were not correctly falling back to cached realm credentials when an index URL was provided with only a username. This came up in a [later comment](https://github.com/astral-sh/uv/issues/13443#issuecomment-2881115301) on #13443 where credentials in a pip extra index in `uv.toml` were being ignored when the same URL (but with only a username) was used at the command line for `--extra-index-url`. I've added a test to catch this case. Closes #13443 --- crates/uv-auth/src/middleware.rs | 15 ++++++++++++--- crates/uv/tests/it/pip_install.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/crates/uv-auth/src/middleware.rs b/crates/uv-auth/src/middleware.rs index cb9b2be20..86155438d 100644 --- a/crates/uv-auth/src/middleware.rs +++ b/crates/uv-auth/src/middleware.rs @@ -280,7 +280,10 @@ impl Middleware for AuthMiddleware { .map(|credentials| credentials.to_username()) .unwrap_or(Username::none()); let credentials = if let Some(index_url) = maybe_index_url { - self.cache().get_url(index_url, &username) + self.cache().get_url(index_url, &username).or_else(|| { + self.cache() + .get_realm(Realm::from(retry_request.url()), username) + }) } else { // Since there is no known index for this URL, check if there are credentials in // the realm-level cache. @@ -434,9 +437,15 @@ impl AuthMiddleware { Some(credentials) } else if index_url.is_some() { // If this is a known index, we fall back to checking for the realm. - self.cache() + if let Some(credentials) = self + .cache() .get_realm(Realm::from(request.url()), credentials.to_username()) - .or(Some(credentials)) + { + request = credentials.authenticate(request); + Some(credentials) + } else { + Some(credentials) + } } else { // If we don't find a password, we'll still attempt the request with the existing credentials Some(credentials) diff --git a/crates/uv/tests/it/pip_install.rs b/crates/uv/tests/it/pip_install.rs index 2d46dc099..d603d66eb 100644 --- a/crates/uv/tests/it/pip_install.rs +++ b/crates/uv/tests/it/pip_install.rs @@ -288,6 +288,36 @@ fn invalid_uv_toml_option_disallowed() -> Result<()> { Ok(()) } +#[test] +fn cache_uv_toml_credentials() -> Result<()> { + let context = TestContext::new("3.12"); + let uv_toml = context.temp_dir.child("uv.toml"); + uv_toml.write_str(indoc! {r#" + [pip] + extra-index-url = ["https://public:heron@pypi-proxy.fly.dev/basic-auth/simple/"] + "#})?; + + // Provide an extra index with the same username and URL as in `uv.toml` but + // no password. + uv_snapshot!(context.pip_install() + .arg("iniconfig") + .arg("--extra-index-url") + .arg("https://public@pypi-proxy.fly.dev/basic-auth/simple/"), @r" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 1 package in [TIME] + Prepared 1 package in [TIME] + Installed 1 package in [TIME] + + iniconfig==2.0.0 + " + ); + + Ok(()) +} + /// For indirect, non-user controlled pyproject.toml, we don't enforce correctness. /// /// If we fail to extract the PEP 621 metadata, we fall back to treating it as a source