feat: allow resolving webfinger for remote users
returned JRDs are already expired, indicating that we can't really be trusted for remote users, go fetch their webfinger server instead
This commit is contained in:
parent
e146dc2a51
commit
ebb7d77cae
1 changed files with 64 additions and 36 deletions
|
@ -1,8 +1,8 @@
|
||||||
use axum::{extract::{Path, Query, State}, http::StatusCode, response::{IntoResponse, Response}, Json};
|
use axum::{extract::{Path, Query, State}, http::StatusCode, response::{IntoResponse, Response}, Json};
|
||||||
use jrd::{JsonResourceDescriptor, JsonResourceDescriptorLink};
|
use jrd::{JsonResourceDescriptor, JsonResourceDescriptorLink};
|
||||||
use sea_orm::{EntityTrait, PaginatorTrait};
|
use sea_orm::{ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter};
|
||||||
|
|
||||||
use crate::{model, server::Context, url, VERSION};
|
use crate::{errors::UpubError, model, server::Context, url, VERSION};
|
||||||
|
|
||||||
#[derive(serde::Serialize)]
|
#[derive(serde::Serialize)]
|
||||||
pub struct NodeInfoDiscovery {
|
pub struct NodeInfoDiscovery {
|
||||||
|
@ -96,57 +96,85 @@ impl<T: serde::Serialize> IntoResponse for JsonRD<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn webfinger(State(ctx): State<Context>, Query(query): Query<WebfingerQuery>) -> Result<JsonRD<JsonResourceDescriptor>, StatusCode> {
|
pub async fn webfinger(State(ctx): State<Context>, Query(query): Query<WebfingerQuery>) -> crate::Result<JsonRD<JsonResourceDescriptor>> {
|
||||||
if let Some((user, domain)) = query
|
if let Some((user, domain)) = query
|
||||||
.resource
|
.resource
|
||||||
.replace("acct:", "")
|
.replace("acct:", "")
|
||||||
.split_once('@')
|
.split_once('@')
|
||||||
{
|
{
|
||||||
if user == ctx.domain() && domain == ctx.domain() {
|
if domain == ctx.domain() {
|
||||||
return Ok(JsonRD(JsonResourceDescriptor {
|
if user == ctx.domain() {
|
||||||
|
// we fetch with our domain as user, they are checking us back, this is a special edge case
|
||||||
|
Ok(JsonRD(JsonResourceDescriptor {
|
||||||
|
subject: format!("acct:{user}@{domain}"),
|
||||||
|
aliases: vec![ctx.base().to_string()],
|
||||||
|
links: vec![
|
||||||
|
JsonResourceDescriptorLink {
|
||||||
|
rel: "self".to_string(),
|
||||||
|
link_type: Some("application/ld+json".to_string()),
|
||||||
|
href: Some(ctx.base().to_string()),
|
||||||
|
properties: jrd::Map::default(),
|
||||||
|
titles: jrd::Map::default(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
expires: None,
|
||||||
|
properties: jrd::Map::default(),
|
||||||
|
}))
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// local user
|
||||||
|
let uid = ctx.uid(user);
|
||||||
|
let usr = model::user::Entity::find_by_id(uid)
|
||||||
|
.one(ctx.db())
|
||||||
|
.await?
|
||||||
|
.ok_or_else(UpubError::not_found)?;
|
||||||
|
|
||||||
|
Ok(JsonRD(JsonResourceDescriptor {
|
||||||
|
subject: format!("acct:{user}@{domain}"),
|
||||||
|
aliases: vec![usr.id.clone()],
|
||||||
|
links: vec![
|
||||||
|
JsonResourceDescriptorLink {
|
||||||
|
rel: "self".to_string(),
|
||||||
|
link_type: Some("application/ld+json".to_string()),
|
||||||
|
href: Some(usr.id),
|
||||||
|
properties: jrd::Map::default(),
|
||||||
|
titles: jrd::Map::default(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
expires: None,
|
||||||
|
properties: jrd::Map::default(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// remote user
|
||||||
|
let usr = model::user::Entity::find()
|
||||||
|
.filter(model::user::Column::PreferredUsername.eq(user))
|
||||||
|
.filter(model::user::Column::Domain.eq(domain))
|
||||||
|
.one(ctx.db())
|
||||||
|
.await?
|
||||||
|
.ok_or_else(UpubError::not_found)?;
|
||||||
|
|
||||||
|
Ok(JsonRD(JsonResourceDescriptor {
|
||||||
subject: format!("acct:{user}@{domain}"),
|
subject: format!("acct:{user}@{domain}"),
|
||||||
aliases: vec![ctx.base().to_string()],
|
aliases: vec![usr.id.clone()],
|
||||||
links: vec![
|
links: vec![
|
||||||
JsonResourceDescriptorLink {
|
JsonResourceDescriptorLink {
|
||||||
rel: "self".to_string(),
|
rel: "self".to_string(),
|
||||||
link_type: Some("application/ld+json".to_string()),
|
link_type: Some("application/ld+json".to_string()),
|
||||||
href: Some(ctx.base().to_string()),
|
href: Some(usr.id),
|
||||||
properties: jrd::Map::default(),
|
properties: jrd::Map::default(),
|
||||||
titles: jrd::Map::default(),
|
titles: jrd::Map::default(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
expires: None,
|
|
||||||
properties: jrd::Map::default(),
|
properties: jrd::Map::default(),
|
||||||
}));
|
// we are no authority on local users, this info should be considered already outdated,
|
||||||
}
|
// but can still be relevant, for example for our frontend
|
||||||
let uid = ctx.uid(user);
|
expires: Some(chrono::Utc::now()),
|
||||||
match model::user::Entity::find_by_id(uid)
|
}))
|
||||||
.one(ctx.db())
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(Some(x)) => Ok(JsonRD(JsonResourceDescriptor {
|
|
||||||
subject: format!("acct:{user}@{domain}"),
|
|
||||||
aliases: vec![x.id.clone()],
|
|
||||||
links: vec![
|
|
||||||
JsonResourceDescriptorLink {
|
|
||||||
rel: "self".to_string(),
|
|
||||||
link_type: Some("application/ld+json".to_string()),
|
|
||||||
href: Some(x.id),
|
|
||||||
properties: jrd::Map::default(),
|
|
||||||
titles: jrd::Map::default(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
expires: None,
|
|
||||||
properties: jrd::Map::default(),
|
|
||||||
})),
|
|
||||||
Ok(None) => Err(StatusCode::NOT_FOUND),
|
|
||||||
Err(e) => {
|
|
||||||
tracing::error!("error executing webfinger query: {e}");
|
|
||||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(StatusCode::UNPROCESSABLE_ENTITY)
|
Err(StatusCode::UNPROCESSABLE_ENTITY.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue