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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/uv-auth/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ workspace = true

[dependencies]
uv-once-map = { workspace = true }
uv-small-str = { workspace = true }
uv-static = { workspace = true }

anyhow = { workspace = true }
Expand Down
41 changes: 33 additions & 8 deletions crates/uv-auth/src/cache.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::fmt::Formatter;
use std::hash::BuildHasherDefault;
use std::sync::Arc;
use std::sync::RwLock;
Expand Down Expand Up @@ -41,26 +42,30 @@ impl CredentialsCache {
/// Return the credentials that should be used for a realm and username, if any.
pub(crate) fn get_realm(&self, realm: Realm, username: Username) -> Option<Arc<Credentials>> {
let realms = self.realms.read().unwrap();
let name = if let Some(username) = username.as_deref() {
format!("{username}@{realm}")
} else {
realm.to_string()
};
let given_username = username.is_some();
let key = (realm, username);

let Some(credentials) = realms.get(&key).cloned() else {
trace!("No credentials in cache for realm {name}");
trace!(
"No credentials in cache for realm {}",
RealmUsername::from(key)
);
return None;
};

if given_username && credentials.password().is_none() {
// If given a username, don't return password-less credentials
trace!("No password in cache for realm {name}");
trace!(
"No password in cache for realm {}",
RealmUsername::from(key)
);
return None;
}

trace!("Found cached credentials for realm {name}");
trace!(
"Found cached credentials for realm {}",
RealmUsername::from(key)
);
Some(credentials)
}

Expand Down Expand Up @@ -214,6 +219,26 @@ impl TrieState {
}
}

#[derive(Debug)]
struct RealmUsername(Realm, Username);

impl std::fmt::Display for RealmUsername {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let Self(realm, username) = self;
if let Some(username) = username.as_deref() {
write!(f, "{username}@{realm}")
} else {
write!(f, "{realm}")
}
}
}

impl From<(Realm, Username)> for RealmUsername {
fn from((realm, username): (Realm, Username)) -> Self {
Self(realm, username)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
4 changes: 4 additions & 0 deletions crates/uv-auth/src/credentials.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ impl Credentials {
self.username.clone()
}

pub(crate) fn as_username(&self) -> &Username {
&self.username
}

pub fn password(&self) -> Option<&str> {
self.password.as_deref()
}
Expand Down
2 changes: 1 addition & 1 deletion crates/uv-auth/src/middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ impl Middleware for AuthMiddleware {
None
} else if let Some(credentials) = self
.cache()
.get_url(request.url(), &credentials.to_username())
.get_url(request.url(), credentials.as_username())
{
request = credentials.authenticate(request);
// Do not insert already-cached credentials
Expand Down
9 changes: 5 additions & 4 deletions crates/uv-auth/src/realm.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{fmt::Display, fmt::Formatter};

use url::Url;
use uv_small_str::SmallString;

/// Used to determine if authentication information should be retained on a new URL.
/// Based on the specification defined in RFC 7235 and 7230.
Expand All @@ -23,16 +24,16 @@ use url::Url;
// so we do not need any special handling here.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct Realm {
scheme: String,
host: Option<String>,
scheme: SmallString,
host: Option<SmallString>,
port: Option<u16>,
}

impl From<&Url> for Realm {
fn from(url: &Url) -> Self {
Self {
scheme: url.scheme().to_string(),
host: url.host_str().map(str::to_string),
scheme: SmallString::from(url.scheme()),
host: url.host_str().map(SmallString::from),
port: url.port(),
}
}
Expand Down
Loading