From 697c5ac5349369cb79c90ee4078c24bd87afa26f Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Mon, 3 Jun 2024 19:54:50 -0700 Subject: [PATCH] allow searching in left rooms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The spec states that > The search API allows clients to perform full text search across > events in all rooms that the user has been in, including those that they > have left. Only events that the user is allowed to see will be > searched, e.g. it won’t include events in rooms that happened after > you left. --- src/api/client_server/search.rs | 4 +-- src/database/key_value/rooms/state_cache.rs | 33 +++++++++++++++++++++ src/service/rooms/state_cache.rs | 9 ++++++ src/service/rooms/state_cache/data.rs | 6 ++++ 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/api/client_server/search.rs b/src/api/client_server/search.rs index 115b01b9..897ff3b6 100644 --- a/src/api/client_server/search.rs +++ b/src/api/client_server/search.rs @@ -35,7 +35,7 @@ pub(crate) async fn search_events_route( services() .rooms .state_cache - .rooms_joined(sender_user) + .rooms_once_joined(sender_user) .filter_map(Result::ok) .collect() }); @@ -51,7 +51,7 @@ pub(crate) async fn search_events_route( let mut searches = Vec::new(); for room_id in room_ids { - if !services().rooms.state_cache.is_joined(sender_user, &room_id)? { + if !services().rooms.state_cache.once_joined(sender_user, &room_id)? { return Err(Error::BadRequest( ErrorKind::forbidden(), "You don't have permission to view this room.", diff --git a/src/database/key_value/rooms/state_cache.rs b/src/database/key_value/rooms/state_cache.rs index ce8ce4df..eb70c1a6 100644 --- a/src/database/key_value/rooms/state_cache.rs +++ b/src/database/key_value/rooms/state_cache.rs @@ -615,6 +615,39 @@ impl service::rooms::state_cache::Data for KeyValueDatabase { )) } + /// Returns an iterator over all rooms a user has been in. + #[tracing::instrument(skip(self))] + fn rooms_once_joined<'a>( + &'a self, + user_id: &UserId, + ) -> Box> + 'a> { + let mut prefix = user_id.as_bytes().to_vec(); + prefix.push(0xFF); + + Box::new(self.roomuseroncejoinedids.scan_prefix(prefix).map( + |(key, _)| { + RoomId::parse( + utils::string_from_bytes( + key.rsplit(|&b| b == 0xFF) + .next() + .expect("rsplit always returns an element"), + ) + .map_err(|_| { + Error::bad_database( + "Room ID in roomuseroncejoinedids is invalid \ + unicode.", + ) + })?, + ) + .map_err(|_| { + Error::bad_database( + "Room ID in roomuseroncejoinedids is invalid.", + ) + }) + }, + )) + } + #[tracing::instrument(skip(self))] fn once_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result { let mut userroom_id = user_id.as_bytes().to_vec(); diff --git a/src/service/rooms/state_cache.rs b/src/service/rooms/state_cache.rs index af6c7848..0c7ab5a4 100644 --- a/src/service/rooms/state_cache.rs +++ b/src/service/rooms/state_cache.rs @@ -387,6 +387,15 @@ impl Service { self.db.rooms_left(user_id) } + /// Returns an iterator over all rooms a user has been in. + #[tracing::instrument(skip(self))] + pub(crate) fn rooms_once_joined<'a>( + &'a self, + user_id: &UserId, + ) -> impl Iterator> + 'a { + self.db.rooms_once_joined(user_id) + } + #[tracing::instrument(skip(self))] pub(crate) fn once_joined( &self, diff --git a/src/service/rooms/state_cache/data.rs b/src/service/rooms/state_cache/data.rs index 5369e42f..f5d4ac37 100644 --- a/src/service/rooms/state_cache/data.rs +++ b/src/service/rooms/state_cache/data.rs @@ -125,6 +125,12 @@ pub(crate) trait Data: Send + Sync { + 'a, >; + /// Returns an iterator over all rooms a user has been in. + fn rooms_once_joined<'a>( + &'a self, + user_id: &UserId, + ) -> Box> + 'a>; + fn once_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result; fn is_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result;