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::{Ordering, max},
7    collections::BTreeMap,
8};
9
10use js_int::{Int, int};
11use ruma_common::{
12    OwnedUserId, UserId,
13    power_levels::{NotificationPowerLevels, default_power_level},
14    push::PushConditionPowerLevelsCtx,
15    room_version_rules::{AuthorizationRules, RedactionRules, RoomPowerLevelsRules},
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
319impl From<RedactedRoomPowerLevelsEventContent> for PossiblyRedactedRoomPowerLevelsEventContent {
320    fn from(value: RedactedRoomPowerLevelsEventContent) -> Self {
321        let RedactedRoomPowerLevelsEventContent {
322            ban,
323            events,
324            events_default,
325            invite,
326            kick,
327            redact,
328            state_default,
329            users,
330            users_default,
331        } = value;
332
333        Self {
334            ban,
335            events,
336            events_default,
337            invite,
338            kick,
339            redact,
340            state_default,
341            users,
342            users_default,
343            notifications: NotificationPowerLevels::default(),
344        }
345    }
346}
347
348/// The power level of a particular user.
349///
350/// Is either considered "infinite" if that user is a room creator, or an integer if they are not.
351#[derive(PartialEq, Copy, Clone, Eq, Debug)]
352#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
353pub enum UserPowerLevel {
354    /// The user is considered to have "infinite" power level, due to being a room creator, from
355    /// room version 12 onwards.
356    Infinite,
357
358    /// The user is either not a creator, or the room version is prior to 12, and hence has an
359    /// integer power level.
360    Int(Int),
361}
362
363impl Ord for UserPowerLevel {
364    fn cmp(&self, other: &Self) -> Ordering {
365        match (self, other) {
366            (UserPowerLevel::Infinite, UserPowerLevel::Infinite) => Ordering::Equal,
367            (UserPowerLevel::Infinite, UserPowerLevel::Int(_)) => Ordering::Greater,
368            (UserPowerLevel::Int(_), UserPowerLevel::Infinite) => Ordering::Less,
369            (UserPowerLevel::Int(self_int), UserPowerLevel::Int(other_int)) => {
370                self_int.cmp(other_int)
371            }
372        }
373    }
374}
375
376impl PartialOrd for UserPowerLevel {
377    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
378        Some(self.cmp(other))
379    }
380}
381
382impl PartialEq<Int> for UserPowerLevel {
383    fn eq(&self, other: &Int) -> bool {
384        match self {
385            UserPowerLevel::Infinite => false,
386            UserPowerLevel::Int(int) => int.eq(other),
387        }
388    }
389}
390
391impl PartialEq<UserPowerLevel> for Int {
392    fn eq(&self, other: &UserPowerLevel) -> bool {
393        other.eq(self)
394    }
395}
396
397impl PartialOrd<Int> for UserPowerLevel {
398    fn partial_cmp(&self, other: &Int) -> Option<Ordering> {
399        match self {
400            UserPowerLevel::Infinite => Some(Ordering::Greater),
401            UserPowerLevel::Int(int) => int.partial_cmp(other),
402        }
403    }
404}
405
406impl PartialOrd<UserPowerLevel> for Int {
407    fn partial_cmp(&self, other: &UserPowerLevel) -> Option<Ordering> {
408        match other {
409            UserPowerLevel::Infinite => Some(Ordering::Less),
410            UserPowerLevel::Int(int) => self.partial_cmp(int),
411        }
412    }
413}
414
415impl From<Int> for UserPowerLevel {
416    fn from(value: Int) -> Self {
417        Self::Int(value)
418    }
419}
420
421/// The effective power levels of a room.
422///
423/// This struct contains all the power levels settings from the specification and can be constructed
424/// from several [`RoomPowerLevelsSource`]s, which means that it can be used when wanting to inspect
425/// the power levels of a room, regardless of whether the most recent power levels event is redacted
426/// or not, or the room has no power levels event.
427///
428/// This can also be used to change the power levels of a room by mutating it and then converting it
429/// to a [`RoomPowerLevelsEventContent`] using `RoomPowerLevelsEventContent::try_from` /
430/// `.try_into()`. This allows to validate the format of the power levels before sending them. Note
431/// that the homeserver might still refuse the power levels changes depending on the current power
432/// level of the sender.
433#[derive(Clone, Debug)]
434#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
435pub struct RoomPowerLevels {
436    /// The level required to ban a user.
437    ///
438    /// When built from [`RoomPowerLevelsSource::None`], defaults to `50`.
439    pub ban: Int,
440
441    /// The level required to send specific event types.
442    ///
443    /// This is a mapping from event type to power level required.
444    ///
445    /// When built from [`RoomPowerLevelsSource::None`], defaults to an empty map.
446    pub events: BTreeMap<TimelineEventType, Int>,
447
448    /// The default level required to send message events.
449    ///
450    /// When built from [`RoomPowerLevelsSource::None`], defaults to `0`.
451    pub events_default: Int,
452
453    /// The level required to invite a user.
454    ///
455    /// When built from [`RoomPowerLevelsSource::None`], defaults to `0`.
456    pub invite: Int,
457
458    /// The level required to kick a user.
459    ///
460    /// When built from [`RoomPowerLevelsSource::None`], defaults to `50`.
461    pub kick: Int,
462
463    /// The level required to redact an event.
464    ///
465    /// When built from [`RoomPowerLevelsSource::None`], defaults to `50`.
466    pub redact: Int,
467
468    /// The default level required to send state events.
469    ///
470    /// When built from [`RoomPowerLevelsSource::None`], defaults to `50`.
471    pub state_default: Int,
472
473    /// The power levels for specific users.
474    ///
475    /// This is a mapping from `user_id` to power level for that user.
476    ///
477    /// Must NOT contain creators of the room in room versions where the
478    /// `explicitly_privilege_room_creators` field of [`AuthorizationRules`] is set to `true`. This
479    /// would result in an error when trying to convert this to a [`RoomPowerLevelsEventContent`].
480    ///
481    /// When built from [`RoomPowerLevelsSource::None`]:
482    ///
483    /// * If `explicitly_privilege_room_creators` is set to `false` for the room version, defaults
484    ///   to setting the power level to `100` for the creator(s) of the room.
485    /// * Otherwise, defaults to an empty map.
486    pub users: BTreeMap<OwnedUserId, Int>,
487
488    /// The default power level for every user in the room.
489    ///
490    /// When built from [`RoomPowerLevelsSource::None`], defaults to `0`.
491    pub users_default: Int,
492
493    /// The power level requirements for specific notification types.
494    ///
495    /// This is a mapping from `key` to power level for that notifications key.
496    ///
497    /// When built from [`RoomPowerLevelsSource::None`], uses its `Default` implementation.
498    pub notifications: NotificationPowerLevels,
499
500    /// The tweaks for determining the power level of a user.
501    pub rules: RoomPowerLevelsRules,
502}
503
504impl RoomPowerLevels {
505    /// Constructs `RoomPowerLevels` from `RoomPowerLevelsSource`, `AuthorizationRules` and the
506    /// creators of a room.
507    pub fn new(
508        power_levels: RoomPowerLevelsSource,
509        rules: &AuthorizationRules,
510        creators: impl IntoIterator<Item = OwnedUserId> + Clone,
511    ) -> Self {
512        match power_levels {
513            RoomPowerLevelsSource::Original(RoomPowerLevelsEventContent {
514                ban,
515                events,
516                events_default,
517                invite,
518                kick,
519                redact,
520                state_default,
521                users,
522                users_default,
523                notifications,
524            }) => Self {
525                ban,
526                events,
527                events_default,
528                invite,
529                kick,
530                redact,
531                state_default,
532                users,
533                users_default,
534                notifications,
535                rules: RoomPowerLevelsRules::new(rules, creators),
536            },
537            RoomPowerLevelsSource::Redacted(RedactedRoomPowerLevelsEventContent {
538                ban,
539                events,
540                events_default,
541                invite,
542                kick,
543                redact,
544                state_default,
545                users,
546                users_default,
547            }) => Self {
548                ban,
549                events,
550                events_default,
551                invite,
552                kick,
553                redact,
554                state_default,
555                users,
556                users_default,
557                notifications: NotificationPowerLevels::new(),
558                rules: RoomPowerLevelsRules::new(rules, creators),
559            },
560            // events_default, users_default and invite having a default of 0 while the others have
561            // a default of 50 is not an oversight, these defaults are from the Matrix
562            // specification.
563            RoomPowerLevelsSource::None => Self {
564                ban: default_power_level(),
565                events: BTreeMap::new(),
566                events_default: int!(0),
567                invite: int!(0),
568                kick: default_power_level(),
569                redact: default_power_level(),
570                state_default: default_power_level(),
571                users: if rules.explicitly_privilege_room_creators {
572                    BTreeMap::new()
573                } else {
574                    // If creators are not explicitly privileged, their power level is 100 if there
575                    // is no power levels state.
576                    BTreeMap::from_iter(creators.clone().into_iter().map(|user| (user, int!(100))))
577                },
578                users_default: int!(0),
579                notifications: NotificationPowerLevels::default(),
580                rules: RoomPowerLevelsRules::new(rules, creators),
581            },
582        }
583    }
584
585    /// Whether the given user ID is a privileged creator.
586    fn is_privileged_creator(&self, user_id: &UserId) -> bool {
587        self.rules.privileged_creators.as_ref().is_some_and(|creators| creators.contains(user_id))
588    }
589
590    /// Get the power level of a specific user.
591    pub fn for_user(&self, user_id: &UserId) -> UserPowerLevel {
592        if self.is_privileged_creator(user_id) {
593            return UserPowerLevel::Infinite;
594        }
595
596        self.users.get(user_id).map_or(self.users_default, |pl| *pl).into()
597    }
598
599    /// Get the power level required to perform a given action.
600    pub fn for_action(&self, action: PowerLevelAction) -> Int {
601        match action {
602            PowerLevelAction::Ban => self.ban,
603            PowerLevelAction::Unban => self.ban.max(self.kick),
604            PowerLevelAction::Invite => self.invite,
605            PowerLevelAction::Kick => self.kick,
606            PowerLevelAction::RedactOwn => self.for_message(MessageLikeEventType::RoomRedaction),
607            PowerLevelAction::RedactOther => {
608                self.redact.max(self.for_message(MessageLikeEventType::RoomRedaction))
609            }
610            PowerLevelAction::SendMessage(msg_type) => self.for_message(msg_type),
611            PowerLevelAction::SendState(state_type) => self.for_state(state_type),
612            PowerLevelAction::TriggerNotification(NotificationPowerLevelType::Room) => {
613                self.notifications.room
614            }
615        }
616    }
617
618    /// Get the power level required to send the given message type.
619    pub fn for_message(&self, msg_type: MessageLikeEventType) -> Int {
620        self.events.get(&msg_type.into()).copied().unwrap_or(self.events_default)
621    }
622
623    /// Get the power level required to send the given state event type.
624    pub fn for_state(&self, state_type: StateEventType) -> Int {
625        self.events.get(&state_type.into()).copied().unwrap_or(self.state_default)
626    }
627
628    /// Whether the given user can ban other users based on the power levels.
629    ///
630    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::Ban)`.
631    pub fn user_can_ban(&self, user_id: &UserId) -> bool {
632        self.for_user(user_id) >= self.ban
633    }
634
635    /// Whether the acting user can ban the target user based on the power levels.
636    ///
637    /// On top of `power_levels.user_can_ban(acting_user_id)`, this performs an extra check
638    /// to make sure the acting user has at greater power level than the target user.
639    ///
640    /// Shorthand for `power_levels.user_can_do_to_user(acting_user_id, target_user_id,
641    /// PowerLevelUserAction::Ban)`.
642    pub fn user_can_ban_user(&self, acting_user_id: &UserId, target_user_id: &UserId) -> bool {
643        let acting_pl = self.for_user(acting_user_id);
644        let target_pl = self.for_user(target_user_id);
645        acting_pl >= self.ban && target_pl < acting_pl
646    }
647
648    /// Whether the given user can unban other users based on the power levels.
649    ///
650    /// This action requires to be allowed to ban and to kick.
651    ///
652    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::Unban)`.
653    pub fn user_can_unban(&self, user_id: &UserId) -> bool {
654        let pl = self.for_user(user_id);
655        pl >= self.ban && pl >= self.kick
656    }
657
658    /// Whether the acting user can unban the target user based on the power levels.
659    ///
660    /// This action requires to be allowed to ban and to kick.
661    ///
662    /// On top of `power_levels.user_can_unban(acting_user_id)`, this performs an extra check
663    /// to make sure the acting user has at greater power level than the target user.
664    ///
665    /// Shorthand for `power_levels.user_can_do_to_user(acting_user_id, target_user_id,
666    /// PowerLevelUserAction::Unban)`.
667    pub fn user_can_unban_user(&self, acting_user_id: &UserId, target_user_id: &UserId) -> bool {
668        let acting_pl = self.for_user(acting_user_id);
669        let target_pl = self.for_user(target_user_id);
670        acting_pl >= self.ban && acting_pl >= self.kick && target_pl < acting_pl
671    }
672
673    /// Whether the given user can invite other users based on the power levels.
674    ///
675    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::Invite)`.
676    pub fn user_can_invite(&self, user_id: &UserId) -> bool {
677        self.for_user(user_id) >= self.invite
678    }
679
680    /// Whether the given user can kick other users based on the power levels.
681    ///
682    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::Kick)`.
683    pub fn user_can_kick(&self, user_id: &UserId) -> bool {
684        self.for_user(user_id) >= self.kick
685    }
686
687    /// Whether the acting user can kick the target user based on the power levels.
688    ///
689    /// On top of `power_levels.user_can_kick(acting_user_id)`, this performs an extra check
690    /// to make sure the acting user has at least the same power level as the target user.
691    ///
692    /// Shorthand for `power_levels.user_can_do_to_user(acting_user_id, target_user_id,
693    /// PowerLevelUserAction::Kick)`.
694    pub fn user_can_kick_user(&self, acting_user_id: &UserId, target_user_id: &UserId) -> bool {
695        let acting_pl = self.for_user(acting_user_id);
696        let target_pl = self.for_user(target_user_id);
697        acting_pl >= self.kick && target_pl < acting_pl
698    }
699
700    /// Whether the given user can redact their own events based on the power levels.
701    ///
702    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::RedactOwn)`.
703    pub fn user_can_redact_own_event(&self, user_id: &UserId) -> bool {
704        self.user_can_send_message(user_id, MessageLikeEventType::RoomRedaction)
705    }
706
707    /// Whether the given user can redact events of other users based on the power levels.
708    ///
709    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::RedactOthers)`.
710    pub fn user_can_redact_event_of_other(&self, user_id: &UserId) -> bool {
711        self.user_can_redact_own_event(user_id) && self.for_user(user_id) >= self.redact
712    }
713
714    /// Whether the given user can send message events based on the power levels.
715    ///
716    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::SendMessage(msg_type))`.
717    pub fn user_can_send_message(&self, user_id: &UserId, msg_type: MessageLikeEventType) -> bool {
718        self.for_user(user_id) >= self.for_message(msg_type)
719    }
720
721    /// Whether the given user can send state events based on the power levels.
722    ///
723    /// Shorthand for `power_levels.user_can_do(user_id, PowerLevelAction::SendState(state_type))`.
724    pub fn user_can_send_state(&self, user_id: &UserId, state_type: StateEventType) -> bool {
725        self.for_user(user_id) >= self.for_state(state_type)
726    }
727
728    /// Whether the given user can notify everybody in the room by writing `@room` in a message.
729    ///
730    /// Shorthand for `power_levels.user_can_do(user_id,
731    /// PowerLevelAction::TriggerNotification(NotificationPowerLevelType::Room))`.
732    pub fn user_can_trigger_room_notification(&self, user_id: &UserId) -> bool {
733        self.for_user(user_id) >= self.notifications.room
734    }
735
736    /// Whether the acting user can change the power level of the target user.
737    ///
738    /// Shorthand for `power_levels.user_can_do_to_user(acting_user_id, target_user_id,
739    /// PowerLevelUserAction::ChangePowerLevel`.
740    pub fn user_can_change_user_power_level(
741        &self,
742        acting_user_id: &UserId,
743        target_user_id: &UserId,
744    ) -> bool {
745        // Check that the user can change the power levels first.
746        if !self.user_can_send_state(acting_user_id, StateEventType::RoomPowerLevels) {
747            return false;
748        }
749
750        // No one can change the power level of a privileged creator.
751        if self.is_privileged_creator(target_user_id) {
752            return false;
753        }
754
755        // A user can change their own power level.
756        if acting_user_id == target_user_id {
757            return true;
758        }
759
760        // The permission is different whether the target user is added or changed/removed, so
761        // we need to check that.
762        if let Some(target_pl) = self.users.get(target_user_id).copied() {
763            self.for_user(acting_user_id) > target_pl
764        } else {
765            true
766        }
767    }
768
769    /// Whether the given user can do the given action based on the power levels.
770    pub fn user_can_do(&self, user_id: &UserId, action: PowerLevelAction) -> bool {
771        match action {
772            PowerLevelAction::Ban => self.user_can_ban(user_id),
773            PowerLevelAction::Unban => self.user_can_unban(user_id),
774            PowerLevelAction::Invite => self.user_can_invite(user_id),
775            PowerLevelAction::Kick => self.user_can_kick(user_id),
776            PowerLevelAction::RedactOwn => self.user_can_redact_own_event(user_id),
777            PowerLevelAction::RedactOther => self.user_can_redact_event_of_other(user_id),
778            PowerLevelAction::SendMessage(message_type) => {
779                self.user_can_send_message(user_id, message_type)
780            }
781            PowerLevelAction::SendState(state_type) => {
782                self.user_can_send_state(user_id, state_type)
783            }
784            PowerLevelAction::TriggerNotification(NotificationPowerLevelType::Room) => {
785                self.user_can_trigger_room_notification(user_id)
786            }
787        }
788    }
789
790    /// Whether the acting user can do the given action to the target user based on the power
791    /// levels.
792    pub fn user_can_do_to_user(
793        &self,
794        acting_user_id: &UserId,
795        target_user_id: &UserId,
796        action: PowerLevelUserAction,
797    ) -> bool {
798        match action {
799            PowerLevelUserAction::Ban => self.user_can_ban_user(acting_user_id, target_user_id),
800            PowerLevelUserAction::Unban => self.user_can_unban_user(acting_user_id, target_user_id),
801            PowerLevelUserAction::Invite => self.user_can_invite(acting_user_id),
802            PowerLevelUserAction::Kick => self.user_can_kick_user(acting_user_id, target_user_id),
803            PowerLevelUserAction::ChangePowerLevel => {
804                self.user_can_change_user_power_level(acting_user_id, target_user_id)
805            }
806        }
807    }
808
809    /// Get the maximum power level of any user.
810    pub fn max(&self) -> Int {
811        self.users.values().fold(self.users_default, |max_pl, user_pl| max(max_pl, *user_pl))
812    }
813}
814
815/// An error encountered when trying to build a [`RoomPowerLevelsEventContent`] from
816/// [`RoomPowerLevels`].
817#[derive(Copy, Clone, Debug, PartialEq, Eq, thiserror::Error)]
818#[non_exhaustive]
819pub enum PowerLevelsError {
820    /// A creator is in the `users` map, and it is not allowed by the current room version.
821    #[error("a creator is in the `users` map, and it is not allowed by the current room version")]
822    CreatorInUsersMap,
823}
824
825impl TryFrom<RoomPowerLevels> for RoomPowerLevelsEventContent {
826    type Error = PowerLevelsError;
827
828    fn try_from(c: RoomPowerLevels) -> Result<Self, Self::Error> {
829        if c.rules.privileged_creators.as_ref().is_some_and(|creators| {
830            !c.users.is_empty() && creators.iter().any(|user_id| c.users.contains_key(user_id))
831        }) {
832            return Err(PowerLevelsError::CreatorInUsersMap);
833        }
834
835        Ok(Self {
836            ban: c.ban,
837            events: c.events,
838            events_default: c.events_default,
839            invite: c.invite,
840            kick: c.kick,
841            redact: c.redact,
842            state_default: c.state_default,
843            users: c.users,
844            users_default: c.users_default,
845            notifications: c.notifications,
846        })
847    }
848}
849
850impl From<RoomPowerLevels> for PushConditionPowerLevelsCtx {
851    fn from(c: RoomPowerLevels) -> Self {
852        Self::new(c.users, c.users_default, c.notifications, c.rules)
853    }
854}
855
856/// The possible power level sources for [`RoomPowerLevels`].
857#[derive(Default)]
858#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
859pub enum RoomPowerLevelsSource {
860    /// Construct `RoomPowerLevels` from the non-redacted `m.room.power_levels` event content.
861    Original(RoomPowerLevelsEventContent),
862    /// Construct `RoomPowerLevels` from the redacted `m.room.power_levels` event content.
863    Redacted(RedactedRoomPowerLevelsEventContent),
864    /// Use the default values defined in the specification.
865    ///
866    /// Should only be used when there is no power levels state in a room.
867    #[default]
868    None,
869}
870
871impl From<Option<RoomPowerLevelsEventContent>> for RoomPowerLevelsSource {
872    fn from(value: Option<RoomPowerLevelsEventContent>) -> Self {
873        value.map(Self::Original).unwrap_or_default()
874    }
875}
876
877impl From<Option<RedactedRoomPowerLevelsEventContent>> for RoomPowerLevelsSource {
878    fn from(value: Option<RedactedRoomPowerLevelsEventContent>) -> Self {
879        value.map(Self::Redacted).unwrap_or_default()
880    }
881}
882
883impl From<RoomPowerLevelsEventContent> for RoomPowerLevelsSource {
884    fn from(value: RoomPowerLevelsEventContent) -> Self {
885        Self::Original(value)
886    }
887}
888
889impl From<RedactedRoomPowerLevelsEventContent> for RoomPowerLevelsSource {
890    fn from(value: RedactedRoomPowerLevelsEventContent) -> Self {
891        Self::Redacted(value)
892    }
893}
894
895/// The actions that can be limited by power levels.
896#[derive(Clone, Debug, PartialEq, Eq)]
897#[non_exhaustive]
898pub enum PowerLevelAction {
899    /// Ban a user.
900    Ban,
901
902    /// Unban a user.
903    Unban,
904
905    /// Invite a user.
906    Invite,
907
908    /// Kick a user.
909    Kick,
910
911    /// Redact one's own event.
912    RedactOwn,
913
914    /// Redact the event of another user.
915    RedactOther,
916
917    /// Send a message-like event.
918    SendMessage(MessageLikeEventType),
919
920    /// Send a state event.
921    SendState(StateEventType),
922
923    /// Trigger a notification.
924    TriggerNotification(NotificationPowerLevelType),
925}
926
927/// The notification types that can be limited by power levels.
928#[derive(Clone, Debug, PartialEq, Eq)]
929#[non_exhaustive]
930pub enum NotificationPowerLevelType {
931    /// `@room` notifications.
932    Room,
933}
934
935/// The actions to other users that can be limited by power levels.
936#[derive(Clone, Debug, PartialEq, Eq)]
937#[non_exhaustive]
938pub enum PowerLevelUserAction {
939    /// Ban a user.
940    Ban,
941
942    /// Unban a user.
943    Unban,
944
945    /// Invite a user.
946    Invite,
947
948    /// Kick a user.
949    Kick,
950
951    /// Change a user's power level.
952    ChangePowerLevel,
953}
954
955#[cfg(test)]
956mod tests {
957    use std::collections::BTreeMap;
958
959    use assign::assign;
960    use js_int::int;
961    use maplit::btreemap;
962    use ruma_common::{
963        canonical_json::assert_to_canonical_json_eq, owned_user_id,
964        room_version_rules::AuthorizationRules, user_id,
965    };
966    use serde_json::json;
967
968    use super::{
969        NotificationPowerLevels, RoomPowerLevels, RoomPowerLevelsEventContent,
970        RoomPowerLevelsSource, default_power_level,
971    };
972
973    #[test]
974    fn serialization_with_optional_fields_as_none() {
975        let default = default_power_level();
976
977        let power_levels = RoomPowerLevelsEventContent {
978            ban: default,
979            events: BTreeMap::new(),
980            events_default: int!(0),
981            invite: int!(0),
982            kick: default,
983            redact: default,
984            state_default: default,
985            users: BTreeMap::new(),
986            users_default: int!(0),
987            notifications: NotificationPowerLevels::default(),
988        };
989
990        assert_to_canonical_json_eq!(power_levels, json!({}));
991    }
992
993    #[test]
994    fn serialization_with_all_fields() {
995        let user = user_id!("@carl:example.com");
996        let power_levels_event = RoomPowerLevelsEventContent {
997            ban: int!(23),
998            events: btreemap! {
999                "m.dummy".into() => int!(23)
1000            },
1001            events_default: int!(23),
1002            invite: int!(23),
1003            kick: int!(23),
1004            redact: int!(23),
1005            state_default: int!(23),
1006            users: btreemap! {
1007                user.to_owned() => int!(23)
1008            },
1009            users_default: int!(23),
1010            notifications: assign!(NotificationPowerLevels::new(), { room: int!(23) }),
1011        };
1012
1013        assert_to_canonical_json_eq!(
1014            power_levels_event,
1015            json!({
1016                "ban": 23,
1017                "events": {
1018                    "m.dummy": 23,
1019                },
1020                "events_default": 23,
1021                "invite": 23,
1022                "kick": 23,
1023                "redact": 23,
1024                "state_default": 23,
1025                "users": {
1026                    "@carl:example.com": 23,
1027                },
1028                "users_default": 23,
1029                "notifications": {
1030                    "room": 23,
1031                },
1032            }),
1033        );
1034    }
1035
1036    #[test]
1037    fn cannot_change_power_level_of_privileged_creator() {
1038        let creator = user_id!("@lola:localhost");
1039
1040        let v1_power_levels = RoomPowerLevels::new(
1041            RoomPowerLevelsSource::None,
1042            &AuthorizationRules::V1,
1043            vec![creator.to_owned()],
1044        );
1045        assert!(v1_power_levels.user_can_change_user_power_level(creator, creator));
1046
1047        let v12_power_levels = RoomPowerLevels::new(
1048            RoomPowerLevelsSource::None,
1049            &AuthorizationRules::V12,
1050            vec![creator.to_owned()],
1051        );
1052        assert!(!v12_power_levels.user_can_change_user_power_level(creator, creator));
1053    }
1054
1055    #[test]
1056    fn cannot_convert_to_event_content_with_creator_in_users() {
1057        let creator = owned_user_id!("@lola:localhost");
1058
1059        let mut v1_power_levels = RoomPowerLevels::new(
1060            RoomPowerLevelsSource::None,
1061            &AuthorizationRules::V1,
1062            vec![creator.clone()],
1063        );
1064        v1_power_levels.users.insert(creator.clone(), int!(75));
1065        let v1_event_content = RoomPowerLevelsEventContent::try_from(v1_power_levels).unwrap();
1066        assert_eq!(*v1_event_content.users.get(&creator).unwrap(), int!(75));
1067
1068        let mut v12_power_levels = RoomPowerLevels::new(
1069            RoomPowerLevelsSource::None,
1070            &AuthorizationRules::V12,
1071            vec![creator.to_owned()],
1072        );
1073        v12_power_levels.users.insert(creator.clone(), int!(75));
1074        RoomPowerLevelsEventContent::try_from(v12_power_levels).unwrap_err();
1075    }
1076}