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 let Ok(membership) = services
67 .state_accessor
68 .get_member(&body.room_id, &body.user_id)
69 .await && membership.membership == MembershipState::Ban
70 {
71 debug_warn!(
72 "Remote user {} is banned from {} but attempted to knock",
73 &body.user_id,
74 &body.room_id
75 );
76
77 return Err!(Request(Forbidden("You cannot knock on a room you are banned from.")));
78 }
79
80 let pdu_json = services
81 .timeline
82 .create_hash_and_sign_event(
83 PduBuilder::state(
84 body.user_id.to_string(),
85 &RoomMemberEventContent::new(MembershipState::Knock),
86 ),
87 &body.user_id,
88 &body.room_id,
89 &state_lock,
90 )
91 .map_ok(at!(1))
92 .await?;
93
94 drop(state_lock);
95
96 let event = services
97 .federation
98 .format_pdu_into(pdu_json, Some(&room_version_id))
99 .await;
100
101 Ok(prepare_knock_event::v1::Response { room_version: room_version_id, event })
103}