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 self.users(rules)?
148 .as_ref()
149 .and_then(|users| get_value(users, user_id))
150 .copied()
151 .map_or_else(
152 || self.get_as_int_or_default(RoomPowerLevelsIntField::UsersDefault, rules),
153 Ok,
154 )
155 .map(Into::into)
156 }
157
158 pub(crate) fn event_power_level(
160 &self,
161 event_type: &TimelineEventType,
162 state_key: Option<&str>,
163 rules: &AuthorizationRules,
164 ) -> Result<Int> {
165 let events = self.events(rules)?;
166
167 if let Some(power_level) = events
168 .as_ref()
169 .and_then(|events| get_value(events, event_type))
170 {
171 return Ok(*power_level);
172 }
173
174 let default_field = if state_key.is_some() {
175 RoomPowerLevelsIntField::StateDefault
176 } else {
177 RoomPowerLevelsIntField::EventsDefault
178 };
179
180 self.get_as_int_or_default(default_field, rules)
181 }
182
183 pub(crate) fn int_fields_map(
186 &self,
187 rules: &AuthorizationRules,
188 ) -> Result<Vec<(RoomPowerLevelsIntField, Int)>> {
189 RoomPowerLevelsIntField::ALL
190 .iter()
191 .copied()
192 .filter_map(|field| match self.get_as_int(field, rules) {
193 | Ok(value) => value.map(|value| Ok((field, value))),
194 | Err(error) => Some(Err(error)),
195 })
196 .collect()
197 }
198}
199
200impl<E: Event> Deref for RoomPowerLevelsEvent<E> {
201 type Target = E;
202
203 #[inline]
204 fn deref(&self) -> &Self::Target { &self.0 }
205}
206
207pub(crate) trait RoomPowerLevelsEventOptionExt {
209 fn user_power_level(
211 &self,
212 user_id: &UserId,
213 creators: impl Iterator<Item = OwnedUserId>,
214 rules: &AuthorizationRules,
215 ) -> Result<UserPowerLevel>;
216
217 fn get_as_int_or_default(
220 &self,
221 field: RoomPowerLevelsIntField,
222 rules: &AuthorizationRules,
223 ) -> Result<Int>;
224
225 fn event_power_level(
227 &self,
228 event_type: &TimelineEventType,
229 state_key: Option<&str>,
230 rules: &AuthorizationRules,
231 ) -> Result<Int>;
232}
233
234impl<E> RoomPowerLevelsEventOptionExt for Option<RoomPowerLevelsEvent<E>>
235where
236 E: Event,
237{
238 fn user_power_level(
239 &self,
240 user_id: &UserId,
241 mut creators: impl Iterator<Item = OwnedUserId>,
242 rules: &AuthorizationRules,
243 ) -> Result<UserPowerLevel> {
244 if rules.explicitly_privilege_room_creators && creators.any(is_equal_to!(user_id)) {
245 Ok(UserPowerLevel::Infinite)
246 } else if let Some(room_power_levels_event) = self {
247 room_power_levels_event.user_power_level(user_id, rules)
248 } else {
249 let power_level = if creators.any(is_equal_to!(user_id)) {
250 DEFAULT_CREATOR_POWER_LEVEL.into()
251 } else {
252 RoomPowerLevelsIntField::UsersDefault.default_value()
253 };
254
255 Ok(power_level.into())
256 }
257 }
258
259 fn get_as_int_or_default(
260 &self,
261 field: RoomPowerLevelsIntField,
262 rules: &AuthorizationRules,
263 ) -> Result<Int> {
264 self.as_ref().map_or_else(
265 || Ok(field.default_value()),
266 |room_power_levels_event| room_power_levels_event.get_as_int_or_default(field, rules),
267 )
268 }
269
270 fn event_power_level(
271 &self,
272 event_type: &TimelineEventType,
273 state_key: Option<&str>,
274 rules: &AuthorizationRules,
275 ) -> Result<Int> {
276 self.as_ref().map_or_else(
277 || {
278 let default_field = state_key
279 .is_some()
280 .then_some(RoomPowerLevelsIntField::StateDefault)
281 .unwrap_or(RoomPowerLevelsIntField::EventsDefault);
282
283 Ok(default_field.default_value())
284 },
285 |room_power_levels_event| {
286 room_power_levels_event.event_power_level(event_type, state_key, rules)
287 },
288 )
289 }
290}
291
292#[inline]
293pub(crate) fn get_value<'a, K, V, B>(vec: &'a [(K, V)], key: &'a B) -> Option<&'a V>
294where
295 &'a K: PartialEq<&'a B>,
296 B: ?Sized,
297{
298 position(vec, key)
299 .and_then(|i| vec.get(i))
300 .map(ref_at!(1))
301}
302
303#[inline]
304pub(crate) fn contains_key<'a, K, V, B>(vec: &'a [(K, V)], key: &'a B) -> bool
305where
306 &'a K: PartialEq<&'a B>,
307 B: ?Sized,
308{
309 position(vec, key).is_some()
310}
311
312fn position<'a, K, V, B>(vec: &'a [(K, V)], key: &'a B) -> Option<usize>
313where
314 &'a K: PartialEq<&'a B>,
315 B: ?Sized,
316{
317 vec.iter()
318 .map(ref_at!(0))
319 .position(is_equal_to!(key))
320}
321
322#[derive(DebugAsRefStr, Clone, Copy, DisplayAsRefStr, PartialEq, Eq, OrdAsRefStr)]
325#[non_exhaustive]
326pub enum RoomPowerLevelsIntField {
327 UsersDefault,
329
330 EventsDefault,
332
333 StateDefault,
335
336 Ban,
338
339 Redact,
341
342 Kick,
344
345 Invite,
347}
348
349impl RoomPowerLevelsIntField {
350 pub const ALL: &[Self] = &[
352 Self::UsersDefault,
353 Self::EventsDefault,
354 Self::StateDefault,
355 Self::Ban,
356 Self::Redact,
357 Self::Kick,
358 Self::Invite,
359 ];
360
361 #[inline]
363 #[must_use]
364 pub fn as_str(&self) -> &str { self.as_ref() }
365
366 #[inline]
368 #[must_use]
369 pub fn default_value(self) -> Int {
370 match self {
371 | Self::UsersDefault | Self::EventsDefault | Self::Invite => int!(0),
372 | Self::StateDefault | Self::Kick | Self::Ban | Self::Redact => int!(50),
373 }
374 }
375}
376
377impl AsRef<str> for RoomPowerLevelsIntField {
378 #[inline]
379 fn as_ref(&self) -> &'static str {
380 match self {
381 | Self::UsersDefault => "users_default",
382 | Self::EventsDefault => "events_default",
383 | Self::StateDefault => "state_default",
384 | Self::Ban => "ban",
385 | Self::Redact => "redact",
386 | Self::Kick => "kick",
387 | Self::Invite => "invite",
388 }
389 }
390}