routes.rs
145 lignes · 5935 octets
//! Builder for the federation Axum router. //! //! Assembles the four discovery routes (`webfinger`, `nodeinfo` discovery, //! `nodeinfo` detail, actor) into a single `Router` ready to be merged into //! the application by the app entry point. use std::sync::Arc; use axum::{ Extension, Router, extract::DefaultBodyLimit, routing::{get, post}, }; use sea_orm::DatabaseConnection; /// Maximum inbound activity body size accepted on `/inbox` (ANSSI F-NET-1). /// /// 1 MiB matches Mastodon's hard-coded ceiling for inbox activities. /// Anything larger is rejected with 413 before deserialization, capping /// the DoS surface from oversized JSON-LD payloads. const INBOX_BODY_LIMIT_BYTES: usize = 1024 * 1024; use crate::config::FediverseConfig; use crate::services::fediverse_signal_service::{AlwaysAcceptVerifier, SignatureVerifier}; use rustwarden_core::config::auth::AuthConfig; use super::actor; use super::auth_routes::{self, AuthFlowState}; use super::inbox::{self, InboxState}; use super::invite_routes::{self, InviteRoutesState}; use super::nodeinfo; use super::resolver::{LocalUserResolver, SeaOrmLocalUserResolver}; use super::state::FediverseState; use super::webfinger; /// Build the federation router with the default SeaORM-backed resolver /// and the test-only [`AlwaysAcceptVerifier`]. /// /// **DEV/TEST ONLY** — ANSSI F-NET-5: this entry point uses /// `AlwaysAcceptVerifier` which accepts any HTTP signature without /// verifying it. Production callers must use /// [`router_with_auth`] with a real verifier (`HttpSigVerifier`). #[deprecated( since = "0.2.0", note = "use `router_with_auth` with an explicit `AuthConfig` and a production `HttpSigVerifier`" )] pub fn router(config: FediverseConfig, db: DatabaseConnection) -> Router<DatabaseConnection> { let resolver: Arc<dyn LocalUserResolver> = Arc::new(SeaOrmLocalUserResolver::new(db.clone())); // Debug/dev verifier with cache-population side-effect so the // testbed (FEDIVERSE_DEBUG_LOCAL_FEDERATION=true) doesn't need to // manually pre-seed `remote_identities`. let verifier: Arc<dyn SignatureVerifier> = Arc::new(AlwaysAcceptVerifier::with_resolver(db)); router_with(config, resolver, verifier) } /// Build the federation router with custom resolver and signature /// verifier implementations. Used by integration tests, consumers that /// already maintain their own user store, or apps wanting to plug a /// production HTTP-Sig verifier (RFC 0012). pub fn router_with( config: FediverseConfig, resolver: Arc<dyn LocalUserResolver>, verifier: Arc<dyn SignatureVerifier>, ) -> Router<DatabaseConnection> { let challenge_ttl = config.auth_challenge_ttl(); let state = FediverseState { config, resolver }; let inbox_state = InboxState { verifier: verifier.clone(), }; // ANSSI F-NET-5 — fail loudly when env vars are missing rather // than silently falling back to a placeholder JWT secret. Production // callers should prefer `router_with_auth` which takes // `AuthConfig` explicitly and lets the caller surface configuration // errors as proper startup failures. let auth_flow_state = AuthFlowState { auth_config: Arc::new(AuthConfig::from_env().expect( "AuthConfig::from_env() failed — set JWT_SECRET and related env vars, \ or call `router_with_auth` with an explicit AuthConfig", )), verifier, challenge_ttl, }; Router::new() .route("/.well-known/webfinger", get(webfinger::handle)) .route("/.well-known/nodeinfo", get(nodeinfo::discovery)) .route("/nodeinfo/2.1", get(nodeinfo::detail)) .route("/users/{username}", get(actor::handle)) .route( "/inbox", post(inbox::handle).layer(DefaultBodyLimit::max(INBOX_BODY_LIMIT_BYTES)), ) .merge(auth_routes::router(auth_flow_state)) .merge(invite_routes::router(InviteRoutesState { default_ttl: std::time::Duration::from_secs(7 * 86400), })) .layer(Extension(state)) .layer(Extension(inbox_state)) } /// Build the federation router with explicit `AuthConfig` injection. /// Use this entry point when the consumer cannot rely on env vars /// being set at router-build time. pub fn router_with_auth( config: FediverseConfig, resolver: Arc<dyn LocalUserResolver>, verifier: Arc<dyn SignatureVerifier>, auth_config: Arc<AuthConfig>, ) -> Router<DatabaseConnection> { let challenge_ttl = config.auth_challenge_ttl(); let state = FediverseState { config, resolver }; let inbox_state = InboxState { verifier: verifier.clone(), }; let auth_flow_state = AuthFlowState { auth_config, verifier, challenge_ttl, }; Router::new() .route("/.well-known/webfinger", get(webfinger::handle)) .route("/.well-known/nodeinfo", get(nodeinfo::discovery)) .route("/nodeinfo/2.1", get(nodeinfo::detail)) .route("/users/{username}", get(actor::handle)) .route( "/inbox", post(inbox::handle).layer(DefaultBodyLimit::max(INBOX_BODY_LIMIT_BYTES)), ) .merge(auth_routes::router(auth_flow_state)) .merge(invite_routes::router(InviteRoutesState { default_ttl: std::time::Duration::from_secs(7 * 86400), })) .layer(Extension(state)) .layer(Extension(inbox_state)) } /// Backwards-compatible alias for [`router_with`] kept until the next /// major version; new code should call `router_with` directly. #[deprecated(since = "0.2.0", note = "use `router_with` (RFC 0012)")] pub fn router_with_resolver( config: FediverseConfig, resolver: Arc<dyn LocalUserResolver>, ) -> Router<DatabaseConnection> { let verifier: Arc<dyn SignatureVerifier> = Arc::new(AlwaysAcceptVerifier::new()); router_with(config, resolver, verifier) }
GitRust