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 crate::Ruma;
14
15pub(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 services
31 .event_handler
32 .acl_check(origin, room_id)
33 .await?;
34
35 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 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 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}