Files
continuwuity/src/api/server/send_knock.rs
T

121 lines
3.3 KiB
Rust

use axum::extract::State;
use conduwuit::{Err, Event, Result, debug_info, err, matrix::pdu::PduEvent, warn};
use futures::FutureExt;
use ruma::{
api::federation::membership::create_knock_event, events::room::member::MembershipState,
};
use crate::{Ruma, server::utils::validate_any_membership_event};
/// # `PUT /_matrix/federation/v1/send_knock/{roomId}/{eventId}`
///
/// Submits a signed knock event.
pub(crate) async fn create_knock_event_v1_route(
State(services): State<crate::State>,
body: Ruma<create_knock_event::v1::Request>,
) -> Result<create_knock_event::v1::Response> {
if services
.moderation
.is_remote_server_forbidden(&body.identity)
{
warn!(
"Server {} tried knocking room ID {} who has a server name that is globally \
forbidden. Rejecting.",
body.identity, &body.room_id,
);
return Err!(Request(Forbidden("Federation denied with {}", body.identity)));
}
if !services.rooms.metadata.exists(&body.room_id).await {
return Err!(Request(NotFound("Room is unknown to this server.")));
}
if !services
.rooms
.state_cache
.server_in_room(services.globals.server_name(), &body.room_id)
.await
{
debug_info!(
origin = body.identity.as_str(),
room_id = %body.room_id,
"Refusing to serve send_knock for room we aren't participating in"
);
return Err!(Request(NotFound("This server is not participating in that room.")));
}
// ACL check origin server
services
.rooms
.event_handler
.acl_check(&body.identity, &body.room_id)
.await?;
let room_version = services.rooms.state.get_room_version(&body.room_id).await?;
let create_event = services
.rooms
.state_accessor
.get_room_create_event(&body.room_id)
.await;
let room_version_rules = room_version.rules().unwrap();
if !room_version_rules.authorization.knocking {
return Err!(Request(Forbidden("Room version does not support knocking.")));
}
let (mut event, target_membership, sender, target) = validate_any_membership_event(
&services,
&body.pdu,
&room_version_rules,
create_event.event_id().to_owned(),
body.room_id.clone(),
body.event_id.clone(),
)
.await?;
if target_membership != MembershipState::Knock {
return Err!(Request(InvalidParam("Invalid membership (expected `knock`)")));
}
if sender.server_name() != body.identity {
return Err!(Request(InvalidParam("Sender belongs to a different server")));
}
if sender != target {
return Err!(Request(InvalidParam("Sender does not match state key")));
}
event.insert("event_id".to_owned(), body.event_id.as_str().into());
let pdu = PduEvent::from_id_val(&body.event_id, event.clone())
.map_err(|e| err!(Request(InvalidParam("Invalid knock event PDU: {e}"))))?;
let mutex_lock = services
.rooms
.event_handler
.mutex_federation
.lock(body.room_id.as_str())
.await;
let pdu_id = services
.rooms
.event_handler
.handle_incoming_pdu(sender.server_name(), &body.room_id, &body.event_id, event, false)
.boxed()
.await?
.ok_or_else(|| err!(Request(InvalidParam("Could not accept as timeline event."))))?;
drop(mutex_lock);
services
.sending
.send_pdu_room(&body.room_id, &pdu_id)
.await?;
let knock_room_state = services
.rooms
.state
.summary_stripped(&pdu, &body.room_id, &sender, true)
.await;
Ok(create_knock_event::v1::Response::new(knock_room_state))
}