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