Skip to main content

tuwunel_api/server/
utils.rs

1use futures::{FutureExt, StreamExt, join};
2use ruma::{EventId, RoomId, ServerName};
3use tuwunel_core::{Err, Result, implement, is_false, utils::option::OptionExt};
4use tuwunel_service::Services;
5
6pub(super) struct AccessCheck<'a> {
7	pub(super) services: &'a Services,
8	pub(super) origin: &'a ServerName,
9	pub(super) room_id: &'a RoomId,
10	pub(super) event_id: Option<&'a EventId>,
11}
12
13#[implement(AccessCheck, params = "<'_>")]
14pub(super) async fn check(&self) -> Result {
15	let acl_check = self
16		.services
17		.event_handler
18		.acl_check(self.origin, self.room_id)
19		.map(|result| result.is_ok());
20
21	let world_readable = self
22		.services
23		.state_accessor
24		.is_world_readable(self.room_id);
25
26	let server_in_room = self
27		.services
28		.state_cache
29		.server_in_room(self.origin, self.room_id);
30
31	// if any user on our homeserver is trying to knock this room, we'll need to
32	// acknowledge bans or leaves
33	let user_is_knocking = self
34		.services
35		.state_cache
36		.room_members_knocked(self.room_id)
37		.count();
38
39	let server_can_see = self.event_id.map_async(|event_id| {
40		self.services
41			.state_accessor
42			.server_can_see_event(self.origin, self.room_id, event_id)
43	});
44
45	let (world_readable, server_in_room, server_can_see, acl_check, user_is_knocking) =
46		join!(world_readable, server_in_room, server_can_see, acl_check, user_is_knocking);
47
48	if !acl_check {
49		return Err!(Request(Forbidden("Server access denied.")));
50	}
51
52	if !world_readable && !server_in_room && user_is_knocking == 0 {
53		return Err!(Request(Forbidden("Server is not in room.")));
54	}
55
56	if server_can_see.is_some_and(is_false!()) {
57		return Err!(Request(Forbidden("Server is not allowed to see event.")));
58	}
59
60	Ok(())
61}