From fa02d7b7e33840de6cd8ec58f3ab2e9c3b46486c Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Sun, 23 Jun 2024 02:55:00 +0000 Subject: [PATCH] diffuse get_alias_helper into services::rooms::alias Signed-off-by: Jason Volk --- src/admin/room/room_moderation_commands.rs | 46 ++++-- src/api/client/alias.rs | 162 +++------------------ src/api/client/membership.rs | 20 ++- src/api/client/mod.rs | 1 - src/service/rooms/alias/mod.rs | 75 +++++++++- src/service/rooms/alias/remote.rs | 71 +++++++++ 6 files changed, 204 insertions(+), 171 deletions(-) create mode 100644 src/service/rooms/alias/remote.rs diff --git a/src/admin/room/room_moderation_commands.rs b/src/admin/room/room_moderation_commands.rs index 75a20584..39eb7c47 100644 --- a/src/admin/room/room_moderation_commands.rs +++ b/src/admin/room/room_moderation_commands.rs @@ -1,4 +1,4 @@ -use api::client::{get_alias_helper, leave_room}; +use api::client::leave_room; use ruma::{ events::room::message::RoomMessageEventContent, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId, RoomOrAliasId, }; @@ -76,13 +76,18 @@ async fn ban_room( } else { debug!("We don't have this room alias to a room ID locally, attempting to fetch room ID over federation"); - match get_alias_helper(room_alias, None).await { - Ok(response) => { - debug!("Got federation response fetching room ID for room {room}: {:?}", response); - response.room_id + match services() + .rooms + .alias + .resolve_alias(&room_alias, None) + .await + { + Ok((room_id, servers)) => { + debug!(?room_id, ?servers, "Got federation response fetching room ID for {room}"); + room_id }, Err(e) => { - return Ok(RoomMessageEventContent::text_plain(format!( + return Ok(RoomMessageEventContent::notice_plain(format!( "Failed to resolve room alias {room} to a room ID: {e}" ))); }, @@ -237,13 +242,19 @@ async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: boo ID over federation" ); - match get_alias_helper(room_alias, None).await { - Ok(response) => { + match services() + .rooms + .alias + .resolve_alias(&room_alias, None) + .await + { + Ok((room_id, servers)) => { debug!( - "Got federation response fetching room ID for room {room}: {:?}", - response + ?room_id, + ?servers, + "Got federation response fetching room ID for {room}", ); - response.room_id + room_id }, Err(e) => { // don't fail if force blocking @@ -426,10 +437,15 @@ async fn unban_room( } else { debug!("We don't have this room alias to a room ID locally, attempting to fetch room ID over federation"); - match get_alias_helper(room_alias, None).await { - Ok(response) => { - debug!("Got federation response fetching room ID for room {room}: {:?}", response); - response.room_id + match services() + .rooms + .alias + .resolve_alias(&room_alias, None) + .await + { + Ok((room_id, servers)) => { + debug!(?room_id, ?servers, "Got federation response fetching room ID for room {room}"); + room_id }, Err(e) => { return Ok(RoomMessageEventContent::text_plain(format!( diff --git a/src/api/client/alias.rs b/src/api/client/alias.rs index 5752ea9b..5230c25b 100644 --- a/src/api/client/alias.rs +++ b/src/api/client/alias.rs @@ -1,22 +1,14 @@ use rand::seq::SliceRandom; use ruma::{ - api::{ - appservice, - client::{ - alias::{create_alias, delete_alias, get_alias}, - error::ErrorKind, - }, - federation, + api::client::{ + alias::{create_alias, delete_alias, get_alias}, + error::ErrorKind, }, - OwnedRoomAliasId, OwnedServerName, RoomAliasId, RoomId, + OwnedServerName, RoomAliasId, RoomId, }; use tracing::debug; -use crate::{ - debug_info, debug_warn, - service::{appservice::RegistrationInfo, server_is_ours}, - services, Error, Result, Ruma, -}; +use crate::{service::server_is_ours, services, Error, Result, Ruma}; /// # `PUT /_matrix/client/v3/directory/room/{roomAlias}` /// @@ -24,7 +16,7 @@ use crate::{ pub(crate) async fn create_alias_route(body: Ruma) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - alias_checks(&body.room_alias, &body.appservice_info).await?; + service::rooms::alias::appservice_checks(&body.room_alias, &body.appservice_info).await?; // this isn't apart of alias_checks or delete alias route because we should // allow removing forbidden room aliases @@ -61,7 +53,7 @@ pub(crate) async fn create_alias_route(body: Ruma) -> pub(crate) async fn delete_alias_route(body: Ruma) -> Result { let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - alias_checks(&body.room_alias, &body.appservice_info).await?; + service::rooms::alias::appservice_checks(&body.room_alias, &body.appservice_info).await?; if services() .rooms @@ -87,124 +79,20 @@ pub(crate) async fn delete_alias_route(body: Ruma) -> /// /// Resolve an alias locally or over federation. pub(crate) async fn get_alias_route(body: Ruma) -> Result { - get_alias_helper(body.body.room_alias, None).await -} + let room_alias = body.body.room_alias; + let servers = None; -pub async fn get_alias_helper( - room_alias: OwnedRoomAliasId, servers: Option>, -) -> Result { - debug!("get_alias_helper servers: {servers:?}"); - if !server_is_ours(room_alias.server_name()) - && (!servers - .as_ref() - .is_some_and(|servers| servers.contains(&services().globals.server_name().to_owned())) - || servers.as_ref().is_none()) - { - let mut response = services() - .sending - .send_federation_request( - room_alias.server_name(), - federation::query::get_room_information::v1::Request { - room_alias: room_alias.clone(), - }, - ) - .await; - - debug!("room alias server_name get_alias_helper response: {response:?}"); - - if let Err(ref e) = response { - debug_info!( - "Server {} of the original room alias failed to assist in resolving room alias: {e}", - room_alias.server_name() - ); - } - - if response.as_ref().is_ok_and(|resp| resp.servers.is_empty()) || response.as_ref().is_err() { - if let Some(servers) = servers { - for server in servers { - response = services() - .sending - .send_federation_request( - &server, - federation::query::get_room_information::v1::Request { - room_alias: room_alias.clone(), - }, - ) - .await; - debug!("Got response from server {server} for room aliases: {response:?}"); - - if let Ok(ref response) = response { - if !response.servers.is_empty() { - break; - } - debug_warn!( - "Server {server} responded with room aliases, but was empty? Response: {response:?}" - ); - } - } - } - } - - if let Ok(response) = response { - let room_id = response.room_id; - - let mut pre_servers = response.servers; - // since the room alis server responded, insert it into the list - pre_servers.push(room_alias.server_name().into()); - - let servers = room_available_servers(&room_id, &room_alias, &Some(pre_servers)); - debug!( - "room alias servers from federation response for room ID {room_id} and room alias {room_alias}: \ - {servers:?}" - ); - - return Ok(get_alias::v3::Response::new(room_id, servers)); - } - - return Err(Error::BadRequest( - ErrorKind::NotFound, - "No servers could assist in resolving the room alias", - )); - } - - let mut room_id = None; - match services().rooms.alias.resolve_local_alias(&room_alias)? { - Some(r) => room_id = Some(r), - None => { - for appservice in services().appservice.read().await.values() { - if appservice.aliases.is_match(room_alias.as_str()) - && matches!( - services() - .sending - .send_appservice_request( - appservice.registration.clone(), - appservice::query::query_room_alias::v1::Request { - room_alias: room_alias.clone(), - }, - ) - .await, - Ok(Some(_opt_result)) - ) { - room_id = Some( - services() - .rooms - .alias - .resolve_local_alias(&room_alias)? - .ok_or_else(|| Error::bad_config("Room does not exist."))?, - ); - break; - } - } - }, - }; - - let Some(room_id) = room_id else { + let Ok((room_id, pre_servers)) = services() + .rooms + .alias + .resolve_alias(&room_alias, servers.as_ref()) + .await + else { return Err(Error::BadRequest(ErrorKind::NotFound, "Room with alias not found.")); }; - let servers = room_available_servers(&room_id, &room_alias, &None); - - debug!("room alias servers for room ID {room_id} and room alias {room_alias}"); + let servers = room_available_servers(&room_id, &room_alias, &pre_servers); + debug!(?room_alias, ?room_id, "available servers: {servers:?}"); Ok(get_alias::v3::Response::new(room_id, servers)) } @@ -250,19 +138,3 @@ fn room_available_servers( servers } - -async fn alias_checks(room_alias: &RoomAliasId, appservice_info: &Option) -> Result<()> { - if !server_is_ours(room_alias.server_name()) { - return Err(Error::BadRequest(ErrorKind::InvalidParam, "Alias is from another server.")); - } - - if let Some(ref info) = appservice_info { - if !info.aliases.is_match(room_alias.as_str()) { - return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias is not in namespace.")); - } - } else if services().appservice.is_exclusive_alias(room_alias).await { - return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias reserved by appservice.")); - } - - Ok(()) -} diff --git a/src/api/client/membership.rs b/src/api/client/membership.rs index 88cbdd44..b09d8e08 100644 --- a/src/api/client/membership.rs +++ b/src/api/client/membership.rs @@ -36,7 +36,6 @@ use serde_json::value::{to_raw_value, RawValue as RawJsonValue}; use tokio::sync::RwLock; use tracing::{debug, error, info, trace, warn}; -use super::get_alias_helper; use crate::{ client::{update_avatar_url, update_displayname}, service::{ @@ -259,17 +258,24 @@ pub(crate) async fn join_room_by_id_or_alias_route( (servers, room_id) }, Err(room_alias) => { - let response = get_alias_helper(room_alias.clone(), Some(body.server_name.clone())).await?; + let response = services() + .rooms + .alias + .resolve_alias(&room_alias, Some(&body.server_name.clone())) + .await?; + let (room_id, mut pre_servers) = response; - banned_room_check(sender_user, Some(&response.room_id), Some(room_alias.server_name()), client).await?; + banned_room_check(sender_user, Some(&room_id), Some(room_alias.server_name()), client).await?; let mut servers = body.server_name; - servers.extend(response.servers); + if let Some(pre_servers) = &mut pre_servers { + servers.append(pre_servers); + } servers.extend( services() .rooms .state_cache - .servers_invite_via(&response.room_id) + .servers_invite_via(&room_id) .filter_map(Result::ok), ); @@ -277,7 +283,7 @@ pub(crate) async fn join_room_by_id_or_alias_route( services() .rooms .state_cache - .invite_state(sender_user, &response.room_id)? + .invite_state(sender_user, &room_id)? .unwrap_or_default() .iter() .filter_map(|event| serde_json::from_str(event.json().get()).ok()) @@ -287,7 +293,7 @@ pub(crate) async fn join_room_by_id_or_alias_route( .map(|user| user.server_name().to_owned()), ); - (servers, response.room_id) + (servers, room_id) }, }; diff --git a/src/api/client/mod.rs b/src/api/client/mod.rs index 1461be94..79088a5b 100644 --- a/src/api/client/mod.rs +++ b/src/api/client/mod.rs @@ -35,7 +35,6 @@ pub(super) mod user_directory; pub(super) mod voip; pub(super) use account::*; -pub use alias::get_alias_helper; pub(super) use alias::*; pub(super) use backup::*; pub(super) use capabilities::*; diff --git a/src/service/rooms/alias/mod.rs b/src/service/rooms/alias/mod.rs index cfa4edb9..6397ef7f 100644 --- a/src/service/rooms/alias/mod.rs +++ b/src/service/rooms/alias/mod.rs @@ -1,4 +1,5 @@ mod data; +mod remote; use std::sync::Arc; @@ -6,15 +7,15 @@ use conduit::{Error, Result, Server}; use data::Data; use database::Database; use ruma::{ - api::client::error::ErrorKind, + api::{appservice, client::error::ErrorKind}, events::{ room::power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent}, StateEventType, }, - OwnedRoomAliasId, OwnedRoomId, RoomAliasId, RoomId, UserId, + OwnedRoomAliasId, OwnedRoomId, OwnedServerName, RoomAliasId, RoomId, UserId, }; -use crate::services; +use crate::{appservice::RegistrationInfo, server_is_ours, services}; pub struct Service { db: Data, @@ -51,6 +52,30 @@ impl Service { } } + #[tracing::instrument(skip(self), name = "resolve")] + pub async fn resolve_alias( + &self, room_alias: &RoomAliasId, servers: Option<&Vec>, + ) -> Result<(OwnedRoomId, Option>)> { + if !server_is_ours(room_alias.server_name()) + && (!servers + .as_ref() + .is_some_and(|servers| servers.contains(&services().globals.server_name().to_owned())) + || servers.as_ref().is_none()) + { + return remote::resolve(room_alias, servers).await; + } + + let room_id: Option = match self.resolve_local_alias(room_alias)? { + Some(r) => Some(r), + None => self.resolve_appservice_alias(room_alias).await?, + }; + + room_id.map_or_else( + || Err(Error::BadRequest(ErrorKind::NotFound, "Room with alias not found.")), + |room_id| Ok((room_id, None)), + ) + } + #[tracing::instrument(skip(self))] pub fn resolve_local_alias(&self, alias: &RoomAliasId) -> Result> { self.db.resolve_local_alias(alias) @@ -112,4 +137,48 @@ impl Service { Err(Error::bad_database("Room has no m.room.create event")) } } + + async fn resolve_appservice_alias(&self, room_alias: &RoomAliasId) -> Result> { + for appservice in services().appservice.read().await.values() { + if appservice.aliases.is_match(room_alias.as_str()) + && matches!( + services() + .sending + .send_appservice_request( + appservice.registration.clone(), + appservice::query::query_room_alias::v1::Request { + room_alias: room_alias.to_owned(), + }, + ) + .await, + Ok(Some(_opt_result)) + ) { + return Ok(Some( + services() + .rooms + .alias + .resolve_local_alias(room_alias)? + .ok_or_else(|| Error::bad_config("Room does not exist."))?, + )); + } + } + + Ok(None) + } +} + +pub async fn appservice_checks(room_alias: &RoomAliasId, appservice_info: &Option) -> Result<()> { + if !server_is_ours(room_alias.server_name()) { + return Err(Error::BadRequest(ErrorKind::InvalidParam, "Alias is from another server.")); + } + + if let Some(ref info) = appservice_info { + if !info.aliases.is_match(room_alias.as_str()) { + return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias is not in namespace.")); + } + } else if services().appservice.is_exclusive_alias(room_alias).await { + return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias reserved by appservice.")); + } + + Ok(()) } diff --git a/src/service/rooms/alias/remote.rs b/src/service/rooms/alias/remote.rs new file mode 100644 index 00000000..7fcd27f5 --- /dev/null +++ b/src/service/rooms/alias/remote.rs @@ -0,0 +1,71 @@ +use conduit::{debug, debug_info, debug_warn, Error, Result}; +use ruma::{ + api::{client::error::ErrorKind, federation}, + OwnedRoomId, OwnedServerName, RoomAliasId, +}; + +use crate::services; + +pub(super) async fn resolve( + room_alias: &RoomAliasId, servers: Option<&Vec>, +) -> Result<(OwnedRoomId, Option>)> { + debug!(?room_alias, ?servers, "resolve"); + + let mut response = services() + .sending + .send_federation_request( + room_alias.server_name(), + federation::query::get_room_information::v1::Request { + room_alias: room_alias.to_owned(), + }, + ) + .await; + + debug!("room alias server_name get_alias_helper response: {response:?}"); + + if let Err(ref e) = response { + debug_info!( + "Server {} of the original room alias failed to assist in resolving room alias: {e}", + room_alias.server_name() + ); + } + + if response.as_ref().is_ok_and(|resp| resp.servers.is_empty()) || response.as_ref().is_err() { + if let Some(servers) = servers { + for server in servers { + response = services() + .sending + .send_federation_request( + server, + federation::query::get_room_information::v1::Request { + room_alias: room_alias.to_owned(), + }, + ) + .await; + debug!("Got response from server {server} for room aliases: {response:?}"); + + if let Ok(ref response) = response { + if !response.servers.is_empty() { + break; + } + debug_warn!("Server {server} responded with room aliases, but was empty? Response: {response:?}"); + } + } + } + } + + if let Ok(response) = response { + let room_id = response.room_id; + + let mut pre_servers = response.servers; + // since the room alis server responded, insert it into the list + pre_servers.push(room_alias.server_name().into()); + + return Ok((room_id, Some(pre_servers))); + } + + Err(Error::BadRequest( + ErrorKind::NotFound, + "No servers could assist in resolving the room alias", + )) +}