diff --git a/src/server/fetcher.rs b/src/server/fetcher.rs index b68f1590..a1205b7e 100644 --- a/src/server/fetcher.rs +++ b/src/server/fetcher.rs @@ -11,6 +11,8 @@ use super::{auth::HttpSignature, Context}; #[axum::async_trait] pub trait Fetcher { + async fn webfinger(&self, user: &str, host: &str) -> crate::Result; + async fn fetch_user(&self, id: &str) -> crate::Result; async fn pull_user(&self, id: &str) -> crate::Result; @@ -81,6 +83,36 @@ pub trait Fetcher { #[axum::async_trait] impl Fetcher for Context { + async fn webfinger(&self, user: &str, host: &str) -> crate::Result { + let subject = format!("acct:{user}@{host}"); + let webfinger_uri = format!("https://{host}/.well-known/webfinger?resource={subject}"); + let resource = Self::request( + Method::GET, &webfinger_uri, None, &self.base(), &self.app().private_key, self.domain(), + ) + .await? + .json::() + .await?; + + if resource.subject != subject { + return Err(UpubError::unprocessable()); + } + + for link in resource.links { + if link.rel == "self" { + if let Some(href) = link.href { + return Ok(href); + } + } + } + + if let Some(alias) = resource.aliases.into_iter().next() { + return Ok(alias); + } + + Err(UpubError::not_found()) + } + + async fn fetch_user(&self, id: &str) -> crate::Result { if let Some(x) = model::user::Entity::find_by_id(id).one(self.db()).await? { return Ok(x); // already in db, easy