1use crate::room_state::member::{AuthorizedMember, MemberId};
2use crate::room_state::ChatRoomParametersV1;
3use crate::util::{sign_struct, verify_struct};
4use crate::ChatRoomStateV1;
5use ed25519_dalek::{Signature, SigningKey, VerifyingKey};
6use freenet_scaffold::util::{fast_hash, FastHash};
7use freenet_scaffold::ComposableState;
8use serde::{Deserialize, Serialize};
9use std::collections::{HashMap, HashSet};
10use std::fmt;
11use std::hash::{Hash, Hasher};
12use std::time::SystemTime;
13
14#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
20pub struct BansV1(pub Vec<AuthorizedUserBan>);
21
22#[derive(Debug, Clone, PartialEq)]
24pub enum BanValidationError {
25 MemberNotFound(MemberId),
27
28 BannerNotFound(MemberId),
30
31 NotInInviteChain(MemberId, MemberId),
33
34 SelfInvitationDetected(MemberId),
36
37 InviterNotFound(MemberId),
39
40 ExceededMaximumBans,
42}
43
44impl fmt::Display for BanValidationError {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 match self {
47 BanValidationError::MemberNotFound(id) => {
48 write!(f, "Banned member not found in member list: {:?}", id)
49 }
50 BanValidationError::BannerNotFound(id) => {
51 write!(f, "Banning member not found in member list: {:?}", id)
52 }
53 BanValidationError::NotInInviteChain(banner_id, banned_id) => write!(
54 f,
55 "Banner {:?} is not in the invite chain of banned member {:?}",
56 banner_id, banned_id
57 ),
58 BanValidationError::SelfInvitationDetected(id) => {
59 write!(f, "Self-invitation detected for member {:?}", id)
60 }
61 BanValidationError::InviterNotFound(id) => {
62 write!(f, "Inviting member not found for {:?}", id)
63 }
64 BanValidationError::ExceededMaximumBans => {
65 write!(f, "Exceeded maximum number of user bans")
66 }
67 }
68 }
69}
70
71impl BansV1 {
72 fn get_invalid_bans(
80 &self,
81 parent_state: &ChatRoomStateV1,
82 parameters: &ChatRoomParametersV1,
83 ) -> HashMap<BanId, BanValidationError> {
84 let member_map = parent_state.members.members_by_member_id();
85 let mut invalid_bans = HashMap::new();
86 let banned_user_ids: HashSet<MemberId> = self.0.iter().map(|b| b.ban.banned_user).collect();
87
88 for ban in &self.0 {
90 self.validate_single_ban(
91 ban,
92 &member_map,
93 parameters,
94 &mut invalid_bans,
95 &banned_user_ids,
96 );
97 }
98
99 self.identify_excess_bans(parent_state, &mut invalid_bans);
101
102 invalid_bans
103 }
104
105 fn validate_single_ban(
107 &self,
108 ban: &AuthorizedUserBan,
109 member_map: &HashMap<MemberId, &AuthorizedMember>,
110 parameters: &ChatRoomParametersV1,
111 invalid_bans: &mut HashMap<BanId, BanValidationError>,
112 banned_user_ids: &HashSet<MemberId>,
113 ) {
114 let banned_member = match member_map.get(&ban.ban.banned_user) {
120 Some(member) => member,
121 None => {
122 return;
124 }
125 };
126
127 if ban.banned_by != parameters.owner_id() {
129 let banning_member = match member_map.get(&ban.banned_by) {
131 Some(member) => member,
132 None => {
133 if banned_user_ids.contains(&ban.banned_by) {
136 invalid_bans
138 .insert(ban.id(), BanValidationError::BannerNotFound(ban.banned_by));
139 }
140 return;
142 }
143 };
144
145 if let Err(error) = self.validate_invite_chain(
147 banned_member,
148 banning_member,
149 member_map,
150 parameters.owner_id(),
151 ban.id(),
152 ) {
153 invalid_bans.insert(ban.id(), error);
154 }
155 }
156 }
157
158 fn validate_invite_chain(
160 &self,
161 banned_member: &AuthorizedMember,
162 banning_member: &AuthorizedMember,
163 member_map: &HashMap<MemberId, &AuthorizedMember>,
164 owner_id: MemberId,
165 _ban_id: BanId,
166 ) -> Result<(), BanValidationError> {
167 let mut current_member = banned_member;
168 let mut chain = Vec::new();
169
170 while current_member.member.id() != owner_id {
171 chain.push(current_member);
172
173 if current_member.member.id() == banning_member.member.id() {
175 return Ok(());
176 }
177
178 current_member = match member_map.get(¤t_member.member.invited_by) {
180 Some(m) => m,
181 None => {
182 return Err(BanValidationError::InviterNotFound(
183 current_member.member.id(),
184 ));
185 }
186 };
187
188 if chain.contains(¤t_member) {
190 return Err(BanValidationError::SelfInvitationDetected(
191 current_member.member.id(),
192 ));
193 }
194 }
195
196 Err(BanValidationError::NotInInviteChain(
198 banning_member.member.id(),
199 banned_member.member.id(),
200 ))
201 }
202
203 fn identify_excess_bans(
207 &self,
208 parent_state: &ChatRoomStateV1,
209 invalid_bans: &mut HashMap<BanId, BanValidationError>,
210 ) {
211 let max_bans = parent_state.configuration.configuration.max_user_bans;
212 let extra_bans = self.0.len() as isize - max_bans as isize;
213
214 if extra_bans > 0 {
215 let mut extra_bans_vec = self.0.clone();
218 extra_bans_vec.sort_by(|a, b| {
219 a.ban
222 .banned_at
223 .cmp(&b.ban.banned_at)
224 .then_with(|| a.id().cmp(&b.id()))
225 });
226 extra_bans_vec.reverse();
227
228 for ban in extra_bans_vec.iter().take(extra_bans as usize) {
229 invalid_bans.insert(ban.id(), BanValidationError::ExceededMaximumBans);
230 }
231 }
232 }
233}
234
235impl ComposableState for BansV1 {
236 type ParentState = ChatRoomStateV1;
237 type Summary = HashSet<BanId>;
238 type Delta = Vec<AuthorizedUserBan>;
239 type Parameters = ChatRoomParametersV1;
240
241 fn verify(
248 &self,
249 parent_state: &Self::ParentState,
250 parameters: &Self::Parameters,
251 ) -> Result<(), String> {
252 let invalid_bans = self.get_invalid_bans(parent_state, parameters);
253 if !invalid_bans.is_empty() {
254 let error_messages: Vec<String> = invalid_bans
255 .iter()
256 .map(|(id, error)| format!("{:?}: {}", id, error))
257 .collect();
258 return Err(format!("Invalid bans: {}", error_messages.join(", ")));
259 }
260
261 if self.0.len() > parent_state.configuration.configuration.max_user_bans {
263 return Err(format!(
264 "Number of bans ({}) exceeds the maximum allowed ({})",
265 self.0.len(),
266 parent_state.configuration.configuration.max_user_bans
267 ));
268 }
269
270 let members_by_id = parent_state.members.members_by_member_id();
271
272 let owner_vk = parameters.owner;
273 let owner_id = parameters.owner_id();
274
275 for ban in &self.0 {
277 if ban.banned_by == owner_id {
278 ban.verify_signature(&owner_vk)
279 .map_err(|e| format!("Invalid ban signature: {}", e))?;
280 } else if let Some(banning_member) = members_by_id.get(&ban.banned_by) {
281 ban.verify_signature(&banning_member.member.member_vk)
282 .map_err(|e| format!("Invalid ban signature: {}", e))?;
283 } else {
284 }
292 }
293
294 Ok(())
295 }
296
297 fn summarize(
301 &self,
302 _parent_state: &Self::ParentState,
303 _parameters: &Self::Parameters,
304 ) -> Self::Summary {
305 self.0.iter().map(|ban| ban.id()).collect()
306 }
307
308 fn delta(
313 &self,
314 _parent_state: &Self::ParentState,
315 _parameters: &Self::Parameters,
316 old_state_summary: &Self::Summary,
317 ) -> Option<Self::Delta> {
318 let delta = self
320 .0
321 .iter()
322 .filter(|ban| !old_state_summary.contains(&ban.id()))
323 .cloned()
324 .collect::<Vec<_>>();
325 if delta.is_empty() {
326 None
327 } else {
328 Some(delta)
329 }
330 }
331
332 fn apply_delta(
342 &mut self,
343 parent_state: &Self::ParentState,
344 parameters: &Self::Parameters,
345 delta: &Option<Self::Delta>,
346 ) -> Result<(), String> {
347 if let Some(delta) = delta {
348 let existing_ban_ids: std::collections::HashSet<_> =
350 self.0.iter().map(|ban| ban.id()).collect();
351 for new_ban in delta {
352 if existing_ban_ids.contains(&new_ban.id()) {
353 return Err(format!("Duplicate ban detected: {:?}", new_ban.id()));
354 }
355 }
356
357 let mut temp_bans = self.clone();
359 temp_bans.0.extend(delta.iter().cloned());
360
361 let max_bans = parent_state.configuration.configuration.max_user_bans;
363 if temp_bans.0.len() > max_bans {
364 temp_bans.0.sort_by(|a, b| {
367 a.ban
368 .banned_at
369 .cmp(&b.ban.banned_at)
370 .then_with(|| a.id().cmp(&b.id()))
371 });
372 let to_remove = temp_bans.0.len() - max_bans;
374 temp_bans.0.drain(0..to_remove);
375 }
376
377 if let Err(e) = temp_bans.verify(parent_state, parameters) {
379 return Err(format!("Invalid delta: {}", e));
380 }
381
382 self.0 = temp_bans.0;
384 }
385
386 self.0.sort_by(|a, b| {
388 a.ban
389 .banned_at
390 .cmp(&b.ban.banned_at)
391 .then_with(|| a.id().cmp(&b.id()))
392 });
393
394 Ok(())
395 }
396}
397
398#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
403pub struct AuthorizedUserBan {
404 pub ban: UserBan,
405 pub banned_by: MemberId,
406 pub signature: Signature,
407}
408
409impl Eq for AuthorizedUserBan {}
410
411impl Hash for AuthorizedUserBan {
412 fn hash<H: Hasher>(&self, state: &mut H) {
413 self.signature.to_bytes().hash(state);
414 }
415}
416
417impl AuthorizedUserBan {
418 pub fn new(ban: UserBan, banned_by: MemberId, banner_signing_key: &SigningKey) -> Self {
423 assert_eq!(
424 MemberId::from(banner_signing_key.verifying_key()),
425 banned_by
426 );
427
428 let signature = sign_struct(&ban, banner_signing_key);
429
430 Self {
431 ban,
432 banned_by,
433 signature,
434 }
435 }
436
437 pub fn with_signature(ban: UserBan, banned_by: MemberId, signature: Signature) -> Self {
440 Self {
441 ban,
442 banned_by,
443 signature,
444 }
445 }
446
447 pub fn verify_signature(&self, banner_verifying_key: &VerifyingKey) -> Result<(), String> {
451 verify_struct(&self.ban, &self.signature, banner_verifying_key)
452 .map_err(|e| format!("Invalid ban signature: {}", e))
453 }
454
455 pub fn id(&self) -> BanId {
457 BanId(fast_hash(&self.signature.to_bytes()))
458 }
459}
460
461#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
465pub struct UserBan {
466 pub owner_member_id: MemberId,
467 pub banned_at: SystemTime,
468 pub banned_user: MemberId,
469}
470
471#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Hash, Debug, Ord, PartialOrd)]
475pub struct BanId(pub FastHash);
476
477#[cfg(test)]
478mod tests {
479 use super::*;
480 use crate::room_state::configuration::AuthorizedConfigurationV1;
481 use crate::room_state::member::{AuthorizedMember, Member, MembersV1};
482 use ed25519_dalek::SigningKey;
483 use std::time::Duration;
484
485 fn create_test_chat_room_state() -> ChatRoomStateV1 {
486 ChatRoomStateV1 {
488 configuration: AuthorizedConfigurationV1::default(),
489 bans: Default::default(),
490 members: MembersV1::default(),
491 member_info: Default::default(),
492 secrets: Default::default(),
493 recent_messages: Default::default(),
494 upgrade: Default::default(),
495 ..Default::default()
496 }
497 }
498
499 fn create_test_parameters() -> ChatRoomParametersV1 {
500 let owner_key = SigningKey::generate(&mut rand::thread_rng());
502 ChatRoomParametersV1 {
503 owner: owner_key.verifying_key(),
504 }
505 }
506
507 #[test]
508 fn test_bans_verify() {
509 let mut state = create_test_chat_room_state();
510 let params = create_test_parameters();
511
512 let owner_key = SigningKey::generate(&mut rand::thread_rng());
514 let owner_id: MemberId = owner_key.verifying_key().into();
515 let member1_key = SigningKey::generate(&mut rand::thread_rng());
516 let member1_id: MemberId = member1_key.verifying_key().into();
517 let member2_key = SigningKey::generate(&mut rand::thread_rng());
518 let member2_id: MemberId = member2_key.verifying_key().into();
519
520 state.members.members.push(AuthorizedMember::new(
522 Member {
523 owner_member_id: owner_id,
524 invited_by: owner_id,
525 member_vk: owner_key.verifying_key(),
526 },
527 &owner_key,
528 ));
529 state.members.members.push(AuthorizedMember::new(
530 Member {
531 owner_member_id: owner_id,
532 invited_by: owner_id,
533 member_vk: member1_key.verifying_key(),
534 },
535 &owner_key,
536 ));
537 state.members.members.push(AuthorizedMember::new(
538 Member {
539 owner_member_id: owner_id,
540 invited_by: member1_id,
541 member_vk: member2_key.verifying_key(),
542 },
543 &member1_key,
544 ));
545
546 state.configuration.configuration.max_user_bans = 5;
548
549 let ban1 = AuthorizedUserBan::new(
551 UserBan {
552 owner_member_id: owner_id,
553 banned_at: SystemTime::now(),
554 banned_user: member1_id,
555 },
556 owner_id,
557 &owner_key,
558 );
559
560 let bans = BansV1(vec![ban1]);
561 assert!(
562 bans.verify(&state, ¶ms).is_ok(),
563 "Valid ban should be verified successfully: {:?}",
564 bans.verify(&state, ¶ms).err()
565 );
566
567 let mut many_bans = Vec::new();
569 for _ in 0..6 {
570 many_bans.push(AuthorizedUserBan::new(
571 UserBan {
572 owner_member_id: owner_id,
573 banned_at: SystemTime::now(),
574 banned_user: member1_id,
575 },
576 owner_id,
577 &owner_key,
578 ));
579 }
580 let too_many_bans = BansV1(many_bans);
581 assert!(
582 too_many_bans.verify(&state, ¶ms).is_err(),
583 "Exceeding max_user_bans should fail verification"
584 );
585
586 let pruned_key = SigningKey::generate(&mut rand::thread_rng());
590 let pruned_id: MemberId = pruned_key.verifying_key().into();
591 let pruned_ban = AuthorizedUserBan::new(
592 UserBan {
593 owner_member_id: owner_id,
594 banned_at: SystemTime::now(),
595 banned_user: member2_id,
596 },
597 pruned_id,
598 &pruned_key,
599 );
600
601 let pruned_bans = BansV1(vec![pruned_ban]);
602 assert!(
603 pruned_bans.verify(&state, ¶ms).is_ok(),
604 "Ban by pruned (non-banned) member should pass verification: {:?}",
605 pruned_bans.verify(&state, ¶ms).err()
606 );
607
608 let orphaned_key = SigningKey::generate(&mut rand::thread_rng());
610 let orphaned_id: MemberId = orphaned_key.verifying_key().into();
611 let orphaned_ban = AuthorizedUserBan::new(
612 UserBan {
613 owner_member_id: owner_id,
614 banned_at: SystemTime::now(),
615 banned_user: member2_id,
616 },
617 orphaned_id,
618 &orphaned_key,
619 );
620 let ban_of_orphaned = AuthorizedUserBan::new(
622 UserBan {
623 owner_member_id: owner_id,
624 banned_at: SystemTime::now(),
625 banned_user: orphaned_id,
626 },
627 owner_id,
628 &owner_key,
629 );
630 let orphaned_bans = BansV1(vec![orphaned_ban, ban_of_orphaned]);
631 assert!(
632 orphaned_bans.verify(&state, ¶ms).is_err(),
633 "Orphaned ban (banner was banned) should fail verification"
634 );
635
636 let ban_by_member = AuthorizedUserBan::new(
638 UserBan {
639 owner_member_id: owner_id,
640 banned_at: SystemTime::now(),
641 banned_user: member2_id,
642 },
643 member1_id,
644 &member1_key,
645 );
646
647 let member_bans = BansV1(vec![ban_by_member]);
648 assert!(
649 member_bans.verify(&state, ¶ms).is_ok(),
650 "Valid ban by non-owner member should pass verification"
651 );
652 }
653
654 #[test]
655 fn test_bans_summarize() {
656 let state = create_test_chat_room_state();
657 let params = create_test_parameters();
658
659 let key = SigningKey::generate(&mut rand::thread_rng());
660 let id: MemberId = key.verifying_key().into();
661
662 let ban1 = AuthorizedUserBan::new(
663 UserBan {
664 owner_member_id: id,
665 banned_at: SystemTime::now(),
666 banned_user: id,
667 },
668 id,
669 &key,
670 );
671
672 let ban2 = AuthorizedUserBan::new(
673 UserBan {
674 owner_member_id: id,
675 banned_at: SystemTime::now() + Duration::from_secs(1),
676 banned_user: id,
677 },
678 id,
679 &key,
680 );
681
682 let bans = BansV1(vec![ban1.clone(), ban2.clone()]);
683 let summary = bans.summarize(&state, ¶ms);
684
685 assert_eq!(summary.len(), 2);
686 assert!(summary.contains(&ban1.id()));
687 assert!(summary.contains(&ban2.id()));
688 }
689
690 #[test]
691 fn test_bans_delta() {
692 let state = create_test_chat_room_state();
693 let params = create_test_parameters();
694
695 let key = SigningKey::generate(&mut rand::thread_rng());
696 let id: MemberId = key.verifying_key().into();
697
698 let ban1 = AuthorizedUserBan::new(
699 UserBan {
700 owner_member_id: id,
701 banned_at: SystemTime::now(),
702 banned_user: id,
703 },
704 id,
705 &key,
706 );
707
708 let ban2 = AuthorizedUserBan::new(
709 UserBan {
710 owner_member_id: id,
711 banned_at: SystemTime::now() + Duration::from_secs(1),
712 banned_user: id,
713 },
714 id,
715 &key,
716 );
717
718 let bans = BansV1(vec![ban1.clone(), ban2.clone()]);
719
720 let empty_summary = HashSet::new();
722 let delta = bans.delta(&state, ¶ms, &empty_summary);
723 assert_eq!(delta, Some(vec![ban1.clone(), ban2.clone()]));
724
725 let partial_summary: HashSet<BanId> = vec![ban1.id()].into_iter().collect();
727 let delta = bans.delta(&state, ¶ms, &partial_summary);
728 assert_eq!(delta, Some(vec![ban2.clone()]));
729
730 let full_summary: HashSet<BanId> = vec![ban1.id(), ban2.id()].into_iter().collect();
732 let delta = bans.delta(&state, ¶ms, &full_summary);
733 assert_eq!(delta, None);
734 }
735
736 #[test]
737 fn test_bans_apply_delta() {
738 let mut state = create_test_chat_room_state();
739 let params = create_test_parameters();
740
741 let owner_key = SigningKey::generate(&mut rand::thread_rng());
742 let owner_id: MemberId = owner_key.verifying_key().into();
743 let member_key = SigningKey::generate(&mut rand::thread_rng());
744 let member_id: MemberId = member_key.verifying_key().into();
745
746 state.members.members.push(AuthorizedMember::new(
748 Member {
749 owner_member_id: owner_id,
750 invited_by: owner_id,
751 member_vk: owner_key.verifying_key(),
752 },
753 &owner_key,
754 ));
755 state.members.members.push(AuthorizedMember::new(
756 Member {
757 owner_member_id: owner_id,
758 invited_by: owner_id,
759 member_vk: member_key.verifying_key(),
760 },
761 &owner_key,
762 ));
763
764 state.configuration.configuration.max_user_bans = 5;
766
767 let mut bans = BansV1::default();
768
769 let new_ban = AuthorizedUserBan::new(
770 UserBan {
771 owner_member_id: owner_id,
772 banned_at: SystemTime::now(),
773 banned_user: member_id,
774 },
775 owner_id,
776 &owner_key,
777 );
778
779 let delta = vec![new_ban.clone()];
781 assert!(
782 bans.apply_delta(&state, ¶ms, &Some(delta.clone()))
783 .is_ok(),
784 "Valid delta should be applied successfully: {:?}",
785 bans.apply_delta(&state, ¶ms, &Some(delta)).err()
786 );
787 assert_eq!(
788 bans.0.len(),
789 1,
790 "Bans should contain one ban after applying delta"
791 );
792 assert_eq!(bans.0[0], new_ban, "Applied ban should match the new ban");
793
794 let mut many_bans = Vec::new();
796 for i in 0..5 {
797 many_bans.push(AuthorizedUserBan::new(
798 UserBan {
799 owner_member_id: owner_id,
800 banned_at: SystemTime::now() + Duration::from_secs(i as u64 + 10),
802 banned_user: member_id,
803 },
804 owner_id,
805 &owner_key,
806 ));
807 }
808 let delta_exceeding_max = Some(many_bans.clone());
809 assert!(
810 bans.apply_delta(&state, ¶ms, &delta_exceeding_max)
811 .is_ok(),
812 "Delta exceeding max_user_bans should succeed by removing oldest: {:?}",
813 bans.apply_delta(&state, ¶ms, &delta_exceeding_max)
814 .err()
815 );
816 assert_eq!(
817 bans.0.len(),
818 5,
819 "Bans should be at max_user_bans limit after removing oldest"
820 );
821 assert!(
823 !bans.0.contains(&new_ban),
824 "Oldest ban should have been removed"
825 );
826
827 let existing_ban = many_bans.last().unwrap().clone();
829 let invalid_delta = Some(vec![existing_ban]);
830 assert!(
831 bans.apply_delta(&state, ¶ms, &invalid_delta).is_err(),
832 "Applying duplicate ban should fail: {:?}",
833 bans.apply_delta(&state, ¶ms, &invalid_delta).ok()
834 );
835 assert_eq!(
836 bans.0.len(),
837 5,
838 "State should not change after applying duplicate ban"
839 );
840
841 let mut additional_bans = Vec::new();
843 for i in 0..2 {
844 additional_bans.push(AuthorizedUserBan::new(
845 UserBan {
846 owner_member_id: owner_id,
847 banned_at: SystemTime::now() + Duration::from_secs(i as u64 + 100),
848 banned_user: member_id,
849 },
850 owner_id,
851 &owner_key,
852 ));
853 }
854 assert!(
855 bans.apply_delta(&state, ¶ms, &Some(additional_bans))
856 .is_ok(),
857 "Applying more bans should succeed by evicting oldest: {:?}",
858 bans.apply_delta(&state, ¶ms, &Some(Vec::new())).err()
859 );
860 assert_eq!(
861 bans.0.len(),
862 5,
863 "State should still have max number of bans after evicting oldest"
864 );
865 }
866
867 #[test]
868 fn test_authorized_user_ban() {
869 let owner_key = SigningKey::generate(&mut rand::thread_rng());
870 let owner_id: MemberId = owner_key.verifying_key().into();
871 let member_key = SigningKey::generate(&mut rand::thread_rng());
872 let member_id: MemberId = member_key.verifying_key().into();
873
874 let ban = UserBan {
875 owner_member_id: owner_id,
876 banned_at: SystemTime::now(),
877 banned_user: member_id,
878 };
879
880 let authorized_ban = AuthorizedUserBan::new(ban.clone(), owner_id, &owner_key);
881
882 assert!(authorized_ban
884 .verify_signature(&owner_key.verifying_key())
885 .is_ok());
886
887 let wrong_key = SigningKey::generate(&mut rand::thread_rng());
889 assert!(authorized_ban
890 .verify_signature(&wrong_key.verifying_key())
891 .is_err());
892
893 let id1 = authorized_ban.id();
895 let id2 = authorized_ban.id();
896 assert_eq!(id1, id2);
897
898 let another_ban = AuthorizedUserBan::new(
900 UserBan {
901 owner_member_id: owner_id,
902 banned_at: SystemTime::now() + Duration::from_secs(1),
903 banned_user: member_id,
904 },
905 owner_id,
906 &owner_key,
907 );
908 assert_ne!(authorized_ban.id(), another_ban.id());
909 }
910}