Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/uv-auth/src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ impl Indexes {
index_urls
}

/// Get the index URL prefix for a URL if one exists.
pub fn index_url_for(&self, url: &Url) -> Option<&DisplaySafeUrl> {
self.find_prefix_index(url).map(|index| &index.url)
/// Get the index for a URL if one exists.
pub fn index_for(&self, url: &Url) -> Option<&Index> {
self.find_prefix_index(url)
}

/// Get the [`AuthPolicy`] for a URL.
Expand Down
56 changes: 32 additions & 24 deletions crates/uv-auth/src/middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::{
realm::Realm,
};

use crate::{TextCredentialStore, TomlCredentialError};
use crate::{Index, TextCredentialStore, TomlCredentialError};

/// Strategy for loading netrc files.
enum NetrcMode {
Expand Down Expand Up @@ -297,7 +297,7 @@ 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());
let maybe_index_url = self.indexes.index_url_for(request.url());
let index = self.indexes.index_for(request.url());
let auth_policy = self.indexes.auth_policy_for(request.url());
trace!("Handling request for {url} with authentication policy {auth_policy}");

Expand All @@ -312,7 +312,7 @@ impl Middleware for AuthMiddleware {
extensions,
next,
&url,
maybe_index_url,
index,
auth_policy,
)
.await;
Expand Down Expand Up @@ -406,8 +406,8 @@ impl Middleware for AuthMiddleware {
.as_ref()
.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).or_else(|| {
let credentials = if let Some(index) = index {
self.cache().get_url(&index.url, &username).or_else(|| {
self.cache()
.get_realm(Realm::from(&**retry_request_url), username)
})
Expand Down Expand Up @@ -435,7 +435,7 @@ impl Middleware for AuthMiddleware {
.fetch_credentials(
credentials.as_deref(),
retry_request_url,
maybe_index_url,
index,
auth_policy,
)
.await
Expand Down Expand Up @@ -529,7 +529,7 @@ impl AuthMiddleware {
extensions: &mut Extensions,
next: Next<'_>,
url: &DisplaySafeUrl,
index_url: Option<&DisplaySafeUrl>,
index: Option<&Index>,
auth_policy: AuthPolicy,
) -> reqwest_middleware::Result<Response> {
let credentials = Arc::new(credentials);
Expand All @@ -545,11 +545,15 @@ impl AuthMiddleware {
trace!("Request for {url} is missing a password, looking for credentials");

// There's just a username, try to find a password.
// If we have an index URL, check the cache for that URL. Otherwise,
// If we have an index, check the cache for that URL. Otherwise,
// check for the realm.
let maybe_cached_credentials = if let Some(index_url) = index_url {
let maybe_cached_credentials = if let Some(index) = index {
self.cache()
.get_url(index_url, credentials.as_username().as_ref())
.get_url(&index.url, credentials.as_username().as_ref())
.or_else(|| {
self.cache()
.get_url(&index.root_url, credentials.as_username().as_ref())
})
} else {
self.cache()
.get_realm(Realm::from(request.url()), credentials.to_username())
Expand All @@ -574,14 +578,14 @@ impl AuthMiddleware {
.fetch_credentials(
Some(&credentials),
DisplaySafeUrl::ref_cast(request.url()),
index_url,
index,
auth_policy,
)
.await
{
request = credentials.authenticate(request);
Some(credentials)
} else if index_url.is_some() {
} else if index.is_some() {
// If this is a known index, we fall back to checking for the realm.
if let Some(credentials) = self
.cache()
Expand All @@ -608,7 +612,7 @@ impl AuthMiddleware {
&self,
credentials: Option<&Credentials>,
url: &DisplaySafeUrl,
maybe_index_url: Option<&DisplaySafeUrl>,
index: Option<&Index>,
auth_policy: AuthPolicy,
) -> Option<Arc<Credentials>> {
let username = Username::from(
Expand All @@ -617,8 +621,8 @@ impl AuthMiddleware {

// Fetches can be expensive, so we will only run them _once_ per realm or index URL and username combination
// All other requests for the same realm or index URL will wait until the first one completes
let key = if let Some(index_url) = maybe_index_url {
(FetchUrl::Index(index_url.clone()), username)
let key = if let Some(index) = index {
(FetchUrl::Index(index.url.clone()), username)
} else {
(FetchUrl::Realm(Realm::from(&**url)), username)
};
Expand Down Expand Up @@ -719,13 +723,16 @@ impl AuthMiddleware {
} else {
String::new()
};
if let Some(index_url) = maybe_index_url {
debug!("Checking native store for credentials for index URL {}{}", display_username, index_url);
native_store.fetch(DisplaySafeUrl::ref_cast(index_url), username).await
if let Some(index) = index {
// N.B. The native store performs an exact look up right now, so we use the root
// URL of the index instead of relying on prefix-matching.
debug!("Checking native store for credentials for index URL {}{}", display_username, index.root_url);
native_store.fetch(&index.root_url, username).await
} else {
debug!("Checking native store for credentials for URL {}{}", display_username, url);
native_store.fetch(url, username).await
}
// TODO(zanieb): We should have a realm fallback here too
} else {
None
}
Expand All @@ -742,19 +749,20 @@ impl AuthMiddleware {
// URLs; instead, we fetch if there's a username or if the user has requested to
// always authenticate.
if let Some(username) = credentials.and_then(|credentials| credentials.username()) {
if let Some(index_url) = maybe_index_url {
debug!("Checking keyring for credentials for index URL {}@{}", username, index_url);
keyring.fetch(DisplaySafeUrl::ref_cast(index_url), Some(username)).await
if let Some(index) = index {
debug!("Checking keyring for credentials for index URL {}@{}", username, index.url);
keyring.fetch(DisplaySafeUrl::ref_cast(&index.url), Some(username)).await
} else {
debug!("Checking keyring for credentials for full URL {}@{}", username, url);
keyring.fetch(url, Some(username)).await
}
} else if matches!(auth_policy, AuthPolicy::Always) {
if let Some(index_url) = maybe_index_url {
if let Some(index) = index {
debug!(
"Checking keyring for credentials for index URL {index_url} without username due to `authenticate = always`"
"Checking keyring for credentials for index URL {} without username due to `authenticate = always`",
index.url
);
keyring.fetch(DisplaySafeUrl::ref_cast(index_url), None).await
keyring.fetch(DisplaySafeUrl::ref_cast(&index.url), None).await
} else {
None
}
Expand Down
15 changes: 8 additions & 7 deletions crates/uv/tests/it/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,17 @@ fn add_package_native_auth() -> Result<()> {
// credentials storied in the system keyring.
uv_snapshot!(context.add().arg("anyio").arg("--default-index").arg("https://public@pypi-proxy.fly.dev/basic-auth/simple")
.env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
success: false
exit_code: 1
success: true
exit_code: 0
----- stdout -----

----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because anyio was not found in the package registry and your project depends on anyio, 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).
help: If you want to add the package regardless of the failed resolution, provide the `--frozen` flag to skip locking and syncing.
Resolved 4 packages in [TIME]
Prepared 3 packages in [TIME]
Installed 3 packages in [TIME]
+ anyio==4.3.0
+ idna==3.6
+ sniffio==1.3.1
"
);

Expand Down
Loading