2024-11-10 20:16:38 -05:00
|
|
|
use axum::extract::State;
|
2026-04-10 11:47:01 -04:00
|
|
|
use conduwuit::{Err, Error, Result, debug_warn, info, matrix::pdu::PartialPdu, utils, warn};
|
2024-11-10 20:16:38 -05:00
|
|
|
use ruma::{
|
2026-04-13 11:33:23 -04:00
|
|
|
api::{
|
|
|
|
|
error::{ErrorKind, IncompatibleRoomVersionErrorData},
|
|
|
|
|
federation::membership::prepare_knock_event,
|
|
|
|
|
},
|
2024-11-10 20:16:38 -05:00
|
|
|
events::room::member::{MembershipState, RoomMemberEventContent},
|
|
|
|
|
};
|
|
|
|
|
use serde_json::value::to_raw_value;
|
|
|
|
|
|
2025-04-04 03:30:13 +00:00
|
|
|
use crate::Ruma;
|
2024-11-10 20:16:38 -05:00
|
|
|
|
|
|
|
|
/// # `GET /_matrix/federation/v1/make_knock/{roomId}/{userId}`
|
|
|
|
|
///
|
|
|
|
|
/// Creates a knock template.
|
|
|
|
|
pub(crate) async fn create_knock_event_template_route(
|
2025-01-11 18:43:54 -05:00
|
|
|
State(services): State<crate::State>,
|
2026-04-13 11:33:23 -04:00
|
|
|
body: Ruma<prepare_knock_event::v1::Request>,
|
|
|
|
|
) -> Result<prepare_knock_event::v1::Response> {
|
2024-11-10 20:16:38 -05:00
|
|
|
if !services.rooms.metadata.exists(&body.room_id).await {
|
2024-12-07 01:07:01 -05:00
|
|
|
return Err!(Request(NotFound("Room is unknown to this server.")));
|
2024-11-10 20:16:38 -05:00
|
|
|
}
|
2026-01-27 03:16:49 +00:00
|
|
|
if !services
|
|
|
|
|
.rooms
|
|
|
|
|
.state_cache
|
|
|
|
|
.server_in_room(services.globals.server_name(), &body.room_id)
|
|
|
|
|
.await
|
|
|
|
|
{
|
2026-01-27 23:26:14 +00:00
|
|
|
info!(
|
2026-05-26 13:28:23 -04:00
|
|
|
origin = body.identity.as_str(),
|
2026-04-07 18:18:00 +01:00
|
|
|
room_id = %body.room_id,
|
2026-01-27 03:16:49 +00:00
|
|
|
"Refusing to serve make_knock for room we aren't participating in"
|
|
|
|
|
);
|
|
|
|
|
return Err!(Request(NotFound("This server is not participating in that room.")));
|
|
|
|
|
}
|
2024-11-10 20:16:38 -05:00
|
|
|
|
2026-05-26 13:28:23 -04:00
|
|
|
if body.user_id.server_name() != body.identity {
|
2024-12-07 01:07:01 -05:00
|
|
|
return Err!(Request(BadJson("Not allowed to knock on behalf of another server/user.")));
|
2024-11-10 20:16:38 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ACL check origin server
|
|
|
|
|
services
|
|
|
|
|
.rooms
|
|
|
|
|
.event_handler
|
2026-05-26 13:28:23 -04:00
|
|
|
.acl_check(&body.identity, &body.room_id)
|
2024-11-10 20:16:38 -05:00
|
|
|
.await?;
|
|
|
|
|
|
|
|
|
|
if services
|
2025-04-19 23:02:43 +01:00
|
|
|
.moderation
|
2026-05-26 13:28:23 -04:00
|
|
|
.is_remote_server_forbidden(&body.identity)
|
2024-11-10 20:16:38 -05:00
|
|
|
{
|
|
|
|
|
warn!(
|
2025-01-11 18:43:54 -05:00
|
|
|
"Server {} for remote user {} tried knocking room ID {} which has a server name \
|
|
|
|
|
that is globally forbidden. Rejecting.",
|
2026-05-26 13:28:23 -04:00
|
|
|
body.identity, &body.user_id, &body.room_id,
|
2024-11-10 20:16:38 -05:00
|
|
|
);
|
|
|
|
|
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(server) = body.room_id.server_name() {
|
2025-04-19 23:02:43 +01:00
|
|
|
if services.moderation.is_remote_server_forbidden(server) {
|
2024-11-10 20:16:38 -05:00
|
|
|
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-13 11:33:23 -04:00
|
|
|
let room_version = services.rooms.state.get_room_version(&body.room_id).await?;
|
|
|
|
|
let room_version_rules = room_version.rules().unwrap();
|
2024-11-10 20:16:38 -05:00
|
|
|
|
2026-04-13 11:33:23 -04:00
|
|
|
if !room_version_rules.authorization.knocking {
|
2024-11-10 20:16:38 -05:00
|
|
|
return Err(Error::BadRequest(
|
2026-04-13 11:33:23 -04:00
|
|
|
ErrorKind::IncompatibleRoomVersion(IncompatibleRoomVersionErrorData::new(
|
|
|
|
|
room_version,
|
|
|
|
|
)),
|
2024-11-10 20:16:38 -05:00
|
|
|
"Room version does not support knocking.",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-13 11:33:23 -04:00
|
|
|
if !body.ver.contains(&room_version) {
|
2024-11-10 20:16:38 -05:00
|
|
|
return Err(Error::BadRequest(
|
2026-04-13 11:33:23 -04:00
|
|
|
ErrorKind::IncompatibleRoomVersion(IncompatibleRoomVersionErrorData::new(
|
|
|
|
|
room_version,
|
|
|
|
|
)),
|
2024-11-10 20:16:38 -05:00
|
|
|
"Your homeserver does not support the features required to knock on this room.",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-13 11:33:23 -04:00
|
|
|
let state_lock = services.rooms.state.mutex.lock(body.room_id.as_str()).await;
|
2024-11-10 20:16:38 -05:00
|
|
|
|
2025-01-11 18:43:54 -05:00
|
|
|
if let Ok(membership) = services
|
|
|
|
|
.rooms
|
|
|
|
|
.state_accessor
|
|
|
|
|
.get_member(&body.room_id, &body.user_id)
|
|
|
|
|
.await
|
|
|
|
|
{
|
|
|
|
|
if membership.membership == MembershipState::Ban {
|
|
|
|
|
debug_warn!(
|
|
|
|
|
"Remote user {} is banned from {} but attempted to knock",
|
|
|
|
|
&body.user_id,
|
|
|
|
|
&body.room_id
|
|
|
|
|
);
|
|
|
|
|
return Err!(Request(Forbidden("You cannot knock on a room you are banned from.")));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-07 18:18:00 +01:00
|
|
|
let (pdu, _) = services
|
2024-11-10 20:16:38 -05:00
|
|
|
.rooms
|
|
|
|
|
.timeline
|
2026-04-07 18:18:00 +01:00
|
|
|
.create_event(
|
2026-04-10 11:47:01 -04:00
|
|
|
PartialPdu::state(
|
2025-01-11 18:43:54 -05:00
|
|
|
body.user_id.to_string(),
|
|
|
|
|
&RoomMemberEventContent::new(MembershipState::Knock),
|
|
|
|
|
),
|
2024-11-10 20:16:38 -05:00
|
|
|
&body.user_id,
|
2025-09-17 20:46:03 +00:00
|
|
|
Some(&body.room_id),
|
2024-11-10 20:16:38 -05:00
|
|
|
&state_lock,
|
|
|
|
|
)
|
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
|
|
drop(state_lock);
|
2026-04-07 18:18:00 +01:00
|
|
|
let mut pdu_json = utils::to_canonical_object(&pdu)
|
|
|
|
|
.expect("Barebones PDU should be convertible to canonical JSON");
|
|
|
|
|
pdu_json.remove("event_id");
|
2024-11-10 20:16:38 -05:00
|
|
|
|
2026-04-13 11:33:23 -04:00
|
|
|
Ok(prepare_knock_event::v1::Response::new(
|
|
|
|
|
room_version,
|
|
|
|
|
to_raw_value(&pdu_json).expect("CanonicalJson can be serialized to JSON"),
|
|
|
|
|
))
|
2024-11-10 20:16:38 -05:00
|
|
|
}
|