tuwunel_api/server/
send_leave.rs1use 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
16pub(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 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 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 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}