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
27 changes: 20 additions & 7 deletions crates/uv-auth/src/middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ impl Middleware for AuthMiddleware {
extensions,
next,
&url,
auth_policy,
)
.await;
}
Expand All @@ -212,7 +213,9 @@ impl Middleware for AuthMiddleware {
// If it's fully authenticated, finish the request
if credentials.password().is_some() {
trace!("Request for {url} is fully authenticated");
return self.complete_request(None, request, extensions, next).await;
return self
.complete_request(None, request, extensions, next, auth_policy)
.await;
}

// If we just found a username, we'll make the request then look for password elsewhere
Expand Down Expand Up @@ -286,7 +289,7 @@ impl Middleware for AuthMiddleware {
trace!("Retrying request for {url} with credentials from cache {credentials:?}");
retry_request = credentials.authenticate(retry_request);
return self
.complete_request(None, retry_request, extensions, next)
.complete_request(None, retry_request, extensions, next, auth_policy)
.await;
}
}
Expand All @@ -300,7 +303,13 @@ impl Middleware for AuthMiddleware {
retry_request = credentials.authenticate(retry_request);
trace!("Retrying request for {url} with {credentials:?}");
return self
.complete_request(Some(credentials), retry_request, extensions, next)
.complete_request(
Some(credentials),
retry_request,
extensions,
next,
auth_policy,
)
.await;
}

Expand All @@ -309,7 +318,7 @@ impl Middleware for AuthMiddleware {
trace!("Retrying request for {url} with username from cache {credentials:?}");
retry_request = credentials.authenticate(retry_request);
return self
.complete_request(None, retry_request, extensions, next)
.complete_request(None, retry_request, extensions, next, auth_policy)
.await;
}
}
Expand All @@ -334,13 +343,16 @@ impl AuthMiddleware {
request: Request,
extensions: &mut Extensions,
next: Next<'_>,
auth_policy: AuthPolicy,
) -> reqwest_middleware::Result<Response> {
let Some(credentials) = credentials else {
// Nothing to insert into the cache if we don't have credentials
return next.run(request, extensions).await;
};

let url = request.url().clone();
if matches!(auth_policy, AuthPolicy::Always) && credentials.password().is_none() {
return Err(Error::Middleware(format_err!("Missing password for {url}")));
}
let result = next.run(request, extensions).await;

// Update the cache with new credentials on a successful request
Expand All @@ -363,14 +375,15 @@ impl AuthMiddleware {
extensions: &mut Extensions,
next: Next<'_>,
url: &str,
auth_policy: AuthPolicy,
) -> reqwest_middleware::Result<Response> {
let credentials = Arc::new(credentials);

// If there's a password, send the request and cache
if credentials.password().is_some() {
trace!("Request for {url} is already fully authenticated");
return self
.complete_request(Some(credentials), request, extensions, next)
.complete_request(Some(credentials), request, extensions, next, auth_policy)
.await;
}

Expand Down Expand Up @@ -402,7 +415,7 @@ impl AuthMiddleware {
};

return self
.complete_request(credentials, request, extensions, next)
.complete_request(credentials, request, extensions, next, auth_policy)
.await;
}

Expand Down
35 changes: 35 additions & 0 deletions crates/uv/tests/it/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10081,6 +10081,41 @@ fn add_auth_policy_always_without_credentials() -> Result<()> {
Ok(())
}

/// In authentication "always", authenticated requests with a username but
/// no discoverable password will fail.
#[test]
fn add_auth_policy_always_with_username_no_password() -> Result<()> {
let context = TestContext::new("3.12");

let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(indoc! { r#"
[project]
name = "foo"
version = "1.0.0"
requires-python = ">=3.11, <4"
dependencies = []

[[tool.uv.index]]
name = "my-index"
url = "https://[email protected]/simple"
authenticate = "always"
default = true
"#
})?;

uv_snapshot!(context.add().arg("anyio"), @r"
success: false
exit_code: 2
----- stdout -----

----- stderr -----
error: Failed to fetch: `https://pypi.org/simple/anyio/`
Caused by: Missing password for https://pypi.org/simple/anyio/
"
);
Ok(())
}

/// In authentication "never", even if the correct credentials are supplied
/// in the URL, no authenticated requests will be allowed.
#[test]
Expand Down
Loading