Skip to main content

tuwunel_api/server/
utils.rs

1use std::pin::pin;
2
3use futures::{FutureExt, StreamExt, join};
4use ruma::{EventId, OwnedRoomId, RoomId, ServerName};
5use serde::Deserialize;
6use tuwunel_core::{Err, Result, err, implement, is_false, utils::option::OptionExt};
7use tuwunel_service::Services;
8
9pub(super) struct AccessCheck<'a> {
10	pub(super) services: &'a Services,
11	pub(super) origin: &'a ServerName,
12	pub(super) room_id: &'a RoomId,
13	pub(super) event_id: Option<&'a EventId>,
14}
15
16#[implement(AccessCheck, params = "<'_>")]
17pub(super) async fn check(&self) -> Result {
18	let acl_check = self
19		.services
20		.event_handler
21		.acl_check(self.origin, self.room_id)
22		.map(|result| result.is_ok());
23
24	let world_readable = self
25		.services
26		.state_accessor
27		.is_world_readable(self.room_id);
28
29	let server_in_room = self
30		.services
31		.state_cache
32		.server_in_room(self.origin, self.room_id);
33
34	// if any user on our homeserver is trying to knock this room, we'll need to
35	// acknowledge bans or leaves
36	let user_is_knocking = async {
37		let knocked = self
38			.services
39			.state_cache
40			.room_members_knocked(self.room_id);
41		let mut knocked = pin!(knocked);
42
43		knocked.next().await.is_some()
44	};
45
46	let server_can_see = self.event_id.map_async(|event_id| {
47		self.services
48			.state_accessor
49			.server_can_see_event(self.origin, self.room_id, event_id)
50	});
51
52	let (world_readable, server_in_room, server_can_see, acl_check, user_is_knocking) =
53		join!(world_readable, server_in_room, server_can_see, acl_check, user_is_knocking);
54
55	if !acl_check {
56		return Err!(Request(Forbidden("Server access denied.")));
57	}
58
59	if !world_readable && !server_in_room && !user_is_knocking {
60		return Err!(Request(Forbidden("Server is not in room.")));
61	}
62
63	if server_can_see.is_some_and(is_false!()) {
64		return Err!(Request(Forbidden("Server is not allowed to see event.")));
65	}
66
67	Ok(())
68}
69
70pub(super) async fn require_known_room(
71	services: &Services,
72	room_id: &RoomId,
73	origin: &ServerName,
74) -> Result {
75	if !services.metadata.exists(room_id).await {
76		return Err!(Request(NotFound("Room is unknown to this server.")));
77	}
78
79	services
80		.event_handler
81		.acl_check(origin, room_id)
82		.await
83}
84
85pub(super) async fn require_event_in_room(
86	services: &Services,
87	event_id: &EventId,
88	room_id: &RoomId,
89) -> Result {
90	#[derive(Deserialize)]
91	struct PduRoomId {
92		room_id: OwnedRoomId,
93	}
94
95	services
96		.timeline
97		.get::<PduRoomId>(event_id)
98		.await
99		.is_ok_and(|pdu| pdu.room_id == room_id)
100		.then_some(())
101		.ok_or_else(|| err!(Request(NotFound("Event not found."))))
102}