tuwunel_api/server/
make_knock.rs1use RoomVersionId::*;
2use axum::extract::State;
3use futures::TryFutureExt;
4use ruma::{
5 RoomVersionId,
6 api::{
7 error::{ErrorKind, IncompatibleRoomVersionErrorData},
8 federation::membership::prepare_knock_event,
9 },
10 events::room::member::{MembershipState, RoomMemberEventContent},
11};
12use tuwunel_core::{Err, Error, Result, at, debug_warn, matrix::pdu::PduBuilder};
13
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 if !services.metadata.exists(&body.room_id).await {
24 return Err!(Request(NotFound("Room is unknown to this server.")));
25 }
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 services
33 .event_handler
34 .acl_check(body.origin(), &body.room_id)
35 .await?;
36
37 if let Some(server) = body.room_id.server_name()
38 && services
39 .config
40 .is_forbidden_remote_server_name(server)
41 {
42 return Err!(Request(Forbidden("Server is banned on this homeserver.")));
43 }
44
45 let room_version = services
46 .state
47 .get_room_version(&body.room_id)
48 .await?;
49
50 if matches!(room_version, V1 | V2 | V3 | V4 | V5 | V6) {
51 return Err(Error::BadRequest(
52 ErrorKind::IncompatibleRoomVersion(IncompatibleRoomVersionErrorData::new(
53 room_version.clone(),
54 )),
55 "Room version does not support knocking.",
56 ));
57 }
58
59 if !body.ver.contains(&room_version) {
60 return Err(Error::BadRequest(
61 ErrorKind::IncompatibleRoomVersion(IncompatibleRoomVersionErrorData::new(
62 room_version.clone(),
63 )),
64 "Your homeserver does not support the features required to knock on this room.",
65 ));
66 }
67
68 let state_lock = services.state.mutex.lock(&body.room_id).await;
69
70 if let Ok(membership) = services
71 .state_accessor
72 .get_member(&body.room_id, &body.user_id)
73 .await && membership.membership == MembershipState::Ban
74 {
75 debug_warn!(
76 "Remote user {} is banned from {} but attempted to knock",
77 &body.user_id,
78 &body.room_id
79 );
80
81 return Err!(Request(Forbidden("You cannot knock on a room you are banned from.")));
82 }
83
84 let pdu_json = services
85 .timeline
86 .create_hash_and_sign_event(
87 PduBuilder::state(
88 body.user_id.to_string(),
89 &RoomMemberEventContent::new(MembershipState::Knock),
90 ),
91 &body.user_id,
92 &body.room_id,
93 &state_lock,
94 )
95 .map_ok(at!(1))
96 .await?;
97
98 drop(state_lock);
99
100 let event = services
101 .federation
102 .format_pdu_into(pdu_json, Some(&room_version))
103 .await;
104
105 Ok(prepare_knock_event::v1::Response { room_version, event })
107}