Skip to main content

tuwunel_api/server/
send_leave.rs

1use axum::extract::State;
2use futures::FutureExt;
3use ruma::{
4	OwnedRoomId, OwnedUserId,
5	api::federation::membership::create_leave_event,
6	events::{
7		StateEventType,
8		room::member::{MembershipState, RoomMemberEventContent},
9	},
10};
11use tuwunel_core::{Err, Result, at, err, matrix::event::gen_event_id_canonical_json};
12
13use super::utils::require_known_room;
14use crate::Ruma;
15
16/// # `PUT /_matrix/federation/v2/send_leave/{roomId}/{eventId}`
17///
18/// Submits a signed leave event.
19pub(crate) async fn create_leave_event_v2_route(
20	State(services): State<crate::State>,
21	body: Ruma<create_leave_event::v2::Request>,
22) -> Result<create_leave_event::v2::Response> {
23	let room_id = &body.room_id;
24	let origin = body.origin();
25
26	services.sending.notify_peer_alive(origin).await;
27
28	require_known_room(&services, room_id, origin).await?;
29
30	// We do not add the event_id field to the pdu here because of signature and
31	// hashes checks
32	let room_version_id = services.state.get_room_version(room_id).await?;
33	let Ok((event_id, value)) = gen_event_id_canonical_json(&body.pdu, &room_version_id) else {
34		// Event could not be converted to canonical json
35		return Err!(Request(BadJson("Could not convert event to canonical json.")));
36	};
37
38	let event_room_id: OwnedRoomId = serde_json::from_value(
39		serde_json::to_value(
40			value
41				.get("room_id")
42				.ok_or_else(|| err!(Request(BadJson("Event missing room_id property."))))?,
43		)
44		.expect("CanonicalJson is valid json value"),
45	)
46	.map_err(|e| err!(Request(BadJson(warn!("room_id field is not a valid room ID: {e}")))))?;
47
48	if event_room_id != *room_id {
49		return Err!(Request(BadJson("Event room_id does not match request path room ID.")));
50	}
51
52	let content: RoomMemberEventContent = serde_json::from_value(
53		value
54			.get("content")
55			.ok_or_else(|| err!(Request(BadJson("Event missing content property."))))?
56			.clone()
57			.into(),
58	)
59	.map_err(|e| err!(Request(BadJson(warn!("Event content is empty or invalid: {e}")))))?;
60
61	if content.membership != MembershipState::Leave {
62		return Err!(Request(BadJson(
63			"Not allowed to send a non-leave membership event to leave endpoint."
64		)));
65	}
66
67	let event_type: StateEventType = serde_json::from_value(
68		value
69			.get("type")
70			.ok_or_else(|| err!(Request(BadJson("Event missing type property."))))?
71			.clone()
72			.into(),
73	)
74	.map_err(|e| err!(Request(BadJson(warn!("Event has invalid state event type: {e}")))))?;
75
76	if event_type != StateEventType::RoomMember {
77		return Err!(Request(BadJson(
78			"Not allowed to send non-membership state event to leave endpoint."
79		)));
80	}
81
82	// ACL check sender server name
83	let sender: OwnedUserId = serde_json::from_value(
84		value
85			.get("sender")
86			.ok_or_else(|| err!(Request(BadJson("Event missing sender property."))))?
87			.clone()
88			.into(),
89	)
90	.map_err(|e| err!(Request(BadJson(warn!("sender property is not a valid user ID: {e}")))))?;
91
92	services
93		.event_handler
94		.acl_check(sender.server_name(), room_id)
95		.await?;
96
97	if sender.server_name() != origin {
98		return Err!(Request(BadJson("Not allowed to leave on behalf of another server/user.")));
99	}
100
101	let state_key: OwnedUserId = serde_json::from_value(
102		value
103			.get("state_key")
104			.ok_or_else(|| err!(Request(BadJson("Event missing state_key property."))))?
105			.clone()
106			.into(),
107	)
108	.map_err(|e| err!(Request(BadJson(warn!("State key is not a valid user ID: {e}")))))?;
109
110	if state_key != sender {
111		return Err!(Request(BadJson("State key does not match sender user.")));
112	}
113
114	let mutex_lock = services
115		.event_handler
116		.mutex_federation
117		.lock(room_id)
118		.await;
119
120	let pdu_id = services
121		.event_handler
122		.handle_incoming_pdu(origin, room_id, &event_id, value, true)
123		.await?
124		.map(at!(0))
125		.ok_or_else(|| err!(Request(InvalidParam("Could not accept as timeline event."))))?;
126
127	drop(mutex_lock);
128
129	services
130		.sending
131		.send_pdu_room(room_id, &pdu_id)
132		.boxed()
133		.await?;
134
135	Ok(create_leave_event::v2::Response::new())
136}