diff --git a/src/api/client/directory.rs b/src/api/client/directory.rs index 629f2993..76bf5269 100644 --- a/src/api/client/directory.rs +++ b/src/api/client/directory.rs @@ -33,7 +33,7 @@ use tuwunel_core::{ TryFutureExtExt, math::Expected, result::FlatOk, - stream::{ReadyExt, WidebandExt}, + stream::{IterStream, ReadyExt, WidebandExt}, }, }; use tuwunel_service::Services; @@ -284,32 +284,62 @@ pub(crate) async fn get_public_rooms_filtered_helper( } } + let search_term = filter + .generic_search_term + .as_deref() + .map(str::to_lowercase); + + let search_room_id = filter + .generic_search_term + .as_deref() + .filter(|_| services.config.allow_public_room_search_by_id) + .filter(|s| s.starts_with('!')) + .filter(|s| s.len() > 5); // require some characters to limit scope. + + let meta_public_rooms = search_room_id + .filter(|_| services.config.allow_unlisted_room_search_by_id) + .map(|prefix| services.rooms.metadata.public_ids_prefix(prefix)) + .into_iter() + .stream() + .flatten(); + let mut all_rooms: Vec = services .rooms .directory .public_rooms() .map(ToOwned::to_owned) + .chain(meta_public_rooms) .wide_then(|room_id| public_rooms_chunk(services, room_id)) .ready_filter_map(|chunk| { - if !filter.room_types.is_empty() && !filter.room_types.contains(&RoomTypeFilter::from(chunk.room_type.clone())) { + if !filter.room_types.is_empty() + && !filter + .room_types + .contains(&RoomTypeFilter::from(chunk.room_type.clone())) + { return None; } - if let Some(query) = filter.generic_search_term.as_ref().map(|q| q.to_lowercase()) { + if let Some(query) = search_room_id { + if chunk.room_id.as_str().contains(query) { + return Some(chunk); + } + } + + if let Some(query) = search_term.as_deref() { if let Some(name) = &chunk.name { - if name.as_str().to_lowercase().contains(&query) { + if name.as_str().to_lowercase().contains(query) { return Some(chunk); } } if let Some(topic) = &chunk.topic { - if topic.to_lowercase().contains(&query) { + if topic.to_lowercase().contains(query) { return Some(chunk); } } if let Some(canonical_alias) = &chunk.canonical_alias { - if canonical_alias.as_str().to_lowercase().contains(&query) { + if canonical_alias.as_str().to_lowercase().contains(query) { return Some(chunk); } } diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index fd796b45..48261ddc 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -598,6 +598,26 @@ pub struct Config { #[serde(default)] pub allow_public_room_directory_without_auth: bool, + /// Allows room directory searches to match on partial room_id's when the + /// search term starts with '!'. + /// + /// default: true + #[serde(default = "true_fn")] + pub allow_public_room_search_by_id: bool, + + /// Set this to false to limit results of rooms when searching by ID to + /// those that would be found by an alias or other query; specifically + /// those listed in the public rooms directory. By default this is set to + /// true allowing any joinable room to match. This satisfies the Principle + /// of Least Expectation when pasting a room_id into a search box with + /// intent to join; many rooms simply opt-out of public listings. Therefor + /// to prevent this feature from abuse, knowledge of several characters of + /// the room_id is required before any results are returned. + /// + /// default: true + #[serde(default = "true_fn")] + pub allow_unlisted_room_search_by_id: bool, + /// Allow guests/unauthenticated users to access TURN credentials. /// /// This is the equivalent of Synapse's `turn_allow_guests` config option. diff --git a/tuwunel-example.toml b/tuwunel-example.toml index fc051c43..51732371 100644 --- a/tuwunel-example.toml +++ b/tuwunel-example.toml @@ -473,6 +473,22 @@ # #allow_public_room_directory_without_auth = false +# Allows room directory searches to match on partial room_id's when the +# search term starts with '!'. +# +#allow_public_room_search_by_id = true + +# Set this to false to limit results of rooms when searching by ID to +# those that would be found by an alias or other query; specifically +# those listed in the public rooms directory. By default this is set to +# true allowing any joinable room to match. This satisfies the Principle +# of Least Expectation when pasting a room_id into a search box with +# intent to join; many rooms simply opt-out of public listings. Therefor +# to prevent this feature from abuse, knowledge of several characters of +# the room_id is required before any results are returned. +# +#allow_unlisted_room_search_by_id = true + # Allow guests/unauthenticated users to access TURN credentials. # # This is the equivalent of Synapse's `turn_allow_guests` config option.