tuwunel_service/rooms/state_res/events/
power_levels.rs1use std::ops::Deref;
4
5use ruma::{
6 Int, OwnedUserId, UserId,
7 events::{TimelineEventType, room::power_levels::UserPowerLevel},
8 int,
9 room_version_rules::AuthorizationRules,
10 serde::{
11 DebugAsRefStr, DisplayAsRefStr, JsonObject, OrdAsRefStr, deserialize_v1_powerlevel,
12 from_raw_json_value, vec_deserialize_int_powerlevel_values,
13 vec_deserialize_v1_powerlevel_values,
14 },
15};
16use serde::de::DeserializeOwned;
17use serde_json::{Error, from_value as from_json_value};
18use tuwunel_core::{Result, err, is_equal_to, matrix::Event, ref_at};
19
20const DEFAULT_CREATOR_POWER_LEVEL: i32 = 100;
22
23#[derive(Clone, Debug)]
25pub struct RoomPowerLevelsEvent<E: Event>(E);
26
27impl<E: Event> RoomPowerLevelsEvent<E> {
28 #[inline]
30 pub fn new(event: E) -> Self { Self(event) }
31
32 fn deserialized_content(&self) -> Result<JsonObject> {
34 from_raw_json_value(self.content()).map_err(|error: Error| {
35 err!(Request(InvalidParam("malformed `m.room.power_levels` content: {error}")))
36 })
37 }
38
39 pub(crate) fn get_as_int(
43 &self,
44 field: RoomPowerLevelsIntField,
45 rules: &AuthorizationRules,
46 ) -> Result<Option<Int>> {
47 let content = self.deserialized_content()?;
48
49 let Some(value) = content.get(field.as_str()) else {
50 return Ok(None);
51 };
52
53 let res = if rules.integer_power_levels {
54 from_json_value(value.clone())
55 } else {
56 deserialize_v1_powerlevel(value)
57 };
58
59 let power_level = res.map(Some).map_err(|error| {
60 err!(Request(InvalidParam(
61 "unexpected format of `{field}` field in `content` of `m.room.power_levels` \
62 event: {error}"
63 )))
64 })?;
65
66 Ok(power_level)
67 }
68
69 #[inline]
72 pub(crate) fn get_as_int_or_default(
73 &self,
74 field: RoomPowerLevelsIntField,
75 rules: &AuthorizationRules,
76 ) -> Result<Int> {
77 Ok(self
78 .get_as_int(field, rules)?
79 .unwrap_or_else(|| field.default_value()))
80 }
81
82 fn get_as_int_map<T: Ord + DeserializeOwned>(
85 &self,
86 field: &str,
87 rules: &AuthorizationRules,
88 ) -> Result<Option<Vec<(T, Int)>>> {
89 let content = self.deserialized_content()?;
90
91 let Some(value) = content.get(field) else {
92 return Ok(None);
93 };
94
95 let res = if rules.integer_power_levels {
96 vec_deserialize_int_powerlevel_values(value)
97 } else {
98 vec_deserialize_v1_powerlevel_values(value)
99 };
100
101 res.map(Some).map_err(|error| {
102 err!(Request(InvalidParam(
103 "unexpected format of `{field}` field in `content` of `m.room.power_levels` \
104 event: {error}"
105 )))
106 })
107 }
108
109 #[inline]
111 pub(crate) fn events(
112 &self,
113 rules: &AuthorizationRules,
114 ) -> Result<Option<Vec<(TimelineEventType, Int)>>> {
115 self.get_as_int_map("events", rules)
116 }
117
118 #[inline]
120 pub(crate) fn notifications(
121 &self,
122 rules: &AuthorizationRules,
123 ) -> Result<Option<Vec<(String, Int)>>> {
124 self.get_as_int_map("notifications", rules)
125 }
126
127 #[inline]
131 pub(crate) fn users(
132 &self,
133 rules: &AuthorizationRules,
134 ) -> Result<Option<Vec<(OwnedUserId, Int)>>> {
135 self.get_as_int_map("users", rules)
136 }
137
138 pub(crate) fn user_power_level(
143 &self,
144 user_id: &UserId,
145 rules: &AuthorizationRules,
146 ) -> Result<UserPowerLevel> {
147 let power_level = if let Some(power_level) = self
148 .users(rules)?
149 .as_ref()
150 .and_then(|users| get_value(users, user_id))
151 {
152 Ok(*power_level)
153 } else {
154 self.get_as_int_or_default(RoomPowerLevelsIntField::UsersDefault, rules)
155 };
156
157 power_level.map(Into::into)
158 }
159
160 pub(crate) fn event_power_level(
162 &self,
163 event_type: &TimelineEventType,
164 state_key: Option<&str>,
165 rules: &AuthorizationRules,
166 ) -> Result<Int> {
167 let events = self.events(rules)?;
168
169 if let Some(power_level) = events
170 .as_ref()
171 .and_then(|events| get_value(events, event_type))
172 {
173 return Ok(*power_level);
174 }
175
176 let default_field = if state_key.is_some() {
177 RoomPowerLevelsIntField::StateDefault
178 } else {
179 RoomPowerLevelsIntField::EventsDefault
180 };
181
182 self.get_as_int_or_default(default_field, rules)
183 }
184
185 pub(crate) fn int_fields_map(
188 &self,
189 rules: &AuthorizationRules,
190 ) -> Result<Vec<(RoomPowerLevelsIntField, Int)>> {
191 RoomPowerLevelsIntField::ALL
192 .iter()
193 .copied()
194 .filter_map(|field| match self.get_as_int(field, rules) {
195 | Ok(value) => value.map(|value| Ok((field, value))),
196 | Err(error) => Some(Err(error)),
197 })
198 .collect()
199 }
200}
201
202impl<E: Event> Deref for RoomPowerLevelsEvent<E> {
203 type Target = E;
204
205 #[inline]
206 fn deref(&self) -> &Self::Target { &self.0 }
207}
208
209pub(crate) trait RoomPowerLevelsEventOptionExt {
211 fn user_power_level(
213 &self,
214 user_id: &UserId,
215 creators: impl Iterator<Item = OwnedUserId>,
216 rules: &AuthorizationRules,
217 ) -> Result<UserPowerLevel>;
218
219 fn get_as_int_or_default(
222 &self,
223 field: RoomPowerLevelsIntField,
224 rules: &AuthorizationRules,
225 ) -> Result<Int>;
226
227 fn event_power_level(
229 &self,
230 event_type: &TimelineEventType,
231 state_key: Option<&str>,
232 rules: &AuthorizationRules,
233 ) -> Result<Int>;
234}
235
236impl<E> RoomPowerLevelsEventOptionExt for Option<RoomPowerLevelsEvent<E>>
237where
238 E: Event,
239{
240 fn user_power_level(
241 &self,
242 user_id: &UserId,
243 mut creators: impl Iterator<Item = OwnedUserId>,
244 rules: &AuthorizationRules,
245 ) -> Result<UserPowerLevel> {
246 if rules.explicitly_privilege_room_creators && creators.any(is_equal_to!(user_id)) {
247 Ok(UserPowerLevel::Infinite)
248 } else if let Some(room_power_levels_event) = self {
249 room_power_levels_event.user_power_level(user_id, rules)
250 } else {
251 let power_level = if creators.any(is_equal_to!(user_id)) {
252 DEFAULT_CREATOR_POWER_LEVEL.into()
253 } else {
254 RoomPowerLevelsIntField::UsersDefault.default_value()
255 };
256
257 Ok(power_level.into())
258 }
259 }
260
261 fn get_as_int_or_default(
262 &self,
263 field: RoomPowerLevelsIntField,
264 rules: &AuthorizationRules,
265 ) -> Result<Int> {
266 if let Some(room_power_levels_event) = self {
267 room_power_levels_event.get_as_int_or_default(field, rules)
268 } else {
269 Ok(field.default_value())
270 }
271 }
272
273 fn event_power_level(
274 &self,
275 event_type: &TimelineEventType,
276 state_key: Option<&str>,
277 rules: &AuthorizationRules,
278 ) -> Result<Int> {
279 if let Some(room_power_levels_event) = self {
280 room_power_levels_event.event_power_level(event_type, state_key, rules)
281 } else {
282 let default_field = if state_key.is_some() {
283 RoomPowerLevelsIntField::StateDefault
284 } else {
285 RoomPowerLevelsIntField::EventsDefault
286 };
287
288 Ok(default_field.default_value())
289 }
290 }
291}
292
293#[inline]
294pub(crate) fn get_value<'a, K, V, B>(vec: &'a [(K, V)], key: &'a B) -> Option<&'a V>
295where
296 &'a K: PartialEq<&'a B>,
297 B: ?Sized,
298{
299 position(vec, key)
300 .and_then(|i| vec.get(i))
301 .map(ref_at!(1))
302}
303
304#[inline]
305pub(crate) fn contains_key<'a, K, V, B>(vec: &'a [(K, V)], key: &'a B) -> bool
306where
307 &'a K: PartialEq<&'a B>,
308 B: ?Sized,
309{
310 position(vec, key).is_some()
311}
312
313fn position<'a, K, V, B>(vec: &'a [(K, V)], key: &'a B) -> Option<usize>
314where
315 &'a K: PartialEq<&'a B>,
316 B: ?Sized,
317{
318 vec.iter()
319 .map(ref_at!(0))
320 .position(is_equal_to!(key))
321}
322
323#[derive(DebugAsRefStr, Clone, Copy, DisplayAsRefStr, PartialEq, Eq, OrdAsRefStr)]
326#[non_exhaustive]
327pub enum RoomPowerLevelsIntField {
328 UsersDefault,
330
331 EventsDefault,
333
334 StateDefault,
336
337 Ban,
339
340 Redact,
342
343 Kick,
345
346 Invite,
348}
349
350impl RoomPowerLevelsIntField {
351 pub const ALL: &[Self] = &[
353 Self::UsersDefault,
354 Self::EventsDefault,
355 Self::StateDefault,
356 Self::Ban,
357 Self::Redact,
358 Self::Kick,
359 Self::Invite,
360 ];
361
362 #[inline]
364 #[must_use]
365 pub fn as_str(&self) -> &str { self.as_ref() }
366
367 #[inline]
369 #[must_use]
370 pub fn default_value(self) -> Int {
371 match self {
372 | Self::UsersDefault | Self::EventsDefault | Self::Invite => int!(0),
373 | Self::StateDefault | Self::Kick | Self::Ban | Self::Redact => int!(50),
374 }
375 }
376}
377
378impl AsRef<str> for RoomPowerLevelsIntField {
379 #[inline]
380 fn as_ref(&self) -> &'static str {
381 match self {
382 | Self::UsersDefault => "users_default",
383 | Self::EventsDefault => "events_default",
384 | Self::StateDefault => "state_default",
385 | Self::Ban => "ban",
386 | Self::Redact => "redact",
387 | Self::Kick => "kick",
388 | Self::Invite => "invite",
389 }
390 }
391}