mirror of
https://github.com/CloudNebulaProject/webfingerd.git
synced 2026-04-10 21:20:41 +00:00
feat: add host-meta endpoint with domain-aware XRD response
This commit is contained in:
parent
697c84accf
commit
7aa5a6738c
3 changed files with 101 additions and 0 deletions
45
src/handler/host_meta.rs
Normal file
45
src/handler/host_meta.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
use axum::extract::State;
|
||||||
|
use axum_extra::extract::Host;
|
||||||
|
use axum::http::header;
|
||||||
|
use axum::response::{IntoResponse, Response};
|
||||||
|
use axum::routing::get;
|
||||||
|
use axum::Router;
|
||||||
|
use sea_orm::*;
|
||||||
|
|
||||||
|
use crate::entity::domains;
|
||||||
|
use crate::error::{AppError, AppResult};
|
||||||
|
use crate::state::AppState;
|
||||||
|
|
||||||
|
async fn host_meta(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Host(hostname): Host,
|
||||||
|
) -> AppResult<Response> {
|
||||||
|
// Strip port if present
|
||||||
|
let domain = hostname.split(':').next().unwrap_or(&hostname);
|
||||||
|
|
||||||
|
// Check this domain is registered and verified
|
||||||
|
let _domain = domains::Entity::find()
|
||||||
|
.filter(domains::Column::Domain.eq(domain))
|
||||||
|
.filter(domains::Column::Verified.eq(true))
|
||||||
|
.one(&state.db)
|
||||||
|
.await?
|
||||||
|
.ok_or(AppError::NotFound)?;
|
||||||
|
|
||||||
|
let base_url = &state.settings.server.base_url;
|
||||||
|
let xrd = format!(
|
||||||
|
r#"<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
|
||||||
|
<Link rel="lrdd" type="application/jrd+json" template="{base_url}/.well-known/webfinger?resource={{uri}}" />
|
||||||
|
</XRD>"#
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
[(header::CONTENT_TYPE, "application/xrd+xml; charset=utf-8")],
|
||||||
|
xrd,
|
||||||
|
)
|
||||||
|
.into_response())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn router() -> Router<AppState> {
|
||||||
|
Router::new().route("/.well-known/host-meta", get(host_meta))
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
mod health;
|
mod health;
|
||||||
|
mod host_meta;
|
||||||
mod webfinger;
|
mod webfinger;
|
||||||
|
|
||||||
use axum::Router;
|
use axum::Router;
|
||||||
|
|
@ -7,6 +8,7 @@ use crate::state::AppState;
|
||||||
pub fn router(state: AppState) -> Router {
|
pub fn router(state: AppState) -> Router {
|
||||||
Router::new()
|
Router::new()
|
||||||
.merge(webfinger::router())
|
.merge(webfinger::router())
|
||||||
|
.merge(host_meta::router())
|
||||||
.merge(health::router())
|
.merge(health::router())
|
||||||
.with_state(state)
|
.with_state(state)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
54
tests/test_host_meta.rs
Normal file
54
tests/test_host_meta.rs
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
use axum_test::TestServer;
|
||||||
|
use webfingerd::handler;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_host_meta_returns_xrd_for_known_domain() {
|
||||||
|
let state = common::test_state().await;
|
||||||
|
|
||||||
|
// Seed a verified domain in DB
|
||||||
|
use sea_orm::ActiveModelTrait;
|
||||||
|
use sea_orm::Set;
|
||||||
|
use webfingerd::entity::domains;
|
||||||
|
|
||||||
|
let domain = domains::ActiveModel {
|
||||||
|
id: Set(uuid::Uuid::new_v4().to_string()),
|
||||||
|
domain: Set("example.com".into()),
|
||||||
|
owner_token_hash: Set("hash".into()),
|
||||||
|
registration_secret: Set("secret".into()),
|
||||||
|
challenge_type: Set("dns-01".into()),
|
||||||
|
challenge_token: Set(None),
|
||||||
|
verified: Set(true),
|
||||||
|
created_at: Set(chrono::Utc::now().naive_utc()),
|
||||||
|
verified_at: Set(Some(chrono::Utc::now().naive_utc())),
|
||||||
|
};
|
||||||
|
domain.insert(&state.db).await.unwrap();
|
||||||
|
|
||||||
|
let app = handler::router(state);
|
||||||
|
let server = TestServer::new(app);
|
||||||
|
|
||||||
|
let response = server
|
||||||
|
.get("/.well-known/host-meta")
|
||||||
|
.add_header("Host", "example.com")
|
||||||
|
.await;
|
||||||
|
|
||||||
|
response.assert_status_ok();
|
||||||
|
let body = response.text();
|
||||||
|
assert!(body.contains("application/jrd+json") || body.contains("XRD"));
|
||||||
|
assert!(body.contains("/.well-known/webfinger"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_host_meta_returns_404_for_unknown_domain() {
|
||||||
|
let state = common::test_state().await;
|
||||||
|
let app = handler::router(state);
|
||||||
|
let server = TestServer::new(app);
|
||||||
|
|
||||||
|
let response = server
|
||||||
|
.get("/.well-known/host-meta")
|
||||||
|
.add_header("Host", "unknown.example.com")
|
||||||
|
.await;
|
||||||
|
|
||||||
|
response.assert_status_not_found();
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue