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		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	/// Get the power level required to send an event of the given type.
161	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	/// Get a map of all the fields with an integer value in the `content` of an
186	/// `m.room.power_levels` event.
187	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
209/// Helper trait for `Option<RoomPowerLevelsEvent<E>>`.
210pub(crate) trait RoomPowerLevelsEventOptionExt {
211	/// Get the power level of the user with the given ID.
212	fn user_power_level(
213		&self,
214		user_id: &UserId,
215		creators: impl Iterator<Item = OwnedUserId>,
216		rules: &AuthorizationRules,
217	) -> Result<UserPowerLevel>;
218
219	/// Get the value of a field that should contain an integer, or its default
220	/// value if it is absent.
221	fn get_as_int_or_default(
222		&self,
223		field: RoomPowerLevelsIntField,
224		rules: &AuthorizationRules,
225	) -> Result<Int>;
226
227	/// Get the power level required to send an event of the given type.
228	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/// Fields in the `content` of an `m.room.power_levels` event with an integer
324/// value.
325#[derive(DebugAsRefStr, Clone, Copy, DisplayAsRefStr, PartialEq, Eq, OrdAsRefStr)]
326#[non_exhaustive]
327pub enum RoomPowerLevelsIntField {
328	/// `users_default`
329	UsersDefault,
330
331	/// `events_default`
332	EventsDefault,
333
334	/// `state_default`
335	StateDefault,
336
337	/// `ban`
338	Ban,
339
340	/// `redact`
341	Redact,
342
343	/// `kick`
344	Kick,
345
346	/// `invite`
347	Invite,
348}
349
350impl RoomPowerLevelsIntField {
351	/// A slice containing all the variants.
352	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	/// The string representation of this field.
363	#[inline]
364	#[must_use]
365	pub fn as_str(&self) -> &str { self.as_ref() }
366
367	/// The default value for this field if it is absent.
368	#[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}