Compare commits

..

2 commits

Author SHA1 Message Date
8a1afadea0
feat: logged in user can search by webfinger
more expensive than "localized" +..@..@ urls because must always make
one extra request but allows easier searching
2024-05-13 01:48:51 +02:00
f2867e56e7
feat: webfinger resolution in fetcher 2024-05-13 01:48:32 +02:00
2 changed files with 39 additions and 2 deletions

View file

@ -7,7 +7,7 @@ pub mod following;
use axum::extract::{Path, Query, State}; use axum::extract::{Path, Query, State};
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QuerySelect, SelectColumns}; use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QuerySelect, SelectColumns};
use apb::{ActorMut, Node, ObjectMut}; use apb::{ActorMut, Node};
use crate::{errors::UpubError, model::{self, user}, server::{auth::AuthIdentity, fetcher::Fetcher, Context}, url}; use crate::{errors::UpubError, model::{self, user}, server::{auth::AuthIdentity, fetcher::Fetcher, Context}, url};
use super::{jsonld::LD, JsonLD, TryFetch}; use super::{jsonld::LD, JsonLD, TryFetch};
@ -19,8 +19,13 @@ pub async fn view(
Path(id): Path<String>, Path(id): Path<String>,
Query(query): Query<TryFetch>, Query(query): Query<TryFetch>,
) -> crate::Result<JsonLD<serde_json::Value>> { ) -> crate::Result<JsonLD<serde_json::Value>> {
let uid = ctx.uri("users", id.clone()); let mut uid = ctx.uri("users", id.clone());
if auth.is_local() && query.fetch && !ctx.is_local(&uid) { if auth.is_local() && query.fetch && !ctx.is_local(&uid) {
if id.starts_with('@') {
if let Some((user, host)) = id.replacen('@', "", 1).split_once('@') {
uid = ctx.webfinger(user, host).await?;
}
}
ctx.fetch_user(&uid).await?; ctx.fetch_user(&uid).await?;
} }

View file

@ -11,6 +11,8 @@ use super::{auth::HttpSignature, Context};
#[axum::async_trait] #[axum::async_trait]
pub trait Fetcher { pub trait Fetcher {
async fn webfinger(&self, user: &str, host: &str) -> crate::Result<String>;
async fn fetch_user(&self, id: &str) -> crate::Result<model::user::Model>; async fn fetch_user(&self, id: &str) -> crate::Result<model::user::Model>;
async fn pull_user(&self, id: &str) -> crate::Result<model::user::Model>; async fn pull_user(&self, id: &str) -> crate::Result<model::user::Model>;
@ -81,6 +83,36 @@ pub trait Fetcher {
#[axum::async_trait] #[axum::async_trait]
impl Fetcher for Context { impl Fetcher for Context {
async fn webfinger(&self, user: &str, host: &str) -> crate::Result<String> {
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::<jrd::JsonResourceDescriptor>()
.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<model::user::Model> { async fn fetch_user(&self, id: &str) -> crate::Result<model::user::Model> {
if let Some(x) = model::user::Entity::find_by_id(id).one(self.db()).await? { if let Some(x) = model::user::Entity::find_by_id(id).one(self.db()).await? {
return Ok(x); // already in db, easy return Ok(x); // already in db, easy