2024-11-16 05:16:52 +00:00
|
|
|
use std::sync::Arc;
|
|
|
|
use axum::Extension;
|
2024-11-15 17:12:13 +00:00
|
|
|
use axum::http::{HeaderMap, Response, StatusCode};
|
2024-11-14 03:02:40 +00:00
|
|
|
use axum_extra::extract::Query;
|
|
|
|
use serde::Deserialize;
|
2024-11-18 05:13:49 +00:00
|
|
|
use acrate_database::diesel::GroupedBy;
|
|
|
|
use acrate_database::diesel_async::{AsyncConnection, AsyncPgConnection};
|
|
|
|
use acrate_database::meta::{MetaAlias, MetaLink, MetaLinkProperty, MetaLinkTitle, MetaProperty, MetaSubject};
|
2024-11-19 02:31:07 +00:00
|
|
|
use acrate_rd::jrd::ResourceDescriptorLinkJRD;
|
|
|
|
use acrate_rd::xrd::{ResourceDescriptorLinkXRD, ResourceDescriptorPropertyXRD, ResourceDescriptorTitleXRD};
|
2024-11-15 05:50:51 +00:00
|
|
|
use crate::config;
|
2024-11-14 03:02:40 +00:00
|
|
|
|
|
|
|
#[derive(Debug, Clone, Deserialize)]
|
|
|
|
pub struct WebfingerQuery {
|
2024-11-15 17:12:13 +00:00
|
|
|
pub resource: Option<String>,
|
2024-11-14 03:02:40 +00:00
|
|
|
|
|
|
|
#[serde(default)]
|
|
|
|
pub rel: Vec<String>,
|
|
|
|
}
|
|
|
|
|
2024-11-15 05:50:51 +00:00
|
|
|
const WEBFINGER_DOC: &str = "/.well-known/webfinger";
|
|
|
|
|
2024-11-14 03:16:12 +00:00
|
|
|
#[axum::debug_handler]
|
2024-11-14 03:02:40 +00:00
|
|
|
pub async fn webfinger_handler(
|
2024-11-15 05:50:51 +00:00
|
|
|
Query(WebfingerQuery {resource, rel}): Query<WebfingerQuery>,
|
|
|
|
headers: HeaderMap,
|
2024-11-16 05:16:52 +00:00
|
|
|
Extension(mj): Extension<Arc<minijinja::Environment<'static>>>,
|
2024-11-15 17:12:13 +00:00
|
|
|
) -> Result<Response<String>, StatusCode> {
|
2024-11-14 03:16:12 +00:00
|
|
|
log::info!("Handling a WebFinger request!");
|
2024-11-15 05:50:51 +00:00
|
|
|
|
2024-11-15 17:12:13 +00:00
|
|
|
let resource = resource.unwrap_or_else(|| "".to_string());
|
2024-11-14 03:16:12 +00:00
|
|
|
log::debug!("Resource is: {resource:#?}");
|
2024-11-15 05:50:51 +00:00
|
|
|
|
2024-11-14 03:16:12 +00:00
|
|
|
log::debug!("Rel is: {rel:#?}");
|
|
|
|
|
2024-11-15 05:50:51 +00:00
|
|
|
let accept = headers.get("Accept")
|
|
|
|
.map(|v| v.to_str())
|
|
|
|
.filter(Result::is_ok)
|
|
|
|
.map(|v| v.unwrap())
|
|
|
|
.unwrap_or("application/json")
|
|
|
|
.to_string();
|
|
|
|
log::debug!("Accept is: {accept:#?}");
|
|
|
|
|
2024-11-15 17:12:13 +00:00
|
|
|
let mut response = Response::new("".to_string());
|
2024-11-15 05:50:51 +00:00
|
|
|
|
|
|
|
let mut conn = AsyncPgConnection::establish(config::ACRATE_WEBFINGER_DATABASE_URL())
|
|
|
|
.await
|
|
|
|
.map_err(|_| StatusCode::BAD_GATEWAY)?;
|
|
|
|
|
2024-11-19 02:31:07 +00:00
|
|
|
let subjects = MetaSubject::aquery_matching(&mut conn, WEBFINGER_DOC, &resource)
|
2024-11-16 05:46:25 +00:00
|
|
|
.await
|
|
|
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
|
|
|
|
|
|
|
let subject = subjects.first()
|
|
|
|
.ok_or(StatusCode::NOT_FOUND)?;
|
|
|
|
|
|
|
|
if subject.redirect.is_some() {
|
|
|
|
{
|
|
|
|
let headers = response.headers_mut();
|
|
|
|
headers.insert(
|
|
|
|
"Location",
|
|
|
|
subject.redirect
|
|
|
|
.as_ref()
|
|
|
|
.expect("redirect not to have become suddenly None")
|
|
|
|
.parse()
|
|
|
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
|
|
|
);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
let status = response.status_mut();
|
|
|
|
*status = StatusCode::FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Ok(response);
|
|
|
|
}
|
|
|
|
|
2024-11-16 06:49:28 +00:00
|
|
|
let subject = subject.subject.clone();
|
|
|
|
|
2024-11-19 02:31:07 +00:00
|
|
|
let aliases = MetaAlias::aquery_matching(&mut conn, WEBFINGER_DOC, &resource)
|
2024-11-15 05:50:51 +00:00
|
|
|
.await
|
|
|
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
|
|
|
|
2024-11-19 02:31:07 +00:00
|
|
|
let properties = MetaProperty::aquery_matching(&mut conn, WEBFINGER_DOC, &resource)
|
2024-11-15 05:50:51 +00:00
|
|
|
.await
|
|
|
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
|
|
|
|
2024-11-19 02:31:07 +00:00
|
|
|
let links = MetaLink::aquery_matching(&mut conn, WEBFINGER_DOC, &resource)
|
2024-11-15 05:50:51 +00:00
|
|
|
.await
|
|
|
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
|
|
|
|
2024-11-19 02:31:07 +00:00
|
|
|
let link_properties = MetaLinkProperty::aquery_by_link(&mut conn, &links)
|
2024-11-16 01:58:45 +00:00
|
|
|
.await
|
|
|
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
|
|
|
.grouped_by(&links);
|
|
|
|
|
2024-11-19 02:31:07 +00:00
|
|
|
let link_titles = MetaLinkTitle::aquery_by_link(&mut conn, &links)
|
2024-11-16 01:58:45 +00:00
|
|
|
.await
|
|
|
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
|
|
|
.grouped_by(&links);
|
|
|
|
|
|
|
|
let links_full: Vec<(MetaLink, Vec<MetaLinkProperty>, Vec<MetaLinkTitle>)> = links
|
|
|
|
.into_iter()
|
|
|
|
.zip(link_properties)
|
|
|
|
.zip(link_titles)
|
|
|
|
.map(|((link, properties), titles)| (link, properties, titles))
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
{
|
|
|
|
let headers = response.headers_mut();
|
|
|
|
headers.insert(
|
|
|
|
"Access-Control-Allow-Origin",
|
|
|
|
"*".parse().unwrap()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-11-15 17:12:13 +00:00
|
|
|
for mime in accept.split(",") {
|
|
|
|
{
|
|
|
|
let headers = response.headers_mut();
|
2024-11-16 01:58:45 +00:00
|
|
|
headers.insert(
|
|
|
|
"Content-Type",
|
|
|
|
mime.parse().map_err(|_| StatusCode::BAD_REQUEST)?
|
|
|
|
);
|
2024-11-15 17:12:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let (mime, _params) = match mime.trim().split_once(";") {
|
|
|
|
Some((mime, params)) => (mime, Some(params)),
|
|
|
|
None => (mime, None),
|
|
|
|
};
|
|
|
|
|
2024-11-15 05:50:51 +00:00
|
|
|
match mime {
|
2024-11-15 17:12:13 +00:00
|
|
|
"*/*" | "application/json" | "application/jrd+json" => {
|
2024-11-15 05:50:51 +00:00
|
|
|
let aliases = aliases
|
|
|
|
.into_iter()
|
|
|
|
.map(|alias| alias.alias)
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let properties = properties
|
|
|
|
.into_iter()
|
|
|
|
.map(|prop| (prop.rel, prop.value))
|
|
|
|
.collect();
|
|
|
|
|
2024-11-16 01:58:45 +00:00
|
|
|
let links = links_full
|
2024-11-15 05:50:51 +00:00
|
|
|
.into_iter()
|
2024-11-16 01:58:45 +00:00
|
|
|
.map(|(link, properties, titles)| ResourceDescriptorLinkJRD {
|
2024-11-15 05:50:51 +00:00
|
|
|
rel: link.rel,
|
2024-11-19 02:31:07 +00:00
|
|
|
r#type: link.type_.map(|m| m.0),
|
2024-11-15 05:50:51 +00:00
|
|
|
href: link.href,
|
2024-11-16 01:58:45 +00:00
|
|
|
template: link.template,
|
|
|
|
properties: properties
|
|
|
|
.into_iter()
|
|
|
|
.map(|property| (property.rel, property.value))
|
|
|
|
.collect(),
|
|
|
|
titles: titles
|
|
|
|
.into_iter()
|
|
|
|
.map(|title| (title.language, title.value))
|
|
|
|
.collect(),
|
2024-11-15 05:50:51 +00:00
|
|
|
})
|
|
|
|
.collect::<Vec<ResourceDescriptorLinkJRD>>();
|
|
|
|
|
2024-11-19 02:31:07 +00:00
|
|
|
let rd = acrate_rd::jrd::ResourceDescriptorJRD {
|
2024-11-15 05:50:51 +00:00
|
|
|
subject,
|
|
|
|
aliases,
|
|
|
|
properties,
|
|
|
|
links,
|
|
|
|
};
|
|
|
|
|
2024-11-15 17:12:13 +00:00
|
|
|
let json = serde_json::to_string_pretty(&rd)
|
|
|
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
2024-11-15 05:50:51 +00:00
|
|
|
|
2024-11-15 17:12:13 +00:00
|
|
|
{
|
|
|
|
let body = response.body_mut();
|
|
|
|
body.push_str(&json);
|
|
|
|
}
|
|
|
|
|
2024-11-16 05:16:52 +00:00
|
|
|
return Ok(response);
|
2024-11-15 05:50:51 +00:00
|
|
|
},
|
|
|
|
"application/xml" | "application/xrd+xml" => {
|
|
|
|
let aliases = aliases
|
|
|
|
.into_iter()
|
|
|
|
.map(|alias| alias.alias)
|
|
|
|
.collect();
|
|
|
|
|
2024-11-16 01:58:45 +00:00
|
|
|
let properties: Vec<ResourceDescriptorPropertyXRD> = properties
|
2024-11-15 05:50:51 +00:00
|
|
|
.into_iter()
|
|
|
|
.map(|prop| ResourceDescriptorPropertyXRD {
|
2024-11-16 00:42:04 +00:00
|
|
|
rel: prop.rel,
|
2024-11-15 05:50:51 +00:00
|
|
|
value: prop.value,
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
2024-11-16 01:58:45 +00:00
|
|
|
let links = links_full
|
2024-11-15 05:50:51 +00:00
|
|
|
.into_iter()
|
2024-11-16 01:58:45 +00:00
|
|
|
.map(|(link, properties, titles)| ResourceDescriptorLinkXRD {
|
2024-11-15 05:50:51 +00:00
|
|
|
rel: link.rel,
|
2024-11-19 02:31:07 +00:00
|
|
|
r#type: link.type_.map(|m| m.0),
|
2024-11-15 05:50:51 +00:00
|
|
|
href: link.href,
|
2024-11-16 01:58:45 +00:00
|
|
|
template: link.template,
|
|
|
|
properties: properties
|
|
|
|
.into_iter()
|
|
|
|
.map(|property| ResourceDescriptorPropertyXRD {
|
|
|
|
rel: property.rel,
|
|
|
|
value: property.value,
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
titles: titles
|
|
|
|
.into_iter()
|
|
|
|
.map(|title| ResourceDescriptorTitleXRD {
|
|
|
|
language: title.language,
|
|
|
|
value: title.value,
|
|
|
|
})
|
|
|
|
.collect(),
|
2024-11-15 05:50:51 +00:00
|
|
|
})
|
|
|
|
.collect::<Vec<ResourceDescriptorLinkXRD>>();
|
|
|
|
|
2024-11-19 02:31:07 +00:00
|
|
|
let rd = acrate_rd::xrd::ResourceDescriptorXRD {
|
2024-11-15 05:50:51 +00:00
|
|
|
subject,
|
|
|
|
aliases,
|
|
|
|
properties,
|
|
|
|
links,
|
|
|
|
};
|
|
|
|
|
2024-11-15 17:12:13 +00:00
|
|
|
let xml = quick_xml::se::to_string(&rd)
|
|
|
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
|
|
|
|
|
|
|
{
|
|
|
|
let body = response.body_mut();
|
2024-11-16 05:16:52 +00:00
|
|
|
body.push_str(r#"<?xml version="1.0" encoding="UTF-8"?>"#);
|
2024-11-15 17:12:13 +00:00
|
|
|
body.push_str(&xml);
|
|
|
|
}
|
|
|
|
|
2024-11-16 05:16:52 +00:00
|
|
|
return Ok(response);
|
|
|
|
},
|
|
|
|
"text/html" => {
|
|
|
|
let aliases: Vec<String> = aliases.into_iter()
|
|
|
|
.map(|alias| alias.alias)
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let properties: Vec<(String, Option<String>)> = properties.into_iter()
|
|
|
|
.map(|prop| {
|
|
|
|
(prop.rel, prop.value)
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
2024-11-19 02:31:07 +00:00
|
|
|
let links: Vec<(String, Option<mime::Mime>, Option<String>, Option<String>, Vec<(String, Option<String>)>, Vec<(String, String)>)> = links_full
|
2024-11-16 05:16:52 +00:00
|
|
|
.into_iter()
|
|
|
|
.map(|(link, properties, titles)| {
|
|
|
|
(
|
|
|
|
link.rel,
|
2024-11-19 02:31:07 +00:00
|
|
|
link.type_.map(|m| m.0),
|
2024-11-16 05:16:52 +00:00
|
|
|
link.href,
|
|
|
|
link.template,
|
|
|
|
properties.into_iter()
|
|
|
|
.map(|prop| (prop.rel, prop.value))
|
|
|
|
.collect::<Vec<(String, Option<String>)>>(),
|
|
|
|
titles.into_iter()
|
|
|
|
.map(|title| (title.language, title.value))
|
|
|
|
.collect::<Vec<(String, String)>>()
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let html = mj.get_template("webfinger.html.j2")
|
|
|
|
.expect("webfinger.html.j2 to exist")
|
|
|
|
.render(
|
|
|
|
minijinja::context!(
|
2024-11-16 06:49:28 +00:00
|
|
|
subject => subject,
|
2024-11-16 05:16:52 +00:00
|
|
|
aliases => aliases,
|
|
|
|
properties => properties,
|
|
|
|
links => links,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
|
|
|
|
|
|
|
{
|
|
|
|
let body = response.body_mut();
|
|
|
|
body.push_str(&html);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Ok(response);
|
2024-11-15 05:50:51 +00:00
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
continue;
|
2024-11-16 05:16:52 +00:00
|
|
|
},
|
2024-11-15 05:50:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Err(StatusCode::NOT_ACCEPTABLE)
|
2024-11-14 02:50:59 +00:00
|
|
|
}
|