1use 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#[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
319#[derive(PartialEq, Copy, Clone, Eq, Debug)]
323#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
324pub enum UserPowerLevel {
325 Infinite,
328
329 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#[derive(Clone, Debug)]
405#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
406pub struct RoomPowerLevels {
407 pub ban: Int,
411
412 pub events: BTreeMap<TimelineEventType, Int>,
418
419 pub events_default: Int,
423
424 pub invite: Int,
428
429 pub kick: Int,
433
434 pub redact: Int,
438
439 pub state_default: Int,
443
444 pub users: BTreeMap<OwnedUserId, Int>,
458
459 pub users_default: Int,
463
464 pub notifications: NotificationPowerLevels,
470
471 pub rules: RoomPowerLevelsRules,
473}
474
475impl RoomPowerLevels {
476 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 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 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 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 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 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 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 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 pub fn user_can_ban(&self, user_id: &UserId) -> bool {
603 self.for_user(user_id) >= self.ban
604 }
605
606 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 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 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 pub fn user_can_invite(&self, user_id: &UserId) -> bool {
648 self.for_user(user_id) >= self.invite
649 }
650
651 pub fn user_can_kick(&self, user_id: &UserId) -> bool {
655 self.for_user(user_id) >= self.kick
656 }
657
658 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 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 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 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 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 pub fn user_can_trigger_room_notification(&self, user_id: &UserId) -> bool {
704 self.for_user(user_id) >= self.notifications.room
705 }
706
707 pub fn user_can_change_user_power_level(
712 &self,
713 acting_user_id: &UserId,
714 target_user_id: &UserId,
715 ) -> bool {
716 if !self.user_can_send_state(acting_user_id, StateEventType::RoomPowerLevels) {
718 return false;
719 }
720
721 if self.is_privileged_creator(target_user_id) {
723 return false;
724 }
725
726 if acting_user_id == target_user_id {
728 return true;
729 }
730
731 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 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 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 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#[derive(Copy, Clone, Debug, PartialEq, Eq, thiserror::Error)]
789#[non_exhaustive]
790pub enum PowerLevelsError {
791 #[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#[derive(Default)]
829#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
830pub enum RoomPowerLevelsSource {
831 Original(RoomPowerLevelsEventContent),
833 Redacted(RedactedRoomPowerLevelsEventContent),
835 #[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#[derive(Clone, Debug, PartialEq, Eq)]
868#[non_exhaustive]
869pub enum PowerLevelAction {
870 Ban,
872
873 Unban,
875
876 Invite,
878
879 Kick,
881
882 RedactOwn,
884
885 RedactOther,
887
888 SendMessage(MessageLikeEventType),
890
891 SendState(StateEventType),
893
894 TriggerNotification(NotificationPowerLevelType),
896}
897
898#[derive(Clone, Debug, PartialEq, Eq)]
900#[non_exhaustive]
901pub enum NotificationPowerLevelType {
902 Room,
904}
905
906#[derive(Clone, Debug, PartialEq, Eq)]
908#[non_exhaustive]
909pub enum PowerLevelUserAction {
910 Ban,
912
913 Unban,
915
916 Invite,
918
919 Kick,
921
922 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}