diff --git a/src/api/server/invite.rs b/src/api/server/invite.rs index 94bc018f..d1a28040 100644 --- a/src/api/server/invite.rs +++ b/src/api/server/invite.rs @@ -3,13 +3,13 @@ use axum_client_ip::InsecureClientIp; use base64::{Engine as _, engine::general_purpose}; use futures::StreamExt; use ruma::{ - CanonicalJsonValue, OwnedUserId, UserId, + CanonicalJsonValue, OwnedRoomId, OwnedUserId, RoomId, UserId, api::{ client::error::ErrorKind, federation::membership::{RawStrippedState, create_invite}, }, events::{ - GlobalAccountDataEventType, + GlobalAccountDataEventType, StateEventType, push_rules::PushRulesEvent, room::member::{MembershipState, RoomMemberEventContent}, }, @@ -78,6 +78,26 @@ pub(crate) async fn create_invite_route( let mut signed_event = utils::to_canonical_object(&body.event) .map_err(|_| err!(Request(InvalidParam("Invite event is invalid."))))?; + let room_id: OwnedRoomId = signed_event + .get("room_id") + .try_into() + .map(RoomId::to_owned) + .map_err(|e| err!(Request(InvalidParam("Invalid room_id property: {e}"))))?; + + if body.room_id != room_id { + return Err!(Request(InvalidParam("Event room_id does not match the request path."))); + } + + let kind: StateEventType = signed_event + .get("type") + .and_then(CanonicalJsonValue::as_str) + .ok_or_else(|| err!(Request(BadJson("Missing type in event."))))? + .into(); + + if kind != StateEventType::RoomMember { + return Err!(Request(InvalidParam("Event must be m.room.member type."))); + } + let invited_user: OwnedUserId = signed_event .get("state_key") .try_into() @@ -91,6 +111,19 @@ pub(crate) async fn create_invite_route( return Err!(Request(InvalidParam("User does not belong to this homeserver."))); } + let content: RoomMemberEventContent = signed_event + .get("content") + .cloned() + .map(Into::into) + .map(serde_json::from_value) + .transpose() + .map_err(|e| err!(Request(InvalidParam("Invalid content object in event: {e}"))))? + .ok_or_else(|| err!(Request(BadJson("Missing content in event."))))?; + + if content.membership != MembershipState::Invite { + return Err!(Request(InvalidParam("Event membership must be invite."))); + } + // Make sure we're not ACL'ed from their room. services .event_handler @@ -108,11 +141,27 @@ pub(crate) async fn create_invite_route( // Add event_id back signed_event.insert("event_id".into(), CanonicalJsonValue::String(event_id.to_string())); + let origin: Option<&str> = signed_event + .get("origin") + .and_then(CanonicalJsonValue::as_str); + let sender: &UserId = signed_event .get("sender") .try_into() .map_err(|e| err!(Request(InvalidParam("Invalid sender property: {e}"))))?; + if sender.server_name() != body.origin() { + return Err!(Request(Forbidden("Can only send invites on behalf of your users."))); + } + + if origin.is_some_and(|origin| origin != sender.server_name()) { + return Err!(Request(Forbidden("Your users can only be from your origin."))); + } + + if origin.is_some_and(|origin| origin != body.origin()) { + return Err!(Request(Forbidden("Can only send events from your origin."))); + } + if services.metadata.is_banned(&body.room_id).await && !services.users.is_admin(&invited_user).await {