Skip to main content

tuwunel_service/rooms/state_accessor/
server_can.rs

1//! Evaluates federation visibility against historical room state.
2//!
3//! History visibility determines whether an origin may receive an event. A
4//! separate strict membership check supports sender-erasure pruning.
5
6use futures::StreamExt;
7use ruma::{
8	EventId, RoomId, ServerName, UserId,
9	events::{
10		StateEventType,
11		room::history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
12	},
13};
14use tuwunel_core::{implement, utils::stream::ReadyExt};
15
16/// Reports whether a server may see an event over federation.
17///
18/// Missing event state is allowed, and missing or invalid history visibility
19/// defaults to `shared`. For `invited` and `joined`, a currently joined user
20/// from the origin must also hold the required membership at the event.
21#[implement(super::Service)]
22#[tracing::instrument(skip_all, level = "trace")]
23pub async fn server_can_see_event(
24	&self,
25	origin: &ServerName,
26	room_id: &RoomId,
27	event_id: &EventId,
28) -> bool {
29	let Ok(shortstatehash) = self
30		.services
31		.state
32		.pdu_shortstatehash(event_id)
33		.await
34	else {
35		return true;
36	};
37
38	let history_visibility = self
39		.state_get_content(shortstatehash, &StateEventType::RoomHistoryVisibility, "")
40		.await
41		.map_or(HistoryVisibility::Shared, |c: RoomHistoryVisibilityEventContent| {
42			c.history_visibility
43		});
44
45	let current_server_members = self
46		.services
47		.state_cache
48		.room_members(room_id)
49		.ready_filter(|member| member.server_name() == origin);
50
51	match history_visibility {
52		| HistoryVisibility::Invited => {
53			// Allow if any member on requesting server was AT LEAST invited, else deny
54			current_server_members
55				.any(|member| self.user_was_invited(shortstatehash, member))
56				.await
57		},
58		| HistoryVisibility::Joined => {
59			// Allow if any member on requested server was joined, else deny
60			current_server_members
61				.any(|member| self.user_was_joined(shortstatehash, member))
62				.await
63		},
64		| HistoryVisibility::WorldReadable | HistoryVisibility::Shared | _ => true,
65	}
66}
67
68/// Reports whether any user from an origin was joined at an event.
69///
70/// This MSC4025 helper scans membership state at the event and denies when the
71/// snapshot cannot be resolved. Invalid user state keys are skipped.
72#[implement(super::Service)]
73#[tracing::instrument(skip_all, level = "trace")]
74pub async fn server_joined_at_pdu(&self, origin: &ServerName, event_id: &EventId) -> bool {
75	let Ok(shortstatehash) = self
76		.services
77		.state
78		.pdu_shortstatehash(event_id)
79		.await
80	else {
81		return false;
82	};
83
84	self.state_keys(shortstatehash, &StateEventType::RoomMember)
85		.ready_filter_map(|state_key| UserId::parse(state_key.as_str()).ok())
86		.ready_filter(|user_id| user_id.server_name() == origin)
87		.any(async |user_id| {
88			self.user_was_joined(shortstatehash, &user_id)
89				.await
90		})
91		.await
92}