From 9745b7635716f2e8d82a906ff8f205e072e4030b Mon Sep 17 00:00:00 2001 From: John Mumm Date: Tue, 25 Mar 2025 22:13:42 +0100 Subject: [PATCH] Add auth policy support for pip commands (#12470) We were not applying the `authenticate = "always"` behavior to `uv pip` commands (related to #12362). This PR addresses that, applying authentication policies wherever we set up a registry client. --- crates/uv-auth/src/middleware.rs | 3 +-- crates/uv-auth/src/policy.rs | 11 +++++++++++ crates/uv/src/commands/pip/compile.rs | 2 ++ crates/uv/src/commands/pip/install.rs | 2 ++ crates/uv/src/commands/pip/list.rs | 2 ++ crates/uv/src/commands/pip/sync.rs | 2 ++ crates/uv/src/commands/pip/tree.rs | 2 ++ crates/uv/src/commands/project/add.rs | 2 ++ crates/uv/src/commands/venv.rs | 2 ++ crates/uv/tests/it/edit.rs | 11 +++++++++++ 10 files changed, 37 insertions(+), 2 deletions(-) diff --git a/crates/uv-auth/src/middleware.rs b/crates/uv-auth/src/middleware.rs index ffd837328..64e2910da 100644 --- a/crates/uv-auth/src/middleware.rs +++ b/crates/uv-auth/src/middleware.rs @@ -181,9 +181,8 @@ impl Middleware for AuthMiddleware { // In the middleware, existing credentials are already moved from the URL // to the headers so for display purposes we restore some information let url = tracing_url(&request, request_credentials.as_ref()); - trace!("Handling request for {url}"); - let auth_policy = self.url_auth_policies.policy_for(request.url()); + trace!("Handling request for {url} with authentication policy {auth_policy}"); let credentials: Option> = if matches!(auth_policy, AuthPolicy::Never) { None diff --git a/crates/uv-auth/src/policy.rs b/crates/uv-auth/src/policy.rs index f9077b51c..2b6410585 100644 --- a/crates/uv-auth/src/policy.rs +++ b/crates/uv-auth/src/policy.rs @@ -1,3 +1,5 @@ +use std::fmt::{self, Display, Formatter}; + use rustc_hash::FxHashMap; use url::Url; @@ -36,6 +38,15 @@ pub enum AuthPolicy { Never, } +impl Display for AuthPolicy { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + AuthPolicy::Auto => write!(f, "auto"), + AuthPolicy::Always => write!(f, "always"), + AuthPolicy::Never => write!(f, "never"), + } + } +} #[derive(Debug, Default, Clone, Eq, PartialEq)] pub struct UrlAuthPolicies(FxHashMap); diff --git a/crates/uv/src/commands/pip/compile.rs b/crates/uv/src/commands/pip/compile.rs index a33b46067..da6301d63 100644 --- a/crates/uv/src/commands/pip/compile.rs +++ b/crates/uv/src/commands/pip/compile.rs @@ -10,6 +10,7 @@ use owo_colors::OwoColorize; use rustc_hash::FxHashSet; use tracing::debug; +use uv_auth::UrlAuthPolicies; use uv_cache::Cache; use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ @@ -360,6 +361,7 @@ pub(crate) async fn pip_compile( .cache(cache.clone()) .index_urls(index_locations.index_urls()) .index_strategy(index_strategy) + .url_auth_policies(UrlAuthPolicies::from(&index_locations)) .torch_backend(torch_backend) .markers(interpreter.markers()) .platform(interpreter.platform()) diff --git a/crates/uv/src/commands/pip/install.rs b/crates/uv/src/commands/pip/install.rs index 51de2ab2e..d1790c697 100644 --- a/crates/uv/src/commands/pip/install.rs +++ b/crates/uv/src/commands/pip/install.rs @@ -7,6 +7,7 @@ use itertools::Itertools; use owo_colors::OwoColorize; use tracing::{debug, enabled, Level}; +use uv_auth::UrlAuthPolicies; use uv_cache::Cache; use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ @@ -356,6 +357,7 @@ pub(crate) async fn pip_install( .cache(cache.clone()) .index_urls(index_locations.index_urls()) .index_strategy(index_strategy) + .url_auth_policies(UrlAuthPolicies::from(&index_locations)) .torch_backend(torch_backend) .markers(interpreter.markers()) .platform(interpreter.platform()) diff --git a/crates/uv/src/commands/pip/list.rs b/crates/uv/src/commands/pip/list.rs index cebe2a739..3d458e1d7 100644 --- a/crates/uv/src/commands/pip/list.rs +++ b/crates/uv/src/commands/pip/list.rs @@ -11,6 +11,7 @@ use serde::Serialize; use tokio::sync::Semaphore; use unicode_width::UnicodeWidthStr; +use uv_auth::UrlAuthPolicies; use uv_cache::{Cache, Refresh}; use uv_cache_info::Timestamp; use uv_cli::ListFormat; @@ -90,6 +91,7 @@ pub(crate) async fn pip_list( .allow_insecure_host(network_settings.allow_insecure_host.clone()) .index_urls(index_locations.index_urls()) .index_strategy(index_strategy) + .url_auth_policies(UrlAuthPolicies::from(&index_locations)) .keyring(keyring_provider) .markers(environment.interpreter().markers()) .platform(environment.interpreter().platform()) diff --git a/crates/uv/src/commands/pip/sync.rs b/crates/uv/src/commands/pip/sync.rs index 09afa036d..d6cead51c 100644 --- a/crates/uv/src/commands/pip/sync.rs +++ b/crates/uv/src/commands/pip/sync.rs @@ -6,6 +6,7 @@ use anyhow::Result; use owo_colors::OwoColorize; use tracing::debug; +use uv_auth::UrlAuthPolicies; use uv_cache::Cache; use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ @@ -284,6 +285,7 @@ pub(crate) async fn pip_sync( .cache(cache.clone()) .index_urls(index_locations.index_urls()) .index_strategy(index_strategy) + .url_auth_policies(UrlAuthPolicies::from(&index_locations)) .torch_backend(torch_backend) .markers(interpreter.markers()) .platform(interpreter.platform()) diff --git a/crates/uv/src/commands/pip/tree.rs b/crates/uv/src/commands/pip/tree.rs index abd9ff44a..418c75fb3 100644 --- a/crates/uv/src/commands/pip/tree.rs +++ b/crates/uv/src/commands/pip/tree.rs @@ -10,6 +10,7 @@ use petgraph::Direction; use rustc_hash::{FxHashMap, FxHashSet}; use tokio::sync::Semaphore; +use uv_auth::UrlAuthPolicies; use uv_cache::{Cache, Refresh}; use uv_cache_info::Timestamp; use uv_client::RegistryClientBuilder; @@ -91,6 +92,7 @@ pub(crate) async fn pip_tree( .allow_insecure_host(network_settings.allow_insecure_host.clone()) .index_urls(index_locations.index_urls()) .index_strategy(index_strategy) + .url_auth_policies(UrlAuthPolicies::from(&index_locations)) .keyring(keyring_provider) .markers(environment.interpreter().markers()) .platform(environment.interpreter().platform()) diff --git a/crates/uv/src/commands/project/add.rs b/crates/uv/src/commands/project/add.rs index e645142fb..0b515e1be 100644 --- a/crates/uv/src/commands/project/add.rs +++ b/crates/uv/src/commands/project/add.rs @@ -13,6 +13,7 @@ use rustc_hash::{FxBuildHasher, FxHashMap}; use tracing::debug; use url::Url; +use uv_auth::UrlAuthPolicies; use uv_cache::Cache; use uv_cache_key::RepositoryUrl; use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder}; @@ -324,6 +325,7 @@ pub(crate) async fn add( let client = RegistryClientBuilder::try_from(client_builder)? .index_urls(settings.resolver.index_locations.index_urls()) .index_strategy(settings.resolver.index_strategy) + .url_auth_policies(UrlAuthPolicies::from(&settings.resolver.index_locations)) .markers(target.interpreter().markers()) .platform(target.interpreter().platform()) .build(); diff --git a/crates/uv/src/commands/venv.rs b/crates/uv/src/commands/venv.rs index 8ebb2ba79..bec84418d 100644 --- a/crates/uv/src/commands/venv.rs +++ b/crates/uv/src/commands/venv.rs @@ -10,6 +10,7 @@ use miette::{Diagnostic, IntoDiagnostic}; use owo_colors::OwoColorize; use thiserror::Error; +use uv_auth::UrlAuthPolicies; use uv_cache::Cache; use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ @@ -297,6 +298,7 @@ async fn venv_impl( .cache(cache.clone()) .index_urls(index_locations.index_urls()) .index_strategy(index_strategy) + .url_auth_policies(UrlAuthPolicies::from(index_locations)) .keyring(keyring_provider) .allow_insecure_host(network_settings.allow_insecure_host.clone()) .markers(interpreter.markers()) diff --git a/crates/uv/tests/it/edit.rs b/crates/uv/tests/it/edit.rs index dbcf6c07d..acbb5de17 100644 --- a/crates/uv/tests/it/edit.rs +++ b/crates/uv/tests/it/edit.rs @@ -10134,6 +10134,17 @@ fn add_auth_policy_always_without_credentials() -> Result<()> { Caused by: Missing credentials for https://pypi.org/simple/anyio/ " ); + + uv_snapshot!(context.pip_install().arg("black"), @r" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + error: Failed to fetch: `https://pypi.org/simple/black/` + Caused by: Missing credentials for https://pypi.org/simple/black/ + " + ); Ok(()) }