tuwunel_api/server/
make_knock.rs1use axum::extract::State;
2use futures::TryFutureExt;
3use ruma::{
4 api::{
5 error::{ErrorKind, IncompatibleRoomVersionErrorData},
6 federation::membership::prepare_knock_event,
7 },
8 events::room::member::{MembershipState, RoomMemberEventContent},
9};
10use tuwunel_core::{
11 Err, Error, Result, at, debug_warn,
12 matrix::{pdu::PduBuilder, room_version},
13};
14
15use super::utils::require_known_room;
16use crate::Ruma;
17
18pub(crate) async fn create_knock_event_template_route(
22 State(services): State<crate::State>,
23 body: Ruma<prepare_knock_event::v1::Request>,
24) -> Result<prepare_knock_event::v1::Response> {
25 require_known_room(&services, &body.room_id, body.origin()).await?;
26
27 if body.user_id.server_name() != body.origin() {
28 return Err!(Request(BadJson("Not allowed to knock on behalf of another server/user.")));
29 }
30
31 if let Some(server) = body.room_id.server_name()
32 && services
33 .config
34 .is_forbidden_remote_server_name(server)
35 {
36 return Err!(Request(Forbidden("Server is banned on this homeserver.")));
37 }
38
39 let room_version_id = services
40 .state
41 .get_room_version(&body.room_id)
42 .await?;
43
44 if !body.ver.contains(&room_version_id) {
45 return Err(Error::BadRequest(
46 ErrorKind::IncompatibleRoomVersion(IncompatibleRoomVersionErrorData::new(
47 room_version_id,
48 )),
49 "Your homeserver does not support the features required to knock on this room.",
50 ));
51 }
52
53 let room_version_rules = room_version::rules(&room_version_id)?;
54
55 if !room_version_rules.authorization.knocking {
56 return Err(Error::BadRequest(
57 ErrorKind::IncompatibleRoomVersion(IncompatibleRoomVersionErrorData::new(
58 room_version_id,
59 )),
60 "Room version does not support knocking.",
61 ));
62 }
63
64 let state_lock = services.state.mutex.lock(&body.room_id).await;
65
66 if services
67 .state_accessor
68 .get_member(&body.room_id, &body.user_id)
69 .await
70 .is_ok_and(|content| content.membership == MembershipState::Ban)
71 {
72 debug_warn!(
73 user_id = %body.user_id,
74 room_id = %body.room_id,
75 "Banned remote user attempted to knock."
76 );
77
78 return Err!(Request(Forbidden("You cannot knock on a room you are banned from.")));
79 }
80
81 let pdu_json = services
82 .timeline
83 .create_hash_and_sign_event(
84 PduBuilder::state(
85 body.user_id.to_string(),
86 &RoomMemberEventContent::new(MembershipState::Knock),
87 ),
88 &body.user_id,
89 &body.room_id,
90 &state_lock,
91 )
92 .map_ok(at!(1))
93 .await?;
94
95 drop(state_lock);
96
97 let event = services
98 .federation
99 .format_pdu_into(pdu_json, Some(&room_version_id))
100 .await;
101
102 Ok(prepare_knock_event::v1::Response { room_version: room_version_id, event })
104}