1use 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#[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[serde(default, skip_serializing_if = "NotificationPowerLevels::is_default")]
112 pub notifications: NotificationPowerLevels,
113}
114
115impl RoomPowerLevelsEventContent {
116 pub fn new(rules: &AuthorizationRules) -> Self {
119 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 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#[allow(clippy::trivially_copy_pass_by_ref)]
179fn is_default_power_level(l: &Int) -> bool {
180 *l == int!(50)
181}
182
183impl RoomPowerLevelsEvent {
184 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 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 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#[derive(Clone, Debug, Deserialize, Serialize)]
224#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
225pub struct RedactedRoomPowerLevelsEventContent {
226 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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#[derive(PartialEq, Copy, Clone, Eq, Debug)]
352#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
353pub enum UserPowerLevel {
354 Infinite,
357
358 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#[derive(Clone, Debug)]
434#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
435pub struct RoomPowerLevels {
436 pub ban: Int,
440
441 pub events: BTreeMap<TimelineEventType, Int>,
447
448 pub events_default: Int,
452
453 pub invite: Int,
457
458 pub kick: Int,
462
463 pub redact: Int,
467
468 pub state_default: Int,
472
473 pub users: BTreeMap<OwnedUserId, Int>,
487
488 pub users_default: Int,
492
493 pub notifications: NotificationPowerLevels,
499
500 pub rules: RoomPowerLevelsRules,
502}
503
504impl RoomPowerLevels {
505 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 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 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 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 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 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 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 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 pub fn user_can_ban(&self, user_id: &UserId) -> bool {
632 self.for_user(user_id) >= self.ban
633 }
634
635 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 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 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 pub fn user_can_invite(&self, user_id: &UserId) -> bool {
677 self.for_user(user_id) >= self.invite
678 }
679
680 pub fn user_can_kick(&self, user_id: &UserId) -> bool {
684 self.for_user(user_id) >= self.kick
685 }
686
687 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 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 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 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 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 pub fn user_can_trigger_room_notification(&self, user_id: &UserId) -> bool {
733 self.for_user(user_id) >= self.notifications.room
734 }
735
736 pub fn user_can_change_user_power_level(
741 &self,
742 acting_user_id: &UserId,
743 target_user_id: &UserId,
744 ) -> bool {
745 if !self.user_can_send_state(acting_user_id, StateEventType::RoomPowerLevels) {
747 return false;
748 }
749
750 if self.is_privileged_creator(target_user_id) {
752 return false;
753 }
754
755 if acting_user_id == target_user_id {
757 return true;
758 }
759
760 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 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 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 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#[derive(Copy, Clone, Debug, PartialEq, Eq, thiserror::Error)]
818#[non_exhaustive]
819pub enum PowerLevelsError {
820 #[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#[derive(Default)]
858#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
859pub enum RoomPowerLevelsSource {
860 Original(RoomPowerLevelsEventContent),
862 Redacted(RedactedRoomPowerLevelsEventContent),
864 #[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#[derive(Clone, Debug, PartialEq, Eq)]
897#[non_exhaustive]
898pub enum PowerLevelAction {
899 Ban,
901
902 Unban,
904
905 Invite,
907
908 Kick,
910
911 RedactOwn,
913
914 RedactOther,
916
917 SendMessage(MessageLikeEventType),
919
920 SendState(StateEventType),
922
923 TriggerNotification(NotificationPowerLevelType),
925}
926
927#[derive(Clone, Debug, PartialEq, Eq)]
929#[non_exhaustive]
930pub enum NotificationPowerLevelType {
931 Room,
933}
934
935#[derive(Clone, Debug, PartialEq, Eq)]
937#[non_exhaustive]
938pub enum PowerLevelUserAction {
939 Ban,
941
942 Unban,
944
945 Invite,
947
948 Kick,
950
951 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}