From f30b08f015b00de2292d2b6938e352f0eb8d7daa Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Sun, 24 Nov 2024 00:19:55 +0000 Subject: [PATCH] fix optional config section related split api/client well_known simplify well_known config access Signed-off-by: Jason Volk --- src/api/client/mod.rs | 2 + src/api/client/session.rs | 6 +- src/api/client/unversioned.rs | 104 +-------------------------------- src/api/client/well_known.rs | 105 ++++++++++++++++++++++++++++++++++ src/api/server/well_known.rs | 2 +- src/core/config/mod.rs | 26 ++++++--- src/router/serve/mod.rs | 2 +- src/router/serve/tls.rs | 19 +++--- src/service/globals/mod.rs | 16 +----- 9 files changed, 144 insertions(+), 138 deletions(-) create mode 100644 src/api/client/well_known.rs diff --git a/src/api/client/mod.rs b/src/api/client/mod.rs index 9ee88bec..3c9736ea 100644 --- a/src/api/client/mod.rs +++ b/src/api/client/mod.rs @@ -37,6 +37,7 @@ pub(super) mod unstable; pub(super) mod unversioned; pub(super) mod user_directory; pub(super) mod voip; +pub(super) mod well_known; pub use account::full_user_deactivate; pub(super) use account::*; @@ -80,6 +81,7 @@ pub(super) use unstable::*; pub(super) use unversioned::*; pub(super) use user_directory::*; pub(super) use voip::*; +pub(super) use well_known::*; /// generated device ID length const DEVICE_ID_LENGTH: usize = 10; diff --git a/src/api/client/session.rs b/src/api/client/session.rs index 6347a2c9..573f3d97 100644 --- a/src/api/client/session.rs +++ b/src/api/client/session.rs @@ -198,8 +198,10 @@ pub(crate) async fn login_route( // send client well-known if specified so the client knows to reconfigure itself let client_discovery_info: Option = services - .globals - .well_known_client() + .server + .config + .well_known + .client .as_ref() .map(|server| DiscoveryInfo::new(HomeserverInfo::new(server.to_string()))); diff --git a/src/api/client/unversioned.rs b/src/api/client/unversioned.rs index 3aee30c8..ed3ce37a 100644 --- a/src/api/client/unversioned.rs +++ b/src/api/client/unversioned.rs @@ -2,16 +2,9 @@ use std::collections::BTreeMap; use axum::{extract::State, response::IntoResponse, Json}; use futures::StreamExt; -use ruma::api::client::{ - discovery::{ - discover_homeserver::{self, HomeserverInfo, SlidingSyncProxyInfo}, - discover_support::{self, Contact}, - get_supported_versions, - }, - error::ErrorKind, -}; +use ruma::api::client::discovery::get_supported_versions; -use crate::{Error, Result, Ruma}; +use crate::{Result, Ruma}; /// # `GET /_matrix/client/versions` /// @@ -65,99 +58,6 @@ pub(crate) async fn get_supported_versions_route( Ok(resp) } -/// # `GET /.well-known/matrix/client` -/// -/// Returns the .well-known URL if it is configured, otherwise returns 404. -pub(crate) async fn well_known_client( - State(services): State, _body: Ruma, -) -> Result { - let client_url = match services.globals.well_known_client() { - Some(url) => url.to_string(), - None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")), - }; - - Ok(discover_homeserver::Response { - homeserver: HomeserverInfo { - base_url: client_url.clone(), - }, - identity_server: None, - sliding_sync_proxy: Some(SlidingSyncProxyInfo { - url: client_url, - }), - tile_server: None, - }) -} - -/// # `GET /.well-known/matrix/support` -/// -/// Server support contact and support page of a homeserver's domain. -pub(crate) async fn well_known_support( - State(services): State, _body: Ruma, -) -> Result { - let support_page = services - .globals - .well_known_support_page() - .as_ref() - .map(ToString::to_string); - - let role = services.globals.well_known_support_role().clone(); - - // support page or role must be either defined for this to be valid - if support_page.is_none() && role.is_none() { - return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")); - } - - let email_address = services.globals.well_known_support_email().clone(); - let matrix_id = services.globals.well_known_support_mxid().clone(); - - // if a role is specified, an email address or matrix id is required - if role.is_some() && (email_address.is_none() && matrix_id.is_none()) { - return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")); - } - - // TOOD: support defining multiple contacts in the config - let mut contacts: Vec = vec![]; - - if let Some(role) = role { - let contact = Contact { - role, - email_address, - matrix_id, - }; - - contacts.push(contact); - } - - // support page or role+contacts must be either defined for this to be valid - if contacts.is_empty() && support_page.is_none() { - return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")); - } - - Ok(discover_support::Response { - contacts, - support_page, - }) -} - -/// # `GET /client/server.json` -/// -/// Endpoint provided by sliding sync proxy used by some clients such as Element -/// Web as a non-standard health check. -pub(crate) async fn syncv3_client_server_json(State(services): State) -> Result { - let server_url = match services.globals.well_known_client() { - Some(url) => url.to_string(), - None => match services.globals.well_known_server() { - Some(url) => url.to_string(), - None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")), - }, - }; - - Ok(Json(serde_json::json!({ - "server": server_url, - "version": conduit::version(), - }))) -} - /// # `GET /_conduwuit/server_version` /// /// Conduwuit-specific API to get the server version, results akin to diff --git a/src/api/client/well_known.rs b/src/api/client/well_known.rs new file mode 100644 index 00000000..674c9bb0 --- /dev/null +++ b/src/api/client/well_known.rs @@ -0,0 +1,105 @@ +use axum::{extract::State, response::IntoResponse, Json}; +use ruma::api::client::{ + discovery::{ + discover_homeserver::{self, HomeserverInfo, SlidingSyncProxyInfo}, + discover_support::{self, Contact}, + }, + error::ErrorKind, +}; + +use crate::{Error, Result, Ruma}; + +/// # `GET /.well-known/matrix/client` +/// +/// Returns the .well-known URL if it is configured, otherwise returns 404. +pub(crate) async fn well_known_client( + State(services): State, _body: Ruma, +) -> Result { + let client_url = match services.server.config.well_known.client.as_ref() { + Some(url) => url.to_string(), + None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")), + }; + + Ok(discover_homeserver::Response { + homeserver: HomeserverInfo { + base_url: client_url.clone(), + }, + identity_server: None, + sliding_sync_proxy: Some(SlidingSyncProxyInfo { + url: client_url, + }), + tile_server: None, + }) +} + +/// # `GET /.well-known/matrix/support` +/// +/// Server support contact and support page of a homeserver's domain. +pub(crate) async fn well_known_support( + State(services): State, _body: Ruma, +) -> Result { + let support_page = services + .server + .config + .well_known + .support_page + .as_ref() + .map(ToString::to_string); + + let role = services.server.config.well_known.support_role.clone(); + + // support page or role must be either defined for this to be valid + if support_page.is_none() && role.is_none() { + return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")); + } + + let email_address = services.server.config.well_known.support_email.clone(); + let matrix_id = services.server.config.well_known.support_mxid.clone(); + + // if a role is specified, an email address or matrix id is required + if role.is_some() && (email_address.is_none() && matrix_id.is_none()) { + return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")); + } + + // TOOD: support defining multiple contacts in the config + let mut contacts: Vec = vec![]; + + if let Some(role) = role { + let contact = Contact { + role, + email_address, + matrix_id, + }; + + contacts.push(contact); + } + + // support page or role+contacts must be either defined for this to be valid + if contacts.is_empty() && support_page.is_none() { + return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")); + } + + Ok(discover_support::Response { + contacts, + support_page, + }) +} + +/// # `GET /client/server.json` +/// +/// Endpoint provided by sliding sync proxy used by some clients such as Element +/// Web as a non-standard health check. +pub(crate) async fn syncv3_client_server_json(State(services): State) -> Result { + let server_url = match services.server.config.well_known.client.as_ref() { + Some(url) => url.to_string(), + None => match services.server.config.well_known.server.as_ref() { + Some(url) => url.to_string(), + None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")), + }, + }; + + Ok(Json(serde_json::json!({ + "server": server_url, + "version": conduit::version(), + }))) +} diff --git a/src/api/server/well_known.rs b/src/api/server/well_known.rs index 2cc8f238..e6145aea 100644 --- a/src/api/server/well_known.rs +++ b/src/api/server/well_known.rs @@ -10,7 +10,7 @@ pub(crate) async fn well_known_server( State(services): State, _body: Ruma, ) -> Result { Ok(discover_homeserver::Response { - server: match services.globals.well_known_server() { + server: match services.server.config.well_known.server.as_ref() { Some(server_name) => server_name.to_owned(), None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")), }, diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index 66c78440..1754581d 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -87,7 +87,8 @@ pub struct Config { port: ListeningPort, // external structure; separate section - pub tls: Option, + #[serde(default)] + pub tls: TlsConfig, /// Uncomment unix_socket_path to listen on a UNIX socket at the specified /// path. If listening on a UNIX socket, you MUST remove/comment the @@ -1500,17 +1501,19 @@ pub struct Config { catchall: BTreeMap, } -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize, Default)] #[config_example_generator(filename = "conduwuit-example.toml", section = "global.tls")] pub struct TlsConfig { /// Path to a valid TLS certificate file. /// /// example: "/path/to/my/certificate.crt" - pub certs: String, + pub certs: Option, + /// Path to a valid TLS certificate private key. /// /// example: "/path/to/my/certificate.key" - pub key: String, + pub key: Option, + /// Whether to listen and allow for HTTP and HTTPS connections (insecure!) #[serde(default)] pub dual_protocol: bool, @@ -1520,20 +1523,25 @@ pub struct TlsConfig { #[derive(Clone, Debug, Deserialize, Default)] #[config_example_generator(filename = "conduwuit-example.toml", section = "global.well_known")] pub struct WellKnownConfig { + /// The server URL that the client well-known file will serve. This should + /// not contain a port, and should just be a valid HTTPS URL. + /// + /// example: "https://matrix.example.com" + pub client: Option, + /// The server base domain of the URL with a specific port that the server /// well-known file will serve. This should contain a port at the end, and /// should not be a URL. /// /// example: "matrix.example.com:443" pub server: Option, - /// The server URL that the client well-known file will serve. This should - /// not contain a port, and should just be a valid HTTPS URL. - /// - /// example: "https://matrix.example.com" - pub client: Option, + pub support_page: Option, + pub support_role: Option, + pub support_email: Option, + pub support_mxid: Option, } diff --git a/src/router/serve/mod.rs b/src/router/serve/mod.rs index 858d3455..b0254772 100644 --- a/src/router/serve/mod.rs +++ b/src/router/serve/mod.rs @@ -23,7 +23,7 @@ pub(super) async fn serve( if cfg!(unix) && config.unix_socket_path.is_some() { unix::serve(server, app, shutdown).await - } else if config.tls.is_some() { + } else if config.tls.certs.is_some() { #[cfg(feature = "direct_tls")] return tls::serve(server, app, handle, addrs).await; diff --git a/src/router/serve/tls.rs b/src/router/serve/tls.rs index 08c5e7b6..f8d69048 100644 --- a/src/router/serve/tls.rs +++ b/src/router/serve/tls.rs @@ -6,17 +6,20 @@ use axum_server_dual_protocol::{ axum_server::{bind_rustls, tls_rustls::RustlsConfig}, ServerExt, }; -use conduit::{Result, Server}; +use conduit::{err, Result, Server}; use tokio::task::JoinSet; use tracing::{debug, info, warn}; -pub(super) async fn serve( - server: &Arc, app: Router, handle: ServerHandle, addrs: Vec, -) -> Result<()> { - let config = &server.config; - let tls = config.tls.as_ref().expect("TLS configuration"); - let certs = &tls.certs; - let key = &tls.key; +pub(super) async fn serve(server: &Arc, app: Router, handle: ServerHandle, addrs: Vec) -> Result { + let tls = &server.config.tls; + let certs = tls + .certs + .as_ref() + .ok_or(err!(Config("tls.certs", "Missing required value in tls config section")))?; + let key = tls + .key + .as_ref() + .ok_or(err!(Config("tls.key", "Missing required value in tls config section")))?; // we use ring for ruma and hashing state, but aws-lc-rs is the new default. // without this, TLS mode will panic. diff --git a/src/service/globals/mod.rs b/src/service/globals/mod.rs index 55dd10aa..3eefe4b7 100644 --- a/src/service/globals/mod.rs +++ b/src/service/globals/mod.rs @@ -12,11 +12,9 @@ use data::Data; use ipaddress::IPAddress; use regex::RegexSet; use ruma::{ - api::client::discovery::discover_support::ContactRole, OwnedEventId, OwnedRoomAliasId, OwnedServerName, - OwnedUserId, RoomAliasId, RoomVersionId, ServerName, UserId, + OwnedEventId, OwnedRoomAliasId, OwnedServerName, OwnedUserId, RoomAliasId, RoomVersionId, ServerName, UserId, }; use tokio::sync::Mutex; -use url::Url; use crate::service; @@ -243,14 +241,6 @@ impl Service { pub fn allow_outgoing_read_receipts(&self) -> bool { self.config.allow_outgoing_read_receipts } - pub fn well_known_support_page(&self) -> &Option { &self.config.well_known.support_page } - - pub fn well_known_support_role(&self) -> &Option { &self.config.well_known.support_role } - - pub fn well_known_support_email(&self) -> &Option { &self.config.well_known.support_email } - - pub fn well_known_support_mxid(&self) -> &Option { &self.config.well_known.support_mxid } - pub fn block_non_admin_invites(&self) -> bool { self.config.block_non_admin_invites } pub fn supported_room_versions(&self) -> Vec { @@ -265,10 +255,6 @@ impl Service { } } - pub fn well_known_client(&self) -> &Option { &self.config.well_known.client } - - pub fn well_known_server(&self) -> &Option { &self.config.well_known.server } - #[inline] pub fn valid_cidr_range(&self, ip: &IPAddress) -> bool { for cidr in &self.cidr_range_denylist {