From b85110a2928a982f310d606c170a04d4d6964410 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Fri, 10 May 2024 19:00:57 -0700 Subject: [PATCH] implement per-event state filtering for invited rooms on /sync This one is a little weird, because the stripped invite state events are not deserialized. --- src/api/client_server/sync.rs | 7 +++++- src/utils/filter.rs | 43 +++++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index 23937b12..5ff886fe 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -262,9 +262,14 @@ pub(crate) async fn sync_events_route( continue; } + let state_events = invite_state_events + .into_iter() + .filter(|event| compiled_filter.room.state.raw_event_allowed(event)) + .collect(); + let invited_room = InvitedRoom { invite_state: InviteState { - events: invite_state_events, + events: state_events, }, }; if !invited_room.is_empty() { diff --git a/src/utils/filter.rs b/src/utils/filter.rs index 18ff18b4..47afaba7 100644 --- a/src/utils/filter.rs +++ b/src/utils/filter.rs @@ -16,15 +16,18 @@ //! events in a particular category, we can skip work generating events in that //! category for the rejected room. -use std::{collections::HashSet, hash::Hash}; +use std::{borrow::Cow, collections::HashSet, hash::Hash}; use regex::RegexSet; use ruma::{ api::client::filter::{ FilterDefinition, RoomEventFilter, RoomFilter, UrlFilter, }, - RoomId, UserId, + serde::Raw, + OwnedUserId, RoomId, UserId, }; +use serde::Deserialize; +use tracing::error; use crate::{Error, PduEvent}; @@ -270,6 +273,42 @@ impl CompiledRoomEventFilter<'_> { && self.allowed_by_url_filter(pdu) } + /// Similar to [`CompiledRoomEventFilter::pdu_event_allowed`] but takes raw + /// JSON. + pub(crate) fn raw_event_allowed(&self, event: &Raw) -> bool { + // We need to deserialize some of the fields from the raw json, but + // don't need all of them. Fully deserializing to a ruma event type + // would involve a lot extra copying and validation. + #[derive(Deserialize)] + struct LimitedEvent<'a> { + sender: OwnedUserId, + #[serde(rename = "type")] + kind: Cow<'a, str>, + url: Option>, + } + + let event = match event.deserialize_as::>() { + Ok(event) => event, + Err(e) => { + // TODO: maybe rephrase this error, or propagate it to the + // caller + error!("invalid event in database: {e}"); + return false; + } + }; + + let allowed_by_url_filter = match self.url_filter { + None => true, + Some(UrlFilter::EventsWithoutUrl) => event.url.is_none(), + Some(UrlFilter::EventsWithUrl) => event.url.is_some(), + }; + + allowed_by_url_filter + && self.senders.allowed(&event.sender) + && self.types.allowed(&event.kind) + } + + // TODO: refactor this as well? fn allowed_by_url_filter(&self, pdu: &PduEvent) -> bool { let Some(filter) = self.url_filter else { return true;