Skip to content

Commit 88e5e29

Browse files
committed
Use the root index URL when retrieving credentials from the native store
1 parent 5aec0d8 commit 88e5e29

File tree

3 files changed

+43
-34
lines changed

3 files changed

+43
-34
lines changed

crates/uv-auth/src/index.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@ impl Indexes {
9595
index_urls
9696
}
9797

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

103103
/// Get the [`AuthPolicy`] for a URL.

crates/uv-auth/src/middleware.rs

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate::{
2222
realm::Realm,
2323
};
2424

25-
use crate::{TextCredentialStore, TomlCredentialError};
25+
use crate::{Index, TextCredentialStore, TomlCredentialError};
2626

2727
/// Strategy for loading netrc files.
2828
enum NetrcMode {
@@ -297,7 +297,7 @@ impl Middleware for AuthMiddleware {
297297
// In the middleware, existing credentials are already moved from the URL
298298
// to the headers so for display purposes we restore some information
299299
let url = tracing_url(&request, request_credentials.as_ref());
300-
let maybe_index_url = self.indexes.index_url_for(request.url());
300+
let index = self.indexes.index_for(request.url());
301301
let auth_policy = self.indexes.auth_policy_for(request.url());
302302
trace!("Handling request for {url} with authentication policy {auth_policy}");
303303

@@ -312,7 +312,7 @@ impl Middleware for AuthMiddleware {
312312
extensions,
313313
next,
314314
&url,
315-
maybe_index_url,
315+
index,
316316
auth_policy,
317317
)
318318
.await;
@@ -406,8 +406,8 @@ impl Middleware for AuthMiddleware {
406406
.as_ref()
407407
.map(|credentials| credentials.to_username())
408408
.unwrap_or(Username::none());
409-
let credentials = if let Some(index_url) = maybe_index_url {
410-
self.cache().get_url(index_url, &username).or_else(|| {
409+
let credentials = if let Some(index) = index {
410+
self.cache().get_url(&index.url, &username).or_else(|| {
411411
self.cache()
412412
.get_realm(Realm::from(&**retry_request_url), username)
413413
})
@@ -435,7 +435,7 @@ impl Middleware for AuthMiddleware {
435435
.fetch_credentials(
436436
credentials.as_deref(),
437437
retry_request_url,
438-
maybe_index_url,
438+
index,
439439
auth_policy,
440440
)
441441
.await
@@ -529,7 +529,7 @@ impl AuthMiddleware {
529529
extensions: &mut Extensions,
530530
next: Next<'_>,
531531
url: &DisplaySafeUrl,
532-
index_url: Option<&DisplaySafeUrl>,
532+
index: Option<&Index>,
533533
auth_policy: AuthPolicy,
534534
) -> reqwest_middleware::Result<Response> {
535535
let credentials = Arc::new(credentials);
@@ -545,11 +545,15 @@ impl AuthMiddleware {
545545
trace!("Request for {url} is missing a password, looking for credentials");
546546

547547
// There's just a username, try to find a password.
548-
// If we have an index URL, check the cache for that URL. Otherwise,
548+
// If we have an index, check the cache for that URL. Otherwise,
549549
// check for the realm.
550-
let maybe_cached_credentials = if let Some(index_url) = index_url {
550+
let maybe_cached_credentials = if let Some(index) = index {
551551
self.cache()
552-
.get_url(index_url, credentials.as_username().as_ref())
552+
.get_url(&index.url, credentials.as_username().as_ref())
553+
.or_else(|| {
554+
self.cache()
555+
.get_url(&index.root_url, credentials.as_username().as_ref())
556+
})
553557
} else {
554558
self.cache()
555559
.get_realm(Realm::from(request.url()), credentials.to_username())
@@ -574,14 +578,14 @@ impl AuthMiddleware {
574578
.fetch_credentials(
575579
Some(&credentials),
576580
DisplaySafeUrl::ref_cast(request.url()),
577-
index_url,
581+
index,
578582
auth_policy,
579583
)
580584
.await
581585
{
582586
request = credentials.authenticate(request);
583587
Some(credentials)
584-
} else if index_url.is_some() {
588+
} else if index.is_some() {
585589
// If this is a known index, we fall back to checking for the realm.
586590
if let Some(credentials) = self
587591
.cache()
@@ -608,7 +612,7 @@ impl AuthMiddleware {
608612
&self,
609613
credentials: Option<&Credentials>,
610614
url: &DisplaySafeUrl,
611-
maybe_index_url: Option<&DisplaySafeUrl>,
615+
index: Option<&Index>,
612616
auth_policy: AuthPolicy,
613617
) -> Option<Arc<Credentials>> {
614618
let username = Username::from(
@@ -617,8 +621,8 @@ impl AuthMiddleware {
617621

618622
// Fetches can be expensive, so we will only run them _once_ per realm or index URL and username combination
619623
// All other requests for the same realm or index URL will wait until the first one completes
620-
let key = if let Some(index_url) = maybe_index_url {
621-
(FetchUrl::Index(index_url.clone()), username)
624+
let key = if let Some(index) = index {
625+
(FetchUrl::Index(index.url.clone()), username)
622626
} else {
623627
(FetchUrl::Realm(Realm::from(&**url)), username)
624628
};
@@ -719,13 +723,16 @@ impl AuthMiddleware {
719723
} else {
720724
String::new()
721725
};
722-
if let Some(index_url) = maybe_index_url {
723-
debug!("Checking native store for credentials for index URL {}{}", display_username, index_url);
724-
native_store.fetch(DisplaySafeUrl::ref_cast(index_url), username).await
726+
if let Some(index) = index {
727+
// N.B. The native store performs an exact look up right now, so we use the root
728+
// URL of the index instead of relying on prefix-matching.
729+
debug!("Checking native store for credentials for index URL {}{}", display_username, index.root_url);
730+
native_store.fetch(&index.root_url, username).await
725731
} else {
726732
debug!("Checking native store for credentials for URL {}{}", display_username, url);
727733
native_store.fetch(url, username).await
728734
}
735+
// TODO(zanieb): We should have a realm fallback here too
729736
} else {
730737
None
731738
}
@@ -742,19 +749,20 @@ impl AuthMiddleware {
742749
// URLs; instead, we fetch if there's a username or if the user has requested to
743750
// always authenticate.
744751
if let Some(username) = credentials.and_then(|credentials| credentials.username()) {
745-
if let Some(index_url) = maybe_index_url {
746-
debug!("Checking keyring for credentials for index URL {}@{}", username, index_url);
747-
keyring.fetch(DisplaySafeUrl::ref_cast(index_url), Some(username)).await
752+
if let Some(index) = index {
753+
debug!("Checking keyring for credentials for index URL {}@{}", username, index.url);
754+
keyring.fetch(DisplaySafeUrl::ref_cast(&index.url), Some(username)).await
748755
} else {
749756
debug!("Checking keyring for credentials for full URL {}@{}", username, url);
750757
keyring.fetch(url, Some(username)).await
751758
}
752759
} else if matches!(auth_policy, AuthPolicy::Always) {
753-
if let Some(index_url) = maybe_index_url {
760+
if let Some(index) = index {
754761
debug!(
755-
"Checking keyring for credentials for index URL {index_url} without username due to `authenticate = always`"
762+
"Checking keyring for credentials for index URL {} without username due to `authenticate = always`",
763+
index.url
756764
);
757-
keyring.fetch(DisplaySafeUrl::ref_cast(index_url), None).await
765+
keyring.fetch(DisplaySafeUrl::ref_cast(&index.url), None).await
758766
} else {
759767
None
760768
}

crates/uv/tests/it/auth.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,17 @@ fn add_package_native_auth() -> Result<()> {
6868
// credentials storied in the system keyring.
6969
uv_snapshot!(context.add().arg("anyio").arg("--default-index").arg("https://public@pypi-proxy.fly.dev/basic-auth/simple")
7070
.env(EnvVars::UV_PREVIEW_FEATURES, "native-auth"), @r"
71-
success: false
72-
exit_code: 1
71+
success: true
72+
exit_code: 0
7373
----- stdout -----
7474
7575
----- stderr -----
76-
× No solution found when resolving dependencies:
77-
╰─▶ 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.
78-
79-
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).
80-
help: If you want to add the package regardless of the failed resolution, provide the `--frozen` flag to skip locking and syncing.
76+
Resolved 4 packages in [TIME]
77+
Prepared 3 packages in [TIME]
78+
Installed 3 packages in [TIME]
79+
+ anyio==4.3.0
80+
+ idna==3.6
81+
+ sniffio==1.3.1
8182
"
8283
);
8384

0 commit comments

Comments
 (0)