Include explicit indexes in URL auth policies list (#12631)

This will in principle fix the problem reported in #12611 that
`authenticate = "always"` is ignored for an index when `explicit =
true`. This change ensures all indexes are added to the URL auth
policies list passed to our auth middleware.

Incorporates #12624
Fixes #12611

---------

Co-authored-by: Zanie Blue <contact@zanie.dev>
This commit is contained in:
John Mumm 2025-04-02 19:54:31 +02:00 committed by GitHub
parent 2c2db197fc
commit 72f34157c6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 91 additions and 7 deletions

View file

@ -413,7 +413,7 @@ impl<'a> IndexLocations {
impl From<&IndexLocations> for UrlAuthPolicies {
fn from(index_locations: &IndexLocations) -> UrlAuthPolicies {
UrlAuthPolicies::from_tuples(index_locations.indexes().map(|index| {
UrlAuthPolicies::from_tuples(index_locations.allowed_indexes().into_iter().map(|index| {
let mut url = index
.url()
.root()

View file

@ -7822,7 +7822,7 @@ fn lock_redact_https() -> Result<()> {
let lock = context.read("uv.lock");
// The lockfile shout omit the credentials.
// The lockfile should omit the credentials.
insta::with_settings!({
filters => context.filters(),
}, {
@ -8485,7 +8485,7 @@ fn lock_env_credentials() -> Result<()> {
let lock = fs_err::read_to_string(context.temp_dir.join("uv.lock")).unwrap();
// The lockfile shout omit the credentials.
// The lockfile should omit the credentials.
insta::with_settings!({
filters => context.filters(),
}, {
@ -18759,7 +18759,7 @@ fn lock_change_requires_python() -> Result<()> {
Ok(())
}
/// Pass credentials for a named index via environment variables.
/// Retrieve credentials for a named index from the keyring.
#[test]
fn lock_keyring_credentials() -> Result<()> {
let keyring_context = TestContext::new("3.12");
@ -18798,7 +18798,7 @@ fn lock_keyring_credentials() -> Result<()> {
"#,
)?;
// Provide credentials via environment variables.
// Provide credentials via the keyring
uv_snapshot!(context.filters(), context.lock()
.env(EnvVars::index_username("PROXY"), "public")
.env(EnvVars::KEYRING_TEST_CREDENTIALS, r#"{"pypi-proxy.fly.dev": {"public": "heron"}}"#)
@ -18815,7 +18815,7 @@ fn lock_keyring_credentials() -> Result<()> {
let lock = fs_err::read_to_string(context.temp_dir.join("uv.lock")).unwrap();
// The lockfile shout omit the credentials.
// The lockfile should omit the credentials.
insta::with_settings!({
filters => context.filters(),
}, {
@ -18854,6 +18854,90 @@ fn lock_keyring_credentials() -> Result<()> {
Ok(())
}
/// Get credentials from the keyring with `explicit = true` and `authenticate = always`
#[test]
fn lock_keyring_explicit_always() -> Result<()> {
let keyring_context = TestContext::new("3.12");
// Install our keyring plugin
keyring_context
.pip_install()
.arg(
keyring_context
.workspace_root
.join("scripts")
.join("packages")
.join("keyring_test_plugin"),
)
.env_remove(EnvVars::UV_EXCLUDE_NEWER)
// (from `echo "keyring==v25.6.0" | uv pip compile - --no-annotate --no-header -q`)
.arg("jaraco-classes==3.4.0")
.arg("jaraco-context==6.0.1")
.arg("jaraco-functools==4.1.0")
.arg("keyring==25.6.0")
.arg("more-itertools==10.6.0")
.assert()
.success();
let context = TestContext::new("3.12");
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "foo"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["iniconfig"]
[tool.uv]
keyring-provider = "subprocess"
[tool.uv.sources]
iniconfig = {index = "proxy"}
[[tool.uv.index]]
name = "proxy"
url = "https://pypi-proxy.fly.dev/basic-auth/simple"
authenticate = "always"
explicit = true
"#,
)?;
// First, try some invalid credentials — we should not fall back to the default index
uv_snapshot!(context.filters(), context.lock()
.env(EnvVars::KEYRING_TEST_CREDENTIALS, r#"{"pypi-proxy.fly.dev": {"public": "frog"}}"#)
.env(EnvVars::PATH, venv_bin_path(&keyring_context.venv)), @r"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
Request for https://pypi-proxy.fly.dev/basic-auth/simple/iniconfig/
Request for pypi-proxy.fly.dev
× No solution found when resolving dependencies:
Because iniconfig was not found in the package registry and your project depends on iniconfig, we can conclude that your project's requirements are unsatisfiable.
hint: An index URL (https://pypi-proxy.fly.dev/basic-auth/simple) could not be queried due to a lack of valid authentication credentials (401 Unauthorized).
");
// With valid credentials, we should succeed
uv_snapshot!(context.filters(), context.lock()
.env(EnvVars::KEYRING_TEST_CREDENTIALS, r#"{"pypi-proxy.fly.dev": {"public": "heron"}}"#)
.env(EnvVars::PATH, venv_bin_path(&keyring_context.venv)), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Request for https://pypi-proxy.fly.dev/basic-auth/simple/iniconfig/
Request for pypi-proxy.fly.dev
Resolved 2 packages in [TIME]
");
Ok(())
}
/// Fetch credentials (including a username) for a named index via the keyring using `authenticate =
/// always`
#[test]
@ -18919,7 +19003,7 @@ fn lock_keyring_credentials_always_authenticate_fetches_username() -> Result<()>
let lock = fs_err::read_to_string(context.temp_dir.join("uv.lock")).unwrap();
// The lockfile shout omit the credentials.
// The lockfile should omit the credentials.
insta::with_settings!({
filters => context.filters(),
}, {