Skip to main content

tuwunel_service/rooms/state_res/events/
power_levels.rs

1//! Types to deserialize `m.room.power_levels` events.
2
3use 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
20/// The default value of the creator's power level.
21const DEFAULT_CREATOR_POWER_LEVEL: i32 = 100;
22
23/// A helper type for an [`Event`] of type `m.room.power_levels`.
24#[derive(Clone, Debug)]
25pub struct RoomPowerLevelsEvent<E: Event>(E);
26
27impl<E: Event> RoomPowerLevelsEvent<E> {
28	/// Construct a new `RoomPowerLevelsEvent` around the given event.
29	#[inline]
30	pub fn new(event: E) -> Self { Self(event) }
31
32	/// The deserialized content of the event.
33	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	/// Get the value of a field that should contain an integer, if any.
40	///
41	/// The deserialization of this field is cached in memory.
42	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	/// Get the value of a field that should contain an integer, or its default
70	/// value if it is absent.
71	#[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	/// Get the value of a field that should contain a map of any value to
83	/// integer, if any.
84	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	/// Get the power levels required to send events, if any.
110	#[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	/// Get the power levels required to trigger notifications, if any.
119	#[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	/// Get the power levels of the users, if any.
128	///
129	/// The deserialization of this field is cached in memory.
130	#[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	/// Get the power level of the user with the given ID.
139	///
140	/// Calling this method several times should be cheap because the necessary
141	/// deserialization results are cached.
142	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	/// Get the power level required to send an event of the given type.
159	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	/// Get a map of all the fields with an integer value in the `content` of an
184	/// `m.room.power_levels` event.
185	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
207/// Helper trait for `Option<RoomPowerLevelsEvent<E>>`.
208pub(crate) trait RoomPowerLevelsEventOptionExt {
209	/// Get the power level of the user with the given ID.
210	fn user_power_level(
211		&self,
212		user_id: &UserId,
213		creators: impl Iterator<Item = OwnedUserId>,
214		rules: &AuthorizationRules,
215	) -> Result<UserPowerLevel>;
216
217	/// Get the value of a field that should contain an integer, or its default
218	/// value if it is absent.
219	fn get_as_int_or_default(
220		&self,
221		field: RoomPowerLevelsIntField,
222		rules: &AuthorizationRules,
223	) -> Result<Int>;
224
225	/// Get the power level required to send an event of the given type.
226	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/// Fields in the `content` of an `m.room.power_levels` event with an integer
323/// value.
324#[derive(DebugAsRefStr, Clone, Copy, DisplayAsRefStr, PartialEq, Eq, OrdAsRefStr)]
325#[non_exhaustive]
326pub enum RoomPowerLevelsIntField {
327	/// `users_default`
328	UsersDefault,
329
330	/// `events_default`
331	EventsDefault,
332
333	/// `state_default`
334	StateDefault,
335
336	/// `ban`
337	Ban,
338
339	/// `redact`
340	Redact,
341
342	/// `kick`
343	Kick,
344
345	/// `invite`
346	Invite,
347}
348
349impl RoomPowerLevelsIntField {
350	/// A slice containing all the variants.
351	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	/// The string representation of this field.
362	#[inline]
363	#[must_use]
364	pub fn as_str(&self) -> &str { self.as_ref() }
365
366	/// The default value for this field if it is absent.
367	#[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}