simplify client event endpoint

Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
Jason Volk 2025-02-06 10:23:17 +00:00 committed by strawberry
parent 565837ad75
commit 31ab84e928
3 changed files with 71 additions and 53 deletions

View file

@ -1,6 +1,6 @@
use axum::extract::State; use axum::extract::State;
use conduwuit::{ use conduwuit::{
at, is_equal_to, at,
utils::{ utils::{
result::{FlatOk, LogErr}, result::{FlatOk, LogErr},
stream::{BroadbandExt, TryIgnore, WidebandExt}, stream::{BroadbandExt, TryIgnore, WidebandExt},
@ -30,7 +30,7 @@ use service::{
use crate::Ruma; use crate::Ruma;
/// list of safe and common non-state events to ignore if the user is ignored /// list of safe and common non-state events to ignore if the user is ignored
const IGNORED_MESSAGE_TYPES: &[TimelineEventType; 17] = &[ const IGNORED_MESSAGE_TYPES: &[TimelineEventType] = &[
Audio, Audio,
CallInvite, CallInvite,
Emote, Emote,
@ -225,34 +225,50 @@ async fn get_member_event(
.ok() .ok()
} }
#[inline]
pub(crate) async fn ignored_filter( pub(crate) async fn ignored_filter(
services: &Services, services: &Services,
item: PdusIterItem, item: PdusIterItem,
user_id: &UserId, user_id: &UserId,
) -> Option<PdusIterItem> { ) -> Option<PdusIterItem> {
let (_, pdu) = &item; let (_, ref pdu) = item;
is_ignored_pdu(services, pdu, user_id)
.await
.eq(&false)
.then_some(item)
}
#[inline]
pub(crate) async fn is_ignored_pdu(
services: &Services,
pdu: &PduEvent,
user_id: &UserId,
) -> bool {
// exclude Synapse's dummy events from bloating up response bodies. clients // exclude Synapse's dummy events from bloating up response bodies. clients
// don't need to see this. // don't need to see this.
if pdu.kind.to_cow_str() == "org.matrix.dummy_event" { if pdu.kind.to_cow_str() == "org.matrix.dummy_event" {
return None; return true;
} }
if IGNORED_MESSAGE_TYPES.binary_search(&pdu.kind).is_ok() let ignored_type = IGNORED_MESSAGE_TYPES.binary_search(&pdu.kind).is_ok();
&& (services.users.user_is_ignored(&pdu.sender, user_id).await
|| services let ignored_server = services
.server .server
.config .config
.forbidden_remote_server_names .forbidden_remote_server_names
.iter() .contains(pdu.sender().server_name());
.any(is_equal_to!(pdu.sender().server_name())))
if ignored_type
&& (ignored_server || services.users.user_is_ignored(&pdu.sender, user_id).await)
{ {
return None; return true;
} }
Some(item) false
} }
#[inline]
pub(crate) async fn visibility_filter( pub(crate) async fn visibility_filter(
services: &Services, services: &Services,
item: PdusIterItem, item: PdusIterItem,
@ -268,7 +284,16 @@ pub(crate) async fn visibility_filter(
.then_some(item) .then_some(item)
} }
#[inline]
pub(crate) fn event_filter(item: PdusIterItem, filter: &RoomEventFilter) -> Option<PdusIterItem> { pub(crate) fn event_filter(item: PdusIterItem, filter: &RoomEventFilter) -> Option<PdusIterItem> {
let (_, pdu) = &item; let (_, pdu) = &item;
pdu.matches(filter).then_some(item) pdu.matches(filter).then_some(item)
} }
#[cfg_attr(debug_assertions, conduwuit::ctor)]
fn _is_sorted() {
debug_assert!(
IGNORED_MESSAGE_TYPES.is_sorted(),
"IGNORED_MESSAGE_TYPES must be sorted by the developer"
);
}

View file

@ -1,52 +1,44 @@
use axum::extract::State; use axum::extract::State;
use conduwuit::{err, Err, Event, Result}; use conduwuit::{err, Err, Event, Result};
use futures::{try_join, FutureExt, TryFutureExt}; use futures::{future::try_join, FutureExt, TryFutureExt};
use ruma::api::client::room::get_room_event; use ruma::api::client::room::get_room_event;
use crate::{client::ignored_filter, Ruma}; use crate::{client::is_ignored_pdu, Ruma};
/// # `GET /_matrix/client/r0/rooms/{roomId}/event/{eventId}` /// # `GET /_matrix/client/r0/rooms/{roomId}/event/{eventId}`
/// ///
/// Gets a single event. /// Gets a single event.
pub(crate) async fn get_room_event_route( pub(crate) async fn get_room_event_route(
State(services): State<crate::State>, State(ref services): State<crate::State>,
ref body: Ruma<get_room_event::v3::Request>, ref body: Ruma<get_room_event::v3::Request>,
) -> Result<get_room_event::v3::Response> { ) -> Result<get_room_event::v3::Response> {
let event_id = &body.event_id;
let room_id = &body.room_id;
let event = services let event = services
.rooms .rooms
.timeline .timeline
.get_pdu(&body.event_id) .get_pdu(event_id)
.map_err(|_| err!(Request(NotFound("Event {} not found.", &body.event_id)))); .map_err(|_| err!(Request(NotFound("Event {} not found.", event_id))));
let token = services
.rooms
.timeline
.get_pdu_count(&body.event_id)
.map_err(|_| err!(Request(NotFound("Event not found."))));
let visible = services let visible = services
.rooms .rooms
.state_accessor .state_accessor
.user_can_see_event(body.sender_user(), &body.room_id, &body.event_id) .user_can_see_event(body.sender_user(), room_id, event_id)
.map(Ok); .map(Ok);
let (token, mut event, visible) = try_join!(token, event, visible)?; let (mut event, visible) = try_join(event, visible).await?;
if !visible if !visible || is_ignored_pdu(services, &event, body.sender_user()).await {
|| ignored_filter(&services, (token, event.clone()), body.sender_user())
.await
.is_none()
{
return Err!(Request(Forbidden("You don't have permission to view this event."))); return Err!(Request(Forbidden("You don't have permission to view this event.")));
} }
if event.event_id() != &body.event_id || event.room_id() != body.room_id { debug_assert!(
return Err!(Request(NotFound("Event not found"))); event.event_id() == event_id && event.room_id() == room_id,
} "Fetched PDU must match requested"
);
event.add_age().ok(); event.add_age().ok();
let event = event.to_room_event(); Ok(get_room_event::v3::Response { event: event.to_room_event() })
Ok(get_room_event::v3::Response { event })
} }

View file

@ -9,11 +9,13 @@ use crate::{err, implement, is_true, Result};
#[implement(Pdu)] #[implement(Pdu)]
pub fn remove_transaction_id(&mut self) -> Result { pub fn remove_transaction_id(&mut self) -> Result {
use BTreeMap as Map;
let Some(unsigned) = &self.unsigned else { let Some(unsigned) = &self.unsigned else {
return Ok(()); return Ok(());
}; };
let mut unsigned: BTreeMap<String, Box<RawJsonValue>> = serde_json::from_str(unsigned.get()) let mut unsigned: Map<&str, Box<RawJsonValue>> = serde_json::from_str(unsigned.get())
.map_err(|e| err!(Database("Invalid unsigned in pdu event: {e}")))?; .map_err(|e| err!(Database("Invalid unsigned in pdu event: {e}")))?;
unsigned.remove("transaction_id"); unsigned.remove("transaction_id");
@ -26,10 +28,13 @@ pub fn remove_transaction_id(&mut self) -> Result {
#[implement(Pdu)] #[implement(Pdu)]
pub fn add_age(&mut self) -> Result { pub fn add_age(&mut self) -> Result {
let mut unsigned: BTreeMap<String, Box<RawJsonValue>> = self use BTreeMap as Map;
let mut unsigned: Map<&str, Box<RawJsonValue>> = self
.unsigned .unsigned
.as_ref() .as_deref()
.map_or_else(|| Ok(BTreeMap::new()), |u| serde_json::from_str(u.get())) .map(RawJsonValue::get)
.map_or_else(|| Ok(Map::new()), serde_json::from_str)
.map_err(|e| err!(Database("Invalid unsigned in pdu event: {e}")))?; .map_err(|e| err!(Database("Invalid unsigned in pdu event: {e}")))?;
// deliberately allowing for the possibility of negative age // deliberately allowing for the possibility of negative age
@ -37,10 +42,8 @@ pub fn add_age(&mut self) -> Result {
let then: i128 = self.origin_server_ts.into(); let then: i128 = self.origin_server_ts.into();
let this_age = now.saturating_sub(then); let this_age = now.saturating_sub(then);
unsigned.insert("age".to_owned(), to_raw_value(&this_age).expect("age is valid")); unsigned.insert("age", to_raw_value(&this_age)?);
self.unsigned = to_raw_value(&unsigned) self.unsigned = Some(to_raw_value(&unsigned)?);
.map(Some)
.expect("unsigned is valid");
Ok(()) Ok(())
} }
@ -51,8 +54,9 @@ pub fn add_relation(&mut self, name: &str, pdu: Option<&Pdu>) -> Result {
let mut unsigned: Map<String, JsonValue> = self let mut unsigned: Map<String, JsonValue> = self
.unsigned .unsigned
.as_ref() .as_deref()
.map_or_else(|| Ok(Map::new()), |u| serde_json::from_str(u.get())) .map(RawJsonValue::get)
.map_or_else(|| Ok(Map::new()), serde_json::from_str)
.map_err(|e| err!(Database("Invalid unsigned in pdu event: {e}")))?; .map_err(|e| err!(Database("Invalid unsigned in pdu event: {e}")))?;
let pdu = pdu let pdu = pdu
@ -64,12 +68,9 @@ pub fn add_relation(&mut self, name: &str, pdu: Option<&Pdu>) -> Result {
.entry("m.relations") .entry("m.relations")
.or_insert(JsonValue::Object(Map::new())) .or_insert(JsonValue::Object(Map::new()))
.as_object_mut() .as_object_mut()
.unwrap() .map(|object| object.insert(name.to_owned(), pdu));
.insert(name.to_owned(), pdu);
self.unsigned = to_raw_value(&unsigned) self.unsigned = Some(to_raw_value(&unsigned)?);
.map(Some)
.expect("unsigned is valid");
Ok(()) Ok(())
} }