ruma_events/room/
power_levels.rs

1//! Types for the [`m.room.power_levels`] event.
2//!
3//! [`m.room.power_levels`]: https://spec.matrix.org/latest/client-server-api/#mroompower_levels
4
5use std::{
6    cmp::{max, Ordering},
7    collections::BTreeMap,
8};
9
10use js_int::{int, Int};
11use ruma_common::{
12    power_levels::{default_power_level, NotificationPowerLevels},
13    push::PushConditionPowerLevelsCtx,
14    room_version_rules::{AuthorizationRules, RedactionRules, RoomPowerLevelsRules},
15    OwnedUserId, UserId,
16};
17use ruma_macros::EventContent;
18use serde::{Deserialize, Serialize};
19
20use crate::{
21    EmptyStateKey, MessageLikeEventType, RedactContent, RedactedStateEventContent, StateEventType,
22    StaticEventContent, TimelineEventType,
23};
24
25/// The content of an `m.room.power_levels` event.
26///
27/// Defines the power levels (privileges) of users in the room.
28#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
29#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
30#[ruma_event(type = "m.room.power_levels", kind = State, state_key_type = EmptyStateKey, custom_redacted)]
31pub struct RoomPowerLevelsEventContent {
32    /// The level required to ban a user.
33    #[serde(
34        default = "default_power_level",
35        skip_serializing_if = "is_default_power_level",
36        deserialize_with = "ruma_common::serde::deserialize_v1_powerlevel"
37    )]
38    pub ban: Int,
39
40    /// The level required to send specific event types.
41    ///
42    /// This is a mapping from event type to power level required.
43    #[serde(
44        default,
45        skip_serializing_if = "BTreeMap::is_empty",
46        deserialize_with = "ruma_common::serde::btreemap_deserialize_v1_powerlevel_values"
47    )]
48    pub events: BTreeMap<TimelineEventType, Int>,
49
50    /// The default level required to send message events.
51    #[serde(
52        default,
53        skip_serializing_if = "ruma_common::serde::is_default",
54        deserialize_with = "ruma_common::serde::deserialize_v1_powerlevel"
55    )]
56    pub events_default: Int,
57
58    /// The level required to invite a user.
59    #[serde(
60        default,
61        skip_serializing_if = "ruma_common::serde::is_default",
62        deserialize_with = "ruma_common::serde::deserialize_v1_powerlevel"
63    )]
64    pub invite: Int,
65
66    /// The level required to kick a user.
67    #[serde(
68        default = "default_power_level",
69        skip_serializing_if = "is_default_power_level",
70        deserialize_with = "ruma_common::serde::deserialize_v1_powerlevel"
71    )]
72    pub kick: Int,
73
74    /// The level required to redact an event.
75    #[serde(
76        default = "default_power_level",
77        skip_serializing_if = "is_default_power_level",
78        deserialize_with = "ruma_common::serde::deserialize_v1_powerlevel"
79    )]
80    pub redact: Int,
81
82    /// The default level required to send state events.
83    #[serde(
84        default = "default_power_level",
85        skip_serializing_if = "is_default_power_level",
86        deserialize_with = "ruma_common::serde::deserialize_v1_powerlevel"
87    )]
88    pub state_default: Int,
89
90    /// The power levels for specific users.
91    ///
92    /// This is a mapping from `user_id` to power level for that user.
93    #[serde(
94        default,
95        skip_serializing_if = "BTreeMap::is_empty",
96        deserialize_with = "ruma_common::serde::btreemap_deserialize_v1_powerlevel_values"
97    )]
98    pub users: BTreeMap<OwnedUserId, Int>,
99
100    /// The default power level for every user in the room.
101    #[serde(
102        default,
103        skip_serializing_if = "ruma_common::serde::is_default",
104        deserialize_with = "ruma_common::serde::deserialize_v1_powerlevel"
105    )]
106    pub users_default: Int,
107
108    /// The power level requirements for specific notification types.
109    ///
110    /// This is a mapping from `key` to power level for that notifications key.
111    #[serde(default, skip_serializing_if = "NotificationPowerLevels::is_default")]
112    pub notifications: NotificationPowerLevels,
113}
114
115impl RoomPowerLevelsEventContent {
116    /// Creates a new `RoomPowerLevelsEventContent` with all-default values for the given
117    /// authorization rules.
118    pub fn new(rules: &AuthorizationRules) -> Self {
119        // events_default, users_default and invite having a default of 0 while the others have a
120        // default of 50 is not an oversight, these defaults are from the Matrix specification.
121        let mut pl = Self {
122            ban: default_power_level(),
123            events: BTreeMap::new(),
124            events_default: int!(0),
125            invite: int!(0),
126            kick: default_power_level(),
127            redact: default_power_level(),
128            state_default: default_power_level(),
129            users: BTreeMap::new(),
130            users_default: int!(0),
131            notifications: NotificationPowerLevels::default(),
132        };
133
134        if rules.explicitly_privilege_room_creators {
135            // Since v12, the default power level to send m.room.tombstone events is increased to
136            // PL150.
137            pl.events.insert(TimelineEventType::RoomTombstone, int!(150));
138        }
139
140        pl
141    }
142}
143
144impl RedactContent for RoomPowerLevelsEventContent {
145    type Redacted = RedactedRoomPowerLevelsEventContent;
146
147    fn redact(self, rules: &RedactionRules) -> Self::Redacted {
148        let Self {
149            ban,
150            events,
151            events_default,
152            invite,
153            kick,
154            redact,
155            state_default,
156            users,
157            users_default,
158            ..
159        } = self;
160
161        let invite = if rules.keep_room_power_levels_invite { invite } else { int!(0) };
162
163        RedactedRoomPowerLevelsEventContent {
164            ban,
165            events,
166            events_default,
167            invite,
168            kick,
169            redact,
170            state_default,
171            users,
172            users_default,
173        }
174    }
175}
176
177/// Used with `#[serde(skip_serializing_if)]` to omit default power levels.
178#[allow(clippy::trivially_copy_pass_by_ref)]
179fn is_default_power_level(l: &Int) -> bool {
180    *l == int!(50)
181}
182
183impl RoomPowerLevelsEvent {
184    /// Obtain the effective power levels, regardless of whether this event is redacted.
185    pub fn power_levels(
186        &self,
187        rules: &AuthorizationRules,
188        creators: Vec<OwnedUserId>,
189    ) -> RoomPowerLevels {
190        match self {
191            Self::Original(ev) => RoomPowerLevels::new(ev.content.clone().into(), rules, creators),
192            Self::Redacted(ev) => RoomPowerLevels::new(ev.content.clone().into(), rules, creators),
193        }
194    }
195}
196
197impl SyncRoomPowerLevelsEvent {
198    /// Obtain the effective power levels, regardless of whether this event is redacted.
199    pub fn power_levels(
200        &self,
201        rules: &AuthorizationRules,
202        creators: Vec<OwnedUserId>,
203    ) -> RoomPowerLevels {
204        match self {
205            Self::Original(ev) => RoomPowerLevels::new(ev.content.clone().into(), rules, creators),
206            Self::Redacted(ev) => RoomPowerLevels::new(ev.content.clone().into(), rules, creators),
207        }
208    }
209}
210
211impl StrippedRoomPowerLevelsEvent {
212    /// Obtain the effective power levels from this event.
213    pub fn power_levels(
214        &self,
215        rules: &AuthorizationRules,
216        creators: Vec<OwnedUserId>,
217    ) -> RoomPowerLevels {
218        RoomPowerLevels::new(self.content.clone().into(), rules, creators)
219    }
220}
221
222/// Redacted form of [`RoomPowerLevelsEventContent`].
223#[derive(Clone, Debug, Deserialize, Serialize)]
224#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
225pub struct RedactedRoomPowerLevelsEventContent {
226    /// The level required to ban a user.
227    #[serde(
228        default = "default_power_level",
229        skip_serializing_if = "is_default_power_level",
230        deserialize_with = "ruma_common::serde::deserialize_v1_powerlevel"
231    )]
232    pub ban: Int,
233
234    /// The level required to send specific event types.
235    ///
236    /// This is a mapping from event type to power level required.
237    #[serde(
238        default,
239        skip_serializing_if = "BTreeMap::is_empty",
240        deserialize_with = "ruma_common::serde::btreemap_deserialize_v1_powerlevel_values"
241    )]
242    pub events: BTreeMap<TimelineEventType, Int>,
243
244    /// The default level required to send message events.
245    #[serde(
246        default,
247        skip_serializing_if = "ruma_common::serde::is_default",
248        deserialize_with = "ruma_common::serde::deserialize_v1_powerlevel"
249    )]
250    pub events_default: Int,
251
252    /// The level required to invite a user.
253    ///
254    /// This field was redacted in room versions 1 through 10. Starting from room version 11 it is
255    /// preserved.
256    #[serde(
257        default,
258        skip_serializing_if = "ruma_common::serde::is_default",
259        deserialize_with = "ruma_common::serde::deserialize_v1_powerlevel"
260    )]
261    pub invite: Int,
262
263    /// The level required to kick a user.
264    #[serde(
265        default = "default_power_level",
266        skip_serializing_if = "is_default_power_level",
267        deserialize_with = "ruma_common::serde::deserialize_v1_powerlevel"
268    )]
269    pub kick: Int,
270
271    /// The level required to redact an event.
272    #[serde(
273        default = "default_power_level",
274        skip_serializing_if = "is_default_power_level",
275        deserialize_with = "ruma_common::serde::deserialize_v1_powerlevel"
276    )]
277    pub redact: Int,
278
279    /// The default level required to send state events.
280    #[serde(
281        default = "default_power_level",
282        skip_serializing_if = "is_default_power_level",
283        deserialize_with = "ruma_common::serde::deserialize_v1_powerlevel"
284    )]
285    pub state_default: Int,
286
287    /// The power levels for specific users.
288    ///
289    /// This is a mapping from `user_id` to power level for that user.
290    #[serde(
291        default,
292        skip_serializing_if = "BTreeMap::is_empty",
293        deserialize_with = "ruma_common::serde::btreemap_deserialize_v1_powerlevel_values"
294    )]
295    pub users: BTreeMap<OwnedUserId, Int>,
296
297    /// The default power level for every user in the room.
298    #[serde(
299        default,
300        skip_serializing_if = "ruma_common::serde::is_default",
301        deserialize_with = "ruma_common::serde::deserialize_v1_powerlevel"
302    )]
303    pub users_default: Int,
304}
305
306impl StaticEventContent for RedactedRoomPowerLevelsEventContent {
307    const TYPE: &'static str = RoomPowerLevelsEventContent::TYPE;
308    type IsPrefix = <RoomPowerLevelsEventContent as StaticEventContent>::IsPrefix;
309}
310
311impl RedactedStateEventContent for RedactedRoomPowerLevelsEventContent {
312    type StateKey = EmptyStateKey;
313
314    fn event_type(&self) -> StateEventType {
315        StateEventType::RoomPowerLevels
316    }
317}
318
319/// The power level of a particular user.
320///
321/// Is either considered "infinite" if that user is a room creator, or an integer if they are not.
322#[derive(PartialEq, Copy, Clone, Eq, Debug)]
323#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
324pub enum UserPowerLevel {
325    /// The user is considered to have "infinite" power level, due to being a room creator, from
326    /// room version 12 onwards.
327    Infinite,
328
329    /// The user is either not a creator, or the room version is prior to 12, and hence has an
330    /// integer power level.
331    Int(Int),
332}
333
334impl Ord for UserPowerLevel {
335    fn cmp(&self, other: &Self) -> Ordering {
336        match (self, other) {
337            (UserPowerLevel::Infinite, UserPowerLevel::Infinite) => Ordering::Equal,
338            (UserPowerLevel::Infinite, UserPowerLevel::Int(_)) => Ordering::Greater,
339            (UserPowerLevel::Int(_), UserPowerLevel::Infinite) => Ordering::Less,
340            (UserPowerLevel::Int(self_int), UserPowerLevel::Int(other_int)) => {
341                self_int.cmp(other_int)
342            }
343        }
344    }
345}
346
347impl PartialOrd for UserPowerLevel {
348    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
349        Some(self.cmp(other))
350    }
351}
352
353impl PartialEq<Int> for UserPowerLevel {
354    fn eq(&self, other: &Int) -> bool {
355        match self {
356            UserPowerLevel::Infinite => false,
357            UserPowerLevel::Int(int) => int.eq(other),
358        }
359    }
360}
361
362impl PartialEq<UserPowerLevel> for Int {
363    fn eq(&self, other: &UserPowerLevel) -> bool {
364        other.eq(self)
365    }
366}
367
368impl PartialOrd<Int> for UserPowerLevel {
369    fn partial_cmp(&self, other: &Int) -> Option<Ordering> {
370        match self {
371            UserPowerLevel::Infinite => Some(Ordering::Greater),
372            UserPowerLevel::Int(int) => int.partial_cmp(other),
373        }
374    }
375}
376
377impl PartialOrd<UserPowerLevel> for Int {
378    fn partial_cmp(&self, other: &UserPowerLevel) -> Option<Ordering> {
379        match other {
380            UserPowerLevel::Infinite => Some(Ordering::Less),
381            UserPowerLevel::Int(int) => self.partial_cmp(int),
382        }
383    }
384}
385
386impl From<Int> for UserPowerLevel {
387    fn from(value: Int) -> Self {
388        Self::Int(value)
389    }
390}
391
392/// The effective power levels of a room.
393///
394/// This struct contains all the power levels settings from the specification and can be constructed
395/// from several [`RoomPowerLevelsSource`]s, which means that it can be used when wanting to inspect
396/// the power levels of a room, regardless of whether the most recent power levels event is redacted
397/// or not, or the room has no power levels event.
398///
399/// This can also be used to change the power levels of a room by mutating it and then converting it
400/// to a [`RoomPowerLevelsEventContent`] using `RoomPowerLevelsEventContent::try_from` /
401/// `.try_into()`. This allows to validate the format of the power levels before sending them. Note
402/// that the homeserver might still refuse the power levels changes depending on the current power
403/// level of the sender.
404#[derive(Clone, Debug)]
405#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
406pub struct RoomPowerLevels {
407    /// The level required to ban a user.
408    ///
409    /// When built from [`RoomPowerLevelsSource::None`], defaults to `50`.
410    pub ban: Int,
411
412    /// The level required to send specific event types.
413    ///
414    /// This is a mapping from event type to power level required.
415    ///
416    /// When built from [`RoomPowerLevelsSource::None`], defaults to an empty map.
417    pub events: BTreeMap<TimelineEventType, Int>,
418
419    /// The default level required to send message events.
420    ///
421    /// When built from [`RoomPowerLevelsSource::None`], defaults to `0`.
422    pub events_default: Int,
423
424    /// The level required to invite a user.
425    ///
426    /// When built from [`RoomPowerLevelsSource::None`], defaults to `0`.
427    pub invite: Int,
428
429    /// The level required to kick a user.
430    ///
431    /// When built from [`RoomPowerLevelsSource::None`], defaults to `50`.
432    pub kick: Int,
433
434    /// The level required to redact an event.
435    ///
436    /// When built from [`RoomPowerLevelsSource::None`], defaults to `50`.
437    pub redact: Int,
438
439    /// The default level required to send state events.
440    ///
441    /// When built from [`RoomPowerLevelsSource::None`], defaults to `50`.
442    pub state_default: Int,
443
444    /// The power levels for specific users.
445    ///
446    /// This is a mapping from `user_id` to power level for that user.
447    ///
448    /// Must NOT contain creators of the room in room versions where the
449    /// `explicitly_privilege_room_creators` field of [`AuthorizationRules`] is set to `true`. This
450    /// would result in an error when trying to convert this to a [`RoomPowerLevelsEventContent`].
451    ///
452    /// When built from [`RoomPowerLevelsSource::None`]:
453    ///
454    /// * If `explicitly_privilege_room_creators` is set to `false` for the room version, defaults
455    ///   to setting the power level to `100` for the creator(s) of the room.
456    /// * Otherwise, defaults to an empty map.
457    pub users: BTreeMap<OwnedUserId, Int>,
458
459    /// The default power level for every user in the room.
460    ///
461    /// When built from [`RoomPowerLevelsSource::None`], defaults to `0`.
462    pub users_default: Int,
463
464    /// The power level requirements for specific notification types.
465    ///
466    /// This is a mapping from `key` to power level for that notifications key.
467    ///
468    /// When built from [`RoomPowerLevelsSource::None`], uses its `Default` implementation.
469    pub notifications: NotificationPowerLevels,
470
471    /// The tweaks for determining the power level of a user.
472    pub rules: RoomPowerLevelsRules,
473}
474
475impl RoomPowerLevels {
476    /// Constructs `RoomPowerLevels` from `RoomPowerLevelsSource`, `AuthorizationRules` and the
477    /// creators of a room.
478    pub fn new(
479        power_levels: RoomPowerLevelsSource,
480        rules: &AuthorizationRules,
481        creators: impl IntoIterator<Item = OwnedUserId> + Clone,
482    ) -> Self {
483        match power_levels {
484            RoomPowerLevelsSource::Original(RoomPowerLevelsEventContent {
485                ban,
486                events,
487                events_default,
488                invite,
489                kick,
490                redact,
491                state_default,
492                users,
493                users_default,
494                notifications,
495            }) => Self {
496                ban,
497                events,
498                events_default,
499                invite,
500                kick,
501                redact,
502                state_default,
503                users,
504                users_default,
505                notifications,
506                rules: RoomPowerLevelsRules::new(rules, creators),
507            },
508            RoomPowerLevelsSource::Redacted(RedactedRoomPowerLevelsEventContent {
509                ban,
510                events,
511                events_default,
512                invite,
513                kick,
514                redact,
515                state_default,
516                users,
517                users_default,
518            }) => Self {
519                ban,
520                events,
521                events_default,
522                invite,
523                kick,
524                redact,
525                state_default,
526                users,
527                users_default,
528                notifications: NotificationPowerLevels::new(),
529                rules: RoomPowerLevelsRules::new(rules, creators),
530            },
531            // events_default, users_default and invite having a default of 0 while the others have
532            // a default of 50 is not an oversight, these defaults are from the Matrix
533            // specification.
534            RoomPowerLevelsSource::None => Self {
535                ban: default_power_level(),
536                events: BTreeMap::new(),
537                events_default: int!(0),
538                invite: int!(0),
539                kick: default_power_level(),
540                redact: default_power_level(),
541                state_default: default_power_level(),
542                users: if rules.explicitly_privilege_room_creators {
543                    BTreeMap::new()
544                } else {
545                    // If creators are not explicitly privileged, their power level is 100 if there
546                    // is no power levels state.
547                    BTreeMap::from_iter(creators.clone().into_iter().map(|user| (user, int!(100))))
548                },
549                users_default: int!(0),
550                notifications: NotificationPowerLevels::default(),
551                rules: RoomPowerLevelsRules::new(rules, creators),
552            },
553        }
554    }
555
556    /// Whether the given user ID is a privileged creator.
557    fn is_privileged_creator(&self, user_id: &UserId) -> bool {
558        self.rules.privileged_creators.as_ref().is_some_and(|creators| creators.contains(user_id))
559    }
560
561    /// Get the power level of a specific user.
562    pub fn for_user(&self, user_id: &UserId) -> UserPowerLevel {
563        if self.is_privileged_creator(user_id) {
564            return UserPowerLevel::Infinite;
565        }
566
567        self.users.get(user_id).map_or(self.users_default, |pl| *pl).into()
568    }
569
570    /// Get the power level required to perform a given action.
571    pub fn for_action(&self, action: PowerLevelAction) -> Int {
572        match action {
573            PowerLevelAction::Ban => self.ban,
574            PowerLevelAction::Unban => self.ban.max(self.kick),
575            PowerLevelAction::Invite => self.invite,
576            PowerLevelAction::Kick => self.kick,
577            PowerLevelAction::RedactOwn => self.for_message(MessageLikeEventType::RoomRedaction),
578            PowerLevelAction::RedactOther => {
579                self.redact.max(self.for_message(MessageLikeEventType::RoomRedaction))
580            }
581            PowerLevelAction::SendMessage(msg_type) => self.for_message(msg_type),
582            PowerLevelAction::SendState(state_type) => self.for_state(state_type),
583            PowerLevelAction::TriggerNotification(NotificationPowerLevelType::Room) => {
584                self.notifications.room
585            }
586        }
587    }
588
589    /// Get the power level required to send the given message type.
590    pub fn for_message(&self, msg_type: MessageLikeEventType) -> Int {
591        self.events.get(&msg_type.into()).copied().unwrap_or(self.events_default)
592    }
593
594    /// Get the power level required to send the given state event type.
595    pub fn for_state(&self, state_type: StateEventType) -> Int {
596        self.events.get(&state_type.into()).copied().unwrap_or(self.state_default)
597    }
598
599    /// Whether the given user can ban other users based on the power levels.
600    ///
601    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::Ban)`.
602    pub fn user_can_ban(&self, user_id: &UserId) -> bool {
603        self.for_user(user_id) >= self.ban
604    }
605
606    /// Whether the acting user can ban the target user based on the power levels.
607    ///
608    /// On top of `power_levels.user_can_ban(acting_user_id)`, this performs an extra check
609    /// to make sure the acting user has at greater power level than the target user.
610    ///
611    /// Shorthand for `power_levels.user_can_do_to_user(acting_user_id, target_user_id,
612    /// PowerLevelUserAction::Ban)`.
613    pub fn user_can_ban_user(&self, acting_user_id: &UserId, target_user_id: &UserId) -> bool {
614        let acting_pl = self.for_user(acting_user_id);
615        let target_pl = self.for_user(target_user_id);
616        acting_pl >= self.ban && target_pl < acting_pl
617    }
618
619    /// Whether the given user can unban other users based on the power levels.
620    ///
621    /// This action requires to be allowed to ban and to kick.
622    ///
623    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::Unban)`.
624    pub fn user_can_unban(&self, user_id: &UserId) -> bool {
625        let pl = self.for_user(user_id);
626        pl >= self.ban && pl >= self.kick
627    }
628
629    /// Whether the acting user can unban the target user based on the power levels.
630    ///
631    /// This action requires to be allowed to ban and to kick.
632    ///
633    /// On top of `power_levels.user_can_unban(acting_user_id)`, this performs an extra check
634    /// to make sure the acting user has at greater power level than the target user.
635    ///
636    /// Shorthand for `power_levels.user_can_do_to_user(acting_user_id, target_user_id,
637    /// PowerLevelUserAction::Unban)`.
638    pub fn user_can_unban_user(&self, acting_user_id: &UserId, target_user_id: &UserId) -> bool {
639        let acting_pl = self.for_user(acting_user_id);
640        let target_pl = self.for_user(target_user_id);
641        acting_pl >= self.ban && acting_pl >= self.kick && target_pl < acting_pl
642    }
643
644    /// Whether the given user can invite other users based on the power levels.
645    ///
646    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::Invite)`.
647    pub fn user_can_invite(&self, user_id: &UserId) -> bool {
648        self.for_user(user_id) >= self.invite
649    }
650
651    /// Whether the given user can kick other users based on the power levels.
652    ///
653    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::Kick)`.
654    pub fn user_can_kick(&self, user_id: &UserId) -> bool {
655        self.for_user(user_id) >= self.kick
656    }
657
658    /// Whether the acting user can kick the target user based on the power levels.
659    ///
660    /// On top of `power_levels.user_can_kick(acting_user_id)`, this performs an extra check
661    /// to make sure the acting user has at least the same power level as the target user.
662    ///
663    /// Shorthand for `power_levels.user_can_do_to_user(acting_user_id, target_user_id,
664    /// PowerLevelUserAction::Kick)`.
665    pub fn user_can_kick_user(&self, acting_user_id: &UserId, target_user_id: &UserId) -> bool {
666        let acting_pl = self.for_user(acting_user_id);
667        let target_pl = self.for_user(target_user_id);
668        acting_pl >= self.kick && target_pl < acting_pl
669    }
670
671    /// Whether the given user can redact their own events based on the power levels.
672    ///
673    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::RedactOwn)`.
674    pub fn user_can_redact_own_event(&self, user_id: &UserId) -> bool {
675        self.user_can_send_message(user_id, MessageLikeEventType::RoomRedaction)
676    }
677
678    /// Whether the given user can redact events of other users based on the power levels.
679    ///
680    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::RedactOthers)`.
681    pub fn user_can_redact_event_of_other(&self, user_id: &UserId) -> bool {
682        self.user_can_redact_own_event(user_id) && self.for_user(user_id) >= self.redact
683    }
684
685    /// Whether the given user can send message events based on the power levels.
686    ///
687    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::SendMessage(msg_type))`.
688    pub fn user_can_send_message(&self, user_id: &UserId, msg_type: MessageLikeEventType) -> bool {
689        self.for_user(user_id) >= self.for_message(msg_type)
690    }
691
692    /// Whether the given user can send state events based on the power levels.
693    ///
694    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::SendState(state_type))`.
695    pub fn user_can_send_state(&self, user_id: &UserId, state_type: StateEventType) -> bool {
696        self.for_user(user_id) >= self.for_state(state_type)
697    }
698
699    /// Whether the given user can notify everybody in the room by writing `@room` in a message.
700    ///
701    /// Shorthand for `power_levels.user_can_do(user_id,
702    /// PowerLevelAction::TriggerNotification(NotificationPowerLevelType::Room))`.
703    pub fn user_can_trigger_room_notification(&self, user_id: &UserId) -> bool {
704        self.for_user(user_id) >= self.notifications.room
705    }
706
707    /// Whether the acting user can change the power level of the target user.
708    ///
709    /// Shorthand for `power_levels.user_can_do_to_user(acting_user_id, target_user_id,
710    /// PowerLevelUserAction::ChangePowerLevel`.
711    pub fn user_can_change_user_power_level(
712        &self,
713        acting_user_id: &UserId,
714        target_user_id: &UserId,
715    ) -> bool {
716        // Check that the user can change the power levels first.
717        if !self.user_can_send_state(acting_user_id, StateEventType::RoomPowerLevels) {
718            return false;
719        }
720
721        // No one can change the power level of a privileged creator.
722        if self.is_privileged_creator(target_user_id) {
723            return false;
724        }
725
726        // A user can change their own power level.
727        if acting_user_id == target_user_id {
728            return true;
729        }
730
731        // The permission is different whether the target user is added or changed/removed, so
732        // we need to check that.
733        if let Some(target_pl) = self.users.get(target_user_id).copied() {
734            self.for_user(acting_user_id) > target_pl
735        } else {
736            true
737        }
738    }
739
740    /// Whether the given user can do the given action based on the power levels.
741    pub fn user_can_do(&self, user_id: &UserId, action: PowerLevelAction) -> bool {
742        match action {
743            PowerLevelAction::Ban => self.user_can_ban(user_id),
744            PowerLevelAction::Unban => self.user_can_unban(user_id),
745            PowerLevelAction::Invite => self.user_can_invite(user_id),
746            PowerLevelAction::Kick => self.user_can_kick(user_id),
747            PowerLevelAction::RedactOwn => self.user_can_redact_own_event(user_id),
748            PowerLevelAction::RedactOther => self.user_can_redact_event_of_other(user_id),
749            PowerLevelAction::SendMessage(message_type) => {
750                self.user_can_send_message(user_id, message_type)
751            }
752            PowerLevelAction::SendState(state_type) => {
753                self.user_can_send_state(user_id, state_type)
754            }
755            PowerLevelAction::TriggerNotification(NotificationPowerLevelType::Room) => {
756                self.user_can_trigger_room_notification(user_id)
757            }
758        }
759    }
760
761    /// Whether the acting user can do the given action to the target user based on the power
762    /// levels.
763    pub fn user_can_do_to_user(
764        &self,
765        acting_user_id: &UserId,
766        target_user_id: &UserId,
767        action: PowerLevelUserAction,
768    ) -> bool {
769        match action {
770            PowerLevelUserAction::Ban => self.user_can_ban_user(acting_user_id, target_user_id),
771            PowerLevelUserAction::Unban => self.user_can_unban_user(acting_user_id, target_user_id),
772            PowerLevelUserAction::Invite => self.user_can_invite(acting_user_id),
773            PowerLevelUserAction::Kick => self.user_can_kick_user(acting_user_id, target_user_id),
774            PowerLevelUserAction::ChangePowerLevel => {
775                self.user_can_change_user_power_level(acting_user_id, target_user_id)
776            }
777        }
778    }
779
780    /// Get the maximum power level of any user.
781    pub fn max(&self) -> Int {
782        self.users.values().fold(self.users_default, |max_pl, user_pl| max(max_pl, *user_pl))
783    }
784}
785
786/// An error encountered when trying to build a [`RoomPowerLevelsEventContent`] from
787/// [`RoomPowerLevels`].
788#[derive(Copy, Clone, Debug, PartialEq, Eq, thiserror::Error)]
789#[non_exhaustive]
790pub enum PowerLevelsError {
791    /// A creator is in the `users` map, and it is not allowed by the current room version.
792    #[error("a creator is in the `users` map, and it is not allowed by the current room version")]
793    CreatorInUsersMap,
794}
795
796impl TryFrom<RoomPowerLevels> for RoomPowerLevelsEventContent {
797    type Error = PowerLevelsError;
798
799    fn try_from(c: RoomPowerLevels) -> Result<Self, Self::Error> {
800        if c.rules.privileged_creators.as_ref().is_some_and(|creators| {
801            !c.users.is_empty() && creators.iter().any(|user_id| c.users.contains_key(user_id))
802        }) {
803            return Err(PowerLevelsError::CreatorInUsersMap);
804        }
805
806        Ok(Self {
807            ban: c.ban,
808            events: c.events,
809            events_default: c.events_default,
810            invite: c.invite,
811            kick: c.kick,
812            redact: c.redact,
813            state_default: c.state_default,
814            users: c.users,
815            users_default: c.users_default,
816            notifications: c.notifications,
817        })
818    }
819}
820
821impl From<RoomPowerLevels> for PushConditionPowerLevelsCtx {
822    fn from(c: RoomPowerLevels) -> Self {
823        Self::new(c.users, c.users_default, c.notifications, c.rules)
824    }
825}
826
827/// The possible power level sources for [`RoomPowerLevels`].
828#[derive(Default)]
829#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
830pub enum RoomPowerLevelsSource {
831    /// Construct `RoomPowerLevels` from the non-redacted `m.room.power_levels` event content.
832    Original(RoomPowerLevelsEventContent),
833    /// Construct `RoomPowerLevels` from the redacted `m.room.power_levels` event content.
834    Redacted(RedactedRoomPowerLevelsEventContent),
835    /// Use the default values defined in the specification.
836    ///
837    /// Should only be used when there is no power levels state in a room.
838    #[default]
839    None,
840}
841
842impl From<Option<RoomPowerLevelsEventContent>> for RoomPowerLevelsSource {
843    fn from(value: Option<RoomPowerLevelsEventContent>) -> Self {
844        value.map(Self::Original).unwrap_or_default()
845    }
846}
847
848impl From<Option<RedactedRoomPowerLevelsEventContent>> for RoomPowerLevelsSource {
849    fn from(value: Option<RedactedRoomPowerLevelsEventContent>) -> Self {
850        value.map(Self::Redacted).unwrap_or_default()
851    }
852}
853
854impl From<RoomPowerLevelsEventContent> for RoomPowerLevelsSource {
855    fn from(value: RoomPowerLevelsEventContent) -> Self {
856        Self::Original(value)
857    }
858}
859
860impl From<RedactedRoomPowerLevelsEventContent> for RoomPowerLevelsSource {
861    fn from(value: RedactedRoomPowerLevelsEventContent) -> Self {
862        Self::Redacted(value)
863    }
864}
865
866/// The actions that can be limited by power levels.
867#[derive(Clone, Debug, PartialEq, Eq)]
868#[non_exhaustive]
869pub enum PowerLevelAction {
870    /// Ban a user.
871    Ban,
872
873    /// Unban a user.
874    Unban,
875
876    /// Invite a user.
877    Invite,
878
879    /// Kick a user.
880    Kick,
881
882    /// Redact one's own event.
883    RedactOwn,
884
885    /// Redact the event of another user.
886    RedactOther,
887
888    /// Send a message-like event.
889    SendMessage(MessageLikeEventType),
890
891    /// Send a state event.
892    SendState(StateEventType),
893
894    /// Trigger a notification.
895    TriggerNotification(NotificationPowerLevelType),
896}
897
898/// The notification types that can be limited by power levels.
899#[derive(Clone, Debug, PartialEq, Eq)]
900#[non_exhaustive]
901pub enum NotificationPowerLevelType {
902    /// `@room` notifications.
903    Room,
904}
905
906/// The actions to other users that can be limited by power levels.
907#[derive(Clone, Debug, PartialEq, Eq)]
908#[non_exhaustive]
909pub enum PowerLevelUserAction {
910    /// Ban a user.
911    Ban,
912
913    /// Unban a user.
914    Unban,
915
916    /// Invite a user.
917    Invite,
918
919    /// Kick a user.
920    Kick,
921
922    /// Change a user's power level.
923    ChangePowerLevel,
924}
925
926#[cfg(test)]
927mod tests {
928    use std::collections::BTreeMap;
929
930    use assign::assign;
931    use js_int::int;
932    use maplit::btreemap;
933    use ruma_common::{owned_user_id, room_version_rules::AuthorizationRules, user_id};
934    use serde_json::{json, to_value as to_json_value};
935
936    use super::{
937        default_power_level, NotificationPowerLevels, RoomPowerLevels, RoomPowerLevelsEventContent,
938        RoomPowerLevelsSource,
939    };
940
941    #[test]
942    fn serialization_with_optional_fields_as_none() {
943        let default = default_power_level();
944
945        let power_levels = RoomPowerLevelsEventContent {
946            ban: default,
947            events: BTreeMap::new(),
948            events_default: int!(0),
949            invite: int!(0),
950            kick: default,
951            redact: default,
952            state_default: default,
953            users: BTreeMap::new(),
954            users_default: int!(0),
955            notifications: NotificationPowerLevels::default(),
956        };
957
958        let actual = to_json_value(&power_levels).unwrap();
959        let expected = json!({});
960
961        assert_eq!(actual, expected);
962    }
963
964    #[test]
965    fn serialization_with_all_fields() {
966        let user = user_id!("@carl:example.com");
967        let power_levels_event = RoomPowerLevelsEventContent {
968            ban: int!(23),
969            events: btreemap! {
970                "m.dummy".into() => int!(23)
971            },
972            events_default: int!(23),
973            invite: int!(23),
974            kick: int!(23),
975            redact: int!(23),
976            state_default: int!(23),
977            users: btreemap! {
978                user.to_owned() => int!(23)
979            },
980            users_default: int!(23),
981            notifications: assign!(NotificationPowerLevels::new(), { room: int!(23) }),
982        };
983
984        let actual = to_json_value(&power_levels_event).unwrap();
985        let expected = json!({
986            "ban": 23,
987            "events": {
988                "m.dummy": 23
989            },
990            "events_default": 23,
991            "invite": 23,
992            "kick": 23,
993            "redact": 23,
994            "state_default": 23,
995            "users": {
996                "@carl:example.com": 23
997            },
998            "users_default": 23,
999            "notifications": {
1000                "room": 23
1001            },
1002        });
1003
1004        assert_eq!(actual, expected);
1005    }
1006
1007    #[test]
1008    fn cannot_change_power_level_of_privileged_creator() {
1009        let creator = user_id!("@lola:localhost");
1010
1011        let v1_power_levels = RoomPowerLevels::new(
1012            RoomPowerLevelsSource::None,
1013            &AuthorizationRules::V1,
1014            vec![creator.to_owned()],
1015        );
1016        assert!(v1_power_levels.user_can_change_user_power_level(creator, creator));
1017
1018        let v12_power_levels = RoomPowerLevels::new(
1019            RoomPowerLevelsSource::None,
1020            &AuthorizationRules::V12,
1021            vec![creator.to_owned()],
1022        );
1023        assert!(!v12_power_levels.user_can_change_user_power_level(creator, creator));
1024    }
1025
1026    #[test]
1027    fn cannot_convert_to_event_content_with_creator_in_users() {
1028        let creator = owned_user_id!("@lola:localhost");
1029
1030        let mut v1_power_levels = RoomPowerLevels::new(
1031            RoomPowerLevelsSource::None,
1032            &AuthorizationRules::V1,
1033            vec![creator.clone()],
1034        );
1035        v1_power_levels.users.insert(creator.clone(), int!(75));
1036        let v1_event_content = RoomPowerLevelsEventContent::try_from(v1_power_levels).unwrap();
1037        assert_eq!(*v1_event_content.users.get(&creator).unwrap(), int!(75));
1038
1039        let mut v12_power_levels = RoomPowerLevels::new(
1040            RoomPowerLevelsSource::None,
1041            &AuthorizationRules::V12,
1042            vec![creator.to_owned()],
1043        );
1044        v12_power_levels.users.insert(creator.clone(), int!(75));
1045        RoomPowerLevelsEventContent::try_from(v12_power_levels).unwrap_err();
1046    }
1047}