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