tuwunel_api/client/account_data/
mod.rs1mod delete_global_account_data;
2mod delete_room_account_data;
3mod get_global_account_data;
4mod get_room_account_data;
5mod set_global_account_data;
6mod set_room_account_data;
7
8use ruma::{
9 RoomId, UserId,
10 events::{GlobalAccountDataEventType, RoomAccountDataEventType},
11 serde::Raw,
12};
13use serde::Deserialize;
14use serde_json::{Value as JsonValue, json, value::RawValue as RawJsonValue};
15use tuwunel_core::{Err, Result, err};
16use tuwunel_service::{Services, appservice::RegistrationInfo};
17
18pub(crate) use self::{
19 delete_global_account_data::delete_global_account_data_route,
20 delete_room_account_data::delete_room_account_data_route,
21 get_global_account_data::get_global_account_data_route,
22 get_room_account_data::get_room_account_data_route,
23 set_global_account_data::set_global_account_data_route,
24 set_room_account_data::set_room_account_data_route,
25};
26
27fn assert_account_data_owner(
28 sender_user: &UserId,
29 user_id: &UserId,
30 appservice_info: Option<&RegistrationInfo>,
31 message: &str,
32) -> Result {
33 if sender_user != user_id && appservice_info.is_none() {
34 return Err!(Request(Forbidden("{message}")));
35 }
36
37 Ok(())
38}
39
40async fn set_account_data(
41 services: &Services,
42 room_id: Option<&RoomId>,
43 sender_user: &UserId,
44 event_type_s: &str,
45 data: &RawJsonValue,
46) -> Result {
47 if event_type_s == RoomAccountDataEventType::FullyRead.to_cow_str() {
48 return Err!(Request(BadJson(
49 "This endpoint cannot be used for marking a room as fully read (setting \
50 m.fully_read)"
51 )));
52 }
53
54 if event_type_s == GlobalAccountDataEventType::PushRules.to_cow_str() {
55 return Err!(Request(BadJson(
56 "This endpoint cannot be used for setting/configuring push rules."
57 )));
58 }
59
60 let data: serde_json::Value = serde_json::from_str(data.get())
61 .map_err(|e| err!(Request(BadJson(warn!("Invalid JSON provided: {e}")))))?;
62
63 services
64 .account_data
65 .update(
66 room_id,
67 sender_user,
68 event_type_s.into(),
69 &json!({
70 "type": event_type_s,
71 "content": data,
72 }),
73 )
74 .await
75}
76
77fn is_empty_content<T>(content: &Raw<T>) -> bool { is_empty_object_json(content.json()) }
81
82pub(crate) fn is_empty_account_data_event<T>(event: &Raw<T>) -> bool {
85 #[derive(Deserialize)]
86 struct ContentOnly<'a> {
87 #[serde(borrow)]
88 content: &'a RawJsonValue,
89 }
90
91 serde_json::from_str::<ContentOnly<'_>>(event.json().get())
92 .is_ok_and(|c| is_empty_object_json(c.content))
93}
94
95fn is_empty_object_json(s: &RawJsonValue) -> bool {
96 serde_json::from_str::<JsonValue>(s.get())
97 .ok()
98 .and_then(|v| v.as_object().map(serde_json::Map::is_empty))
99 .unwrap_or(false)
100}