2024-07-16 08:05:25 +00:00
|
|
|
use axum::extract::State;
|
2024-06-10 21:04:51 -04:00
|
|
|
use axum_client_ip::InsecureClientIp;
|
2025-02-23 01:17:45 -05:00
|
|
|
use base64::{Engine as _, engine::general_purpose};
|
2024-06-05 04:32:58 +00:00
|
|
|
use ruma::{
|
2025-02-23 01:17:45 -05:00
|
|
|
CanonicalJsonValue, OwnedUserId, UserId,
|
2025-06-29 03:33:29 +00:00
|
|
|
api::{
|
|
|
|
|
client::error::ErrorKind,
|
|
|
|
|
federation::membership::{RawStrippedState, create_invite},
|
|
|
|
|
},
|
2024-06-05 04:32:58 +00:00
|
|
|
events::room::member::{MembershipState, RoomMemberEventContent},
|
|
|
|
|
serde::JsonObject,
|
|
|
|
|
};
|
2025-04-22 01:41:02 +00:00
|
|
|
use tuwunel_core::{
|
2025-06-29 03:33:29 +00:00
|
|
|
Err, Error, Result, err, extract_variant,
|
2025-10-01 23:01:57 +00:00
|
|
|
matrix::{Event, PduCount, PduEvent, event::gen_event_id},
|
2025-04-27 09:34:07 +00:00
|
|
|
utils,
|
|
|
|
|
utils::hash::sha256,
|
|
|
|
|
warn,
|
2025-04-22 01:41:02 +00:00
|
|
|
};
|
2024-06-05 04:32:58 +00:00
|
|
|
|
2024-07-03 21:05:24 +00:00
|
|
|
use crate::Ruma;
|
2024-06-05 04:32:58 +00:00
|
|
|
|
|
|
|
|
/// # `PUT /_matrix/federation/v2/invite/{roomId}/{eventId}`
|
|
|
|
|
///
|
|
|
|
|
/// Invites a remote user to a room.
|
2024-06-17 04:12:11 +00:00
|
|
|
#[tracing::instrument(skip_all, fields(%client), name = "invite")]
|
2024-06-10 21:04:51 -04:00
|
|
|
pub(crate) async fn create_invite_route(
|
2024-12-15 00:05:47 -05:00
|
|
|
State(services): State<crate::State>,
|
|
|
|
|
InsecureClientIp(client): InsecureClientIp,
|
2024-07-16 08:05:25 +00:00
|
|
|
body: Ruma<create_invite::v2::Request>,
|
2024-06-10 21:04:51 -04:00
|
|
|
) -> Result<create_invite::v2::Response> {
|
2024-06-05 04:32:58 +00:00
|
|
|
// ACL check origin
|
2024-07-16 08:05:25 +00:00
|
|
|
services
|
2024-06-05 04:32:58 +00:00
|
|
|
.event_handler
|
2024-10-24 12:03:56 +00:00
|
|
|
.acl_check(body.origin(), &body.room_id)
|
2024-08-08 17:18:30 +00:00
|
|
|
.await?;
|
2024-06-05 04:32:58 +00:00
|
|
|
|
2025-04-22 04:42:26 +00:00
|
|
|
if !services
|
|
|
|
|
.server
|
|
|
|
|
.supported_room_version(&body.room_version)
|
|
|
|
|
{
|
2024-06-05 04:32:58 +00:00
|
|
|
return Err(Error::BadRequest(
|
2024-12-15 00:05:47 -05:00
|
|
|
ErrorKind::IncompatibleRoomVersion { room_version: body.room_version.clone() },
|
2024-06-05 04:32:58 +00:00
|
|
|
"Server does not support this room version.",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(server) = body.room_id.server_name() {
|
2024-07-16 08:05:25 +00:00
|
|
|
if services
|
2024-06-05 04:32:58 +00:00
|
|
|
.config
|
|
|
|
|
.forbidden_remote_server_names
|
2025-04-06 15:25:11 -04:00
|
|
|
.is_match(server.host())
|
2024-06-05 04:32:58 +00:00
|
|
|
{
|
2024-10-03 10:03:31 +00:00
|
|
|
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
|
2024-06-05 04:32:58 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-16 08:05:25 +00:00
|
|
|
if services
|
2024-06-05 04:32:58 +00:00
|
|
|
.config
|
|
|
|
|
.forbidden_remote_server_names
|
2025-04-06 15:25:11 -04:00
|
|
|
.is_match(body.origin().host())
|
2024-06-05 04:32:58 +00:00
|
|
|
{
|
|
|
|
|
warn!(
|
2024-10-24 12:03:56 +00:00
|
|
|
"Received federated/remote invite from banned server {} for room ID {}. Rejecting.",
|
|
|
|
|
body.origin(),
|
2024-06-05 04:32:58 +00:00
|
|
|
body.room_id
|
|
|
|
|
);
|
2024-10-03 10:03:31 +00:00
|
|
|
|
|
|
|
|
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
|
2024-06-05 04:32:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut signed_event = utils::to_canonical_object(&body.event)
|
2025-04-26 23:50:03 +00:00
|
|
|
.map_err(|_| err!(Request(InvalidParam("Invite event is invalid."))))?;
|
2024-06-05 04:32:58 +00:00
|
|
|
|
2024-10-03 09:57:43 +00:00
|
|
|
let invited_user: OwnedUserId = signed_event
|
|
|
|
|
.get("state_key")
|
|
|
|
|
.try_into()
|
|
|
|
|
.map(UserId::to_owned)
|
|
|
|
|
.map_err(|e| err!(Request(InvalidParam("Invalid state_key property: {e}"))))?;
|
2024-06-05 04:32:58 +00:00
|
|
|
|
2025-04-22 04:42:26 +00:00
|
|
|
if !services
|
|
|
|
|
.globals
|
|
|
|
|
.server_is_ours(invited_user.server_name())
|
|
|
|
|
{
|
2024-10-03 10:03:31 +00:00
|
|
|
return Err!(Request(InvalidParam("User does not belong to this homeserver.")));
|
2024-06-05 04:32:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make sure we're not ACL'ed from their room.
|
2024-07-16 08:05:25 +00:00
|
|
|
services
|
2024-06-05 04:32:58 +00:00
|
|
|
.event_handler
|
2024-08-08 17:18:30 +00:00
|
|
|
.acl_check(invited_user.server_name(), &body.room_id)
|
|
|
|
|
.await?;
|
2024-06-05 04:32:58 +00:00
|
|
|
|
2024-10-11 18:57:59 +00:00
|
|
|
services
|
|
|
|
|
.server_keys
|
|
|
|
|
.hash_and_sign_event(&mut signed_event, &body.room_version)
|
|
|
|
|
.map_err(|e| err!(Request(InvalidParam("Failed to sign event: {e}"))))?;
|
2024-06-05 04:32:58 +00:00
|
|
|
|
|
|
|
|
// Generate event id
|
2025-01-11 18:43:54 -05:00
|
|
|
let event_id = gen_event_id(&signed_event, &body.room_version)?;
|
2024-06-05 04:32:58 +00:00
|
|
|
|
|
|
|
|
// Add event_id back
|
2025-11-02 03:56:39 +00:00
|
|
|
signed_event.insert("event_id".into(), CanonicalJsonValue::String(event_id.to_string()));
|
2024-06-05 04:32:58 +00:00
|
|
|
|
2024-10-03 09:57:43 +00:00
|
|
|
let sender: &UserId = signed_event
|
|
|
|
|
.get("sender")
|
|
|
|
|
.try_into()
|
|
|
|
|
.map_err(|e| err!(Request(InvalidParam("Invalid sender property: {e}"))))?;
|
2024-06-05 04:32:58 +00:00
|
|
|
|
2025-08-22 20:15:54 +05:00
|
|
|
if services.metadata.is_banned(&body.room_id).await
|
|
|
|
|
&& !services.users.is_admin(&invited_user).await
|
2024-12-15 00:05:47 -05:00
|
|
|
{
|
2024-10-03 10:03:31 +00:00
|
|
|
return Err!(Request(Forbidden("This room is banned on this homeserver.")));
|
2024-06-05 04:32:58 +00:00
|
|
|
}
|
|
|
|
|
|
2025-03-31 02:28:01 +00:00
|
|
|
if services.config.block_non_admin_invites && !services.users.is_admin(&invited_user).await {
|
2024-10-03 10:03:31 +00:00
|
|
|
return Err!(Request(Forbidden("This server does not allow room invites.")));
|
2024-06-05 04:32:58 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-29 03:33:29 +00:00
|
|
|
let mut invite_state: Vec<_> = body
|
|
|
|
|
.invite_room_state
|
|
|
|
|
.clone()
|
|
|
|
|
.into_iter()
|
|
|
|
|
.filter_map(|s| extract_variant!(s, RawStrippedState::Stripped))
|
|
|
|
|
.collect();
|
2024-06-05 04:32:58 +00:00
|
|
|
|
|
|
|
|
let mut event: JsonObject = serde_json::from_str(body.event.get())
|
2025-01-11 18:43:54 -05:00
|
|
|
.map_err(|e| err!(Request(BadJson("Invalid invite event PDU: {e}"))))?;
|
2024-06-05 04:32:58 +00:00
|
|
|
|
2025-11-02 03:56:39 +00:00
|
|
|
event.insert("event_id".into(), "$placeholder".into());
|
2024-06-05 04:32:58 +00:00
|
|
|
|
|
|
|
|
let pdu: PduEvent = serde_json::from_value(event.into())
|
2025-01-11 18:43:54 -05:00
|
|
|
.map_err(|e| err!(Request(BadJson("Invalid invite event PDU: {e}"))))?;
|
2024-06-05 04:32:58 +00:00
|
|
|
|
2025-04-26 08:24:47 +00:00
|
|
|
invite_state.push(pdu.to_format());
|
2024-06-05 04:32:58 +00:00
|
|
|
|
2024-11-10 21:20:38 -05:00
|
|
|
// If we are active in the room, the remote server will notify us about the
|
|
|
|
|
// join/invite through /send. If we are not in the room, we need to manually
|
|
|
|
|
// record the invited state for client /sync through update_membership(), and
|
|
|
|
|
// send the invite PDU to the relevant appservices.
|
2024-07-16 08:05:25 +00:00
|
|
|
if !services
|
2024-06-05 04:32:58 +00:00
|
|
|
.state_cache
|
2024-08-08 17:18:30 +00:00
|
|
|
.server_in_room(services.globals.server_name(), &body.room_id)
|
|
|
|
|
.await
|
2024-06-05 04:32:58 +00:00
|
|
|
{
|
2025-10-01 23:01:57 +00:00
|
|
|
let count = services.globals.next_count();
|
2024-08-08 17:18:30 +00:00
|
|
|
services
|
|
|
|
|
.state_cache
|
|
|
|
|
.update_membership(
|
|
|
|
|
&body.room_id,
|
|
|
|
|
&invited_user,
|
|
|
|
|
RoomMemberEventContent::new(MembershipState::Invite),
|
2024-10-03 09:57:43 +00:00
|
|
|
sender,
|
2024-08-08 17:18:30 +00:00
|
|
|
Some(invite_state),
|
|
|
|
|
body.via.clone(),
|
|
|
|
|
true,
|
2025-10-01 23:01:57 +00:00
|
|
|
PduCount::Normal(*count),
|
2024-08-08 17:18:30 +00:00
|
|
|
)
|
|
|
|
|
.await?;
|
2025-10-01 23:01:57 +00:00
|
|
|
drop(count);
|
2024-06-05 04:32:58 +00:00
|
|
|
|
2024-12-07 01:07:01 -05:00
|
|
|
for appservice in services.appservice.read().await.values() {
|
|
|
|
|
if appservice.is_user_match(&invited_user) {
|
|
|
|
|
services
|
|
|
|
|
.sending
|
|
|
|
|
.send_appservice_request(
|
|
|
|
|
appservice.registration.clone(),
|
|
|
|
|
ruma::api::appservice::event::push_events::v1::Request {
|
2025-04-26 08:24:47 +00:00
|
|
|
events: vec![pdu.to_format()],
|
2024-12-07 01:07:01 -05:00
|
|
|
txn_id: general_purpose::URL_SAFE_NO_PAD
|
|
|
|
|
.encode(sha256::hash(pdu.event_id.as_bytes()))
|
|
|
|
|
.into(),
|
|
|
|
|
ephemeral: Vec::new(),
|
|
|
|
|
to_device: Vec::new(),
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
.await?;
|
|
|
|
|
}
|
2024-11-10 21:20:38 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-05 04:32:58 +00:00
|
|
|
Ok(create_invite::v2::Response {
|
2024-07-18 06:37:47 +00:00
|
|
|
event: services
|
2025-08-27 06:22:49 +00:00
|
|
|
.federation
|
|
|
|
|
.format_pdu_into(signed_event, Some(&body.room_version))
|
2024-08-08 17:18:30 +00:00
|
|
|
.await,
|
2024-06-05 04:32:58 +00:00
|
|
|
})
|
|
|
|
|
}
|