From 6e9b1596d88db3ed4497af9150c3c2a5c62b3b34 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Fri, 15 May 2026 03:12:52 +0200 Subject: [PATCH] =?UTF-8?q?refactor:=20move=20UUID/username=20routing=20to?= =?UTF-8?q?=20application=20use=20case=20=E2=80=94=20fix=20handler=20bound?= =?UTF-8?q?ary=20leak?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/application/src/use_cases/profile.rs | 15 +++++++++++++++ crates/presentation/src/handlers/users.rs | 13 ++----------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/crates/application/src/use_cases/profile.rs b/crates/application/src/use_cases/profile.rs index 773a043..3b09e44 100644 --- a/crates/application/src/use_cases/profile.rs +++ b/crates/application/src/use_cases/profile.rs @@ -23,6 +23,21 @@ pub async fn get_user_by_username( .ok_or(DomainError::NotFound) } +/// Resolve a path segment that is either a UUID (AP actor URL) or a username. +pub async fn get_user_by_id_or_username( + users: &dyn UserRepository, + id_or_username: &str, +) -> Result { + if let Ok(uuid) = uuid::Uuid::parse_str(id_or_username) { + users + .find_by_id(&UserId::from_uuid(uuid)) + .await? + .ok_or(DomainError::NotFound) + } else { + get_user_by_username(users, id_or_username).await + } +} + pub async fn update_profile( users: &dyn UserRepository, user_id: &UserId, diff --git a/crates/presentation/src/handlers/users.rs b/crates/presentation/src/handlers/users.rs index 1419300..1af4545 100644 --- a/crates/presentation/src/handlers/users.rs +++ b/crates/presentation/src/handlers/users.rs @@ -9,7 +9,7 @@ use api_types::{ responses::{ErrorResponse, ProfileField, RemoteActorResponse, UserResponse}, }; use application::use_cases::feed::list_users; -use application::use_cases::profile::{get_user_by_username, update_profile}; +use application::use_cases::profile::{get_user_by_id_or_username, update_profile}; use application::use_cases::search::search_users; use axum::{ extract::{Path, Query, State}, @@ -32,16 +32,7 @@ pub async fn get_user( OptionalAuthUser(viewer): OptionalAuthUser, headers: HeaderMap, ) -> Result { - // AP actor URLs use the user's UUID (e.g. /users/{uuid}). Fall back to UUID lookup - // so remote servers can fetch the actor JSON for HTTP signature verification. - let user = if let Ok(uuid) = uuid::Uuid::parse_str(&username) { - s.users - .find_by_id(&domain::value_objects::UserId::from_uuid(uuid)) - .await? - .ok_or(ApiError::Domain(domain::errors::DomainError::NotFound))? - } else { - get_user_by_username(&*s.users, &username).await? - }; + let user = get_user_by_id_or_username(&*s.users, &username).await?; let accept = headers .get(header::ACCEPT)