Skip to main content

tuwunel_service/rooms/state_accessor/
server_can.rs

1use futures::StreamExt;
2use ruma::{
3	EventId, RoomId, ServerName, UserId,
4	events::{
5		StateEventType,
6		room::history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
7	},
8};
9use tuwunel_core::{implement, utils::stream::ReadyExt};
10
11/// Whether a server is allowed to see an event through federation, based on
12/// the room's history_visibility at that event's state.
13#[implement(super::Service)]
14#[tracing::instrument(skip_all, level = "trace")]
15pub async fn server_can_see_event(
16	&self,
17	origin: &ServerName,
18	room_id: &RoomId,
19	event_id: &EventId,
20) -> bool {
21	let Ok(shortstatehash) = self
22		.services
23		.state
24		.pdu_shortstatehash(event_id)
25		.await
26	else {
27		return true;
28	};
29
30	let history_visibility = self
31		.state_get_content(shortstatehash, &StateEventType::RoomHistoryVisibility, "")
32		.await
33		.map_or(HistoryVisibility::Shared, |c: RoomHistoryVisibilityEventContent| {
34			c.history_visibility
35		});
36
37	let current_server_members = self
38		.services
39		.state_cache
40		.room_members(room_id)
41		.ready_filter(|member| member.server_name() == origin);
42
43	match history_visibility {
44		| HistoryVisibility::Invited => {
45			// Allow if any member on requesting server was AT LEAST invited, else deny
46			current_server_members
47				.any(|member| self.user_was_invited(shortstatehash, member))
48				.await
49		},
50		| HistoryVisibility::Joined => {
51			// Allow if any member on requested server was joined, else deny
52			current_server_members
53				.any(|member| self.user_was_joined(shortstatehash, member))
54				.await
55		},
56		| HistoryVisibility::WorldReadable | HistoryVisibility::Shared | _ => true,
57	}
58}
59
60/// MSC4025: whether any of `origin`'s users were joined in the room state at
61/// the given event. Unresolvable state denies, matching the reference
62/// implementation's erasure rule.
63#[implement(super::Service)]
64#[tracing::instrument(skip_all, level = "trace")]
65pub async fn server_joined_at_pdu(&self, origin: &ServerName, event_id: &EventId) -> bool {
66	let Ok(shortstatehash) = self
67		.services
68		.state
69		.pdu_shortstatehash(event_id)
70		.await
71	else {
72		return false;
73	};
74
75	self.state_keys(shortstatehash, &StateEventType::RoomMember)
76		.ready_filter_map(|state_key| UserId::parse(state_key.as_str()).ok())
77		.ready_filter(|user_id| user_id.server_name() == origin)
78		.any(async |user_id| {
79			self.user_was_joined(shortstatehash, &user_id)
80				.await
81		})
82		.await
83}