Skip to main content

tuwunel_api/client/membership/
invite.rs

1use axum::extract::State;
2use futures::{FutureExt, join};
3use ruma::{
4	api::client::membership::invite_user::{
5		self,
6		v3::{InvitationRecipient, InviteUserId},
7	},
8	events::room::member::MembershipState,
9};
10use tuwunel_core::{Err, Result};
11
12use super::banned_room_check;
13use crate::{ClientIp, Ruma, client::utils::invite_check};
14
15/// # `POST /_matrix/client/r0/rooms/{roomId}/invite`
16///
17/// Tries to send an invite event into the room.
18#[tracing::instrument(skip_all, fields(%client), name = "invite")]
19pub(crate) async fn invite_user_route(
20	State(services): State<crate::State>,
21	ClientIp(client): ClientIp,
22	body: Ruma<invite_user::v3::Request>,
23) -> Result<invite_user::v3::Response> {
24	let sender_user = body.sender_user();
25
26	let room_id = &body.room_id;
27
28	invite_check(&services, sender_user, room_id).await?;
29
30	banned_room_check(&services, sender_user, room_id, None, client).await?;
31
32	let InvitationRecipient::UserId(InviteUserId { user_id, reason }) = &body.recipient else {
33		return Err!(Request(ThreepidDenied("Third party identifiers are not implemented")));
34	};
35
36	// TODO: this should be in the service, but moving it from here would run the
37	// sender's ignore-list check before the banned check, revealing the ignore
38	// state to the sending user if the recipient is banned
39	let member = services
40		.state_accessor
41		.get_member(room_id, user_id);
42
43	let ignored = services
44		.users
45		.user_is_ignored(user_id, sender_user);
46
47	let (member, ignored) = join!(member, ignored);
48
49	if member.is_ok_and(|member| member.membership == MembershipState::Ban) {
50		return Err!(Request(Forbidden("User is banned from this room.")));
51	}
52
53	if ignored {
54		// silently drop the invite to the recipient if they've been ignored by the
55		// sender, pretend it worked
56		return Ok(invite_user::v3::Response {});
57	}
58
59	services
60		.membership
61		.invite(sender_user, user_id, room_id, reason.as_ref(), false)
62		.boxed()
63		.await?;
64
65	Ok(invite_user::v3::Response {})
66}