1use std::{collections::HashMap, ops::Deref};
16
17use matrix_sdk_common::BoxFuture;
18use ruma::{
19 events::{
20 room::member::{MembershipState, SyncRoomMemberEvent},
21 SyncStateEvent,
22 },
23 OwnedUserId, UserId,
24};
25
26use super::UserIdentity;
27use crate::store::IdentityUpdates;
28
29pub trait RoomIdentityProvider: core::fmt::Debug {
35 fn is_member<'a>(&'a self, user_id: &'a UserId) -> BoxFuture<'a, bool>;
37
38 fn member_identities(&self) -> BoxFuture<'_, Vec<UserIdentity>>;
40
41 fn user_identity<'a>(&'a self, user_id: &'a UserId) -> BoxFuture<'a, Option<UserIdentity>>;
45
46 fn state_of(&self, user_identity: &UserIdentity) -> IdentityState {
49 if user_identity.is_verified() {
50 IdentityState::Verified
51 } else if user_identity.has_verification_violation() {
52 IdentityState::VerificationViolation
53 } else if let UserIdentity::Other(u) = user_identity {
54 if u.identity_needs_user_approval() {
55 IdentityState::PinViolation
56 } else {
57 IdentityState::Pinned
58 }
59 } else {
60 IdentityState::Pinned
61 }
62 }
63}
64
65#[derive(Debug)]
73pub struct RoomIdentityState<R: RoomIdentityProvider> {
74 room: R,
75 known_states: KnownStates,
76}
77
78impl<R: RoomIdentityProvider> RoomIdentityState<R> {
79 pub async fn new(room: R) -> Self {
82 let known_states = KnownStates::from_identities(room.member_identities().await, &room);
83 Self { room, known_states }
84 }
85
86 pub fn current_state(&self) -> Vec<IdentityStatusChange> {
89 self.known_states
90 .known_states
91 .iter()
92 .map(|(user_id, state)| IdentityStatusChange {
93 user_id: user_id.clone(),
94 changed_to: state.clone(),
95 })
96 .collect()
97 }
98
99 pub async fn process_change(&mut self, item: RoomIdentityChange) -> Vec<IdentityStatusChange> {
105 match item {
106 RoomIdentityChange::IdentityUpdates(identity_updates) => {
107 self.process_identity_changes(identity_updates).await
108 }
109 RoomIdentityChange::SyncRoomMemberEvent(sync_room_member_event) => {
110 self.process_membership_change(sync_room_member_event).await
111 }
112 }
113 }
114
115 async fn process_identity_changes(
116 &mut self,
117 identity_updates: IdentityUpdates,
118 ) -> Vec<IdentityStatusChange> {
119 let mut ret = vec![];
120
121 for user_identity in identity_updates.new.values().chain(identity_updates.changed.values())
122 {
123 let user_id = user_identity.user_id();
124 if self.room.is_member(user_id).await {
125 let update = self.update_user_state(user_id, user_identity);
126 if let Some(identity_status_change) = update {
127 ret.push(identity_status_change);
128 }
129 }
130 }
131
132 ret
133 }
134
135 async fn process_membership_change(
136 &mut self,
137 sync_room_member_event: Box<SyncRoomMemberEvent>,
138 ) -> Vec<IdentityStatusChange> {
139 if let SyncStateEvent::Original(event) = sync_room_member_event.deref() {
142 let user_id: Result<&UserId, _> = event.state_key.as_str().try_into();
144 if let Ok(user_id) = user_id {
145 if let Some(user_identity @ UserIdentity::Other(_)) =
147 self.room.user_identity(user_id).await
148 {
149 if matches!(
151 self.room.state_of(&user_identity),
152 IdentityState::Verified | IdentityState::Pinned
153 ) {
154 return vec![];
155 }
156
157 match event.content.membership {
158 MembershipState::Join | MembershipState::Invite => {
159 if let Some(update) = self.update_user_state(user_id, &user_identity) {
162 return vec![update];
163 }
164 }
165 MembershipState::Leave | MembershipState::Ban => {
166 if let Some(update) =
171 self.update_user_state_to(user_id, IdentityState::Pinned)
172 {
173 return vec![update];
174 }
175 }
176 MembershipState::Knock => {
177 }
179 _ => {}
180 }
181 }
182 }
183 }
184
185 vec![]
187 }
188
189 fn update_user_state(
190 &mut self,
191 user_id: &UserId,
192 user_identity: &UserIdentity,
193 ) -> Option<IdentityStatusChange> {
194 if let UserIdentity::Other(_) = &user_identity {
195 self.update_user_state_to(user_id, self.room.state_of(user_identity))
196 } else {
197 None
199 }
200 }
201
202 fn update_user_state_to(
206 &mut self,
207 user_id: &UserId,
208 new_state: IdentityState,
209 ) -> Option<IdentityStatusChange> {
210 let old_state = self.known_states.get(user_id);
211
212 if old_state == new_state {
213 return None;
214 }
215
216 Some(self.set_state(user_id, new_state))
217 }
218
219 fn set_state(&mut self, user_id: &UserId, new_state: IdentityState) -> IdentityStatusChange {
220 self.known_states.set(user_id, &new_state);
222
223 IdentityStatusChange { user_id: user_id.to_owned(), changed_to: new_state }
225 }
226}
227
228#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
240pub struct IdentityStatusChange {
241 pub user_id: OwnedUserId,
243
244 pub changed_to: IdentityState,
246}
247
248#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
250#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
251pub enum IdentityState {
252 Verified,
254
255 Pinned,
259
260 PinViolation,
266
267 VerificationViolation,
272}
273
274#[derive(Debug)]
278pub enum RoomIdentityChange {
279 IdentityUpdates(IdentityUpdates),
281
282 SyncRoomMemberEvent(Box<SyncRoomMemberEvent>),
285}
286
287#[derive(Debug)]
290struct KnownStates {
291 known_states: HashMap<OwnedUserId, IdentityState>,
292}
293
294impl KnownStates {
295 fn from_identities(
296 member_identities: impl IntoIterator<Item = UserIdentity>,
297 room: &dyn RoomIdentityProvider,
298 ) -> Self {
299 let mut known_states = HashMap::new();
300 for user_identity in member_identities {
301 let state = room.state_of(&user_identity);
302 if state != IdentityState::Pinned {
303 known_states.insert(user_identity.user_id().to_owned(), state);
304 }
305 }
306 Self { known_states }
307 }
308
309 fn get(&self, user_id: &UserId) -> IdentityState {
312 self.known_states.get(user_id).cloned().unwrap_or(IdentityState::Pinned)
313 }
314
315 fn set(&mut self, user_id: &UserId, identity_state: &IdentityState) {
318 if let IdentityState::Pinned = identity_state {
319 self.known_states.remove(user_id);
320 } else {
321 self.known_states.insert(user_id.to_owned(), identity_state.clone());
322 }
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use std::{
329 collections::HashMap,
330 sync::{Arc, Mutex},
331 };
332
333 use matrix_sdk_common::BoxFuture;
334 use matrix_sdk_test::async_test;
335 use ruma::{
336 device_id,
337 events::{
338 room::member::{
339 MembershipState, RoomMemberEventContent, RoomMemberUnsigned, SyncRoomMemberEvent,
340 },
341 OriginalSyncStateEvent,
342 },
343 owned_event_id, owned_user_id, user_id, MilliSecondsSinceUnixEpoch, OwnedUserId, UInt,
344 UserId,
345 };
346
347 use super::{IdentityState, RoomIdentityChange, RoomIdentityProvider, RoomIdentityState};
348 use crate::{
349 identities::user::testing::own_identity_wrapped,
350 store::{IdentityUpdates, Store},
351 IdentityStatusChange, OtherUserIdentity, OtherUserIdentityData, OwnUserIdentityData,
352 UserIdentity,
353 };
354
355 #[async_test]
356 async fn test_unpinning_a_pinned_identity_in_the_room_notifies() {
357 let user_id = user_id!("@u:s.co");
359 let mut room = FakeRoom::new();
360 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
361 let mut state = RoomIdentityState::new(room.clone()).await;
362
363 let updates =
365 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false).await;
366 let update = state.process_change(updates).await;
367
368 assert_eq!(
370 update,
371 vec![IdentityStatusChange {
372 user_id: user_id.to_owned(),
373 changed_to: IdentityState::PinViolation
374 }]
375 );
376 }
377
378 #[async_test]
379 async fn test_verifying_a_pinned_identity_in_the_room_notifies() {
380 let user_id = user_id!("@u:s.co");
382 let mut room = FakeRoom::new();
383 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
384 let mut state = RoomIdentityState::new(room.clone()).await;
385
386 let updates =
388 identity_change(&mut room, user_id, IdentityState::Verified, false, false).await;
389 let update = state.process_change(updates).await;
390
391 assert_eq!(
393 update,
394 vec![IdentityStatusChange {
395 user_id: user_id.to_owned(),
396 changed_to: IdentityState::Verified
397 }]
398 );
399 }
400
401 #[async_test]
402 async fn test_pinning_an_unpinned_identity_in_the_room_notifies() {
403 let user_id = user_id!("@u:s.co");
405 let mut room = FakeRoom::new();
406 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
407 let mut state = RoomIdentityState::new(room.clone()).await;
408
409 let updates =
411 identity_change(&mut room, user_id, IdentityState::Pinned, false, false).await;
412 let update = state.process_change(updates).await;
413
414 assert_eq!(
416 update,
417 vec![IdentityStatusChange {
418 user_id: user_id.to_owned(),
419 changed_to: IdentityState::Pinned
420 }]
421 );
422 }
423
424 #[async_test]
425 async fn test_unpinned_identity_becoming_verification_violating_in_the_room_notifies() {
426 let user_id = user_id!("@u:s.co");
428 let mut room = FakeRoom::new();
429 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
430 let mut state = RoomIdentityState::new(room.clone()).await;
431
432 let updates =
434 identity_change(&mut room, user_id, IdentityState::VerificationViolation, false, false)
435 .await;
436 let update = state.process_change(updates).await;
437
438 assert_eq!(
440 update,
441 vec![IdentityStatusChange {
442 user_id: user_id.to_owned(),
443 changed_to: IdentityState::VerificationViolation
444 }]
445 );
446 }
447
448 #[async_test]
449 async fn test_unpinning_an_identity_not_in_the_room_does_nothing() {
450 let user_id = user_id!("@u:s.co");
452 let mut room = FakeRoom::new();
453 let mut state = RoomIdentityState::new(room.clone()).await;
454
455 let updates =
457 identity_change(&mut room, user_id, IdentityState::PinViolation, true, false).await;
458 let update = state.process_change(updates).await;
459
460 assert_eq!(update, vec![]);
462 }
463
464 #[async_test]
465 async fn test_pinning_an_identity_not_in_the_room_does_nothing() {
466 let user_id = user_id!("@u:s.co");
468 let mut room = FakeRoom::new();
469 let mut state = RoomIdentityState::new(room.clone()).await;
470
471 let updates = identity_change(&mut room, user_id, IdentityState::Pinned, true, false).await;
473 let update = state.process_change(updates).await;
474
475 assert_eq!(update, []);
477 }
478
479 #[async_test]
480 async fn test_pinning_an_already_pinned_identity_in_the_room_does_nothing() {
481 let user_id = user_id!("@u:s.co");
483 let mut room = FakeRoom::new();
484 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
485 let mut state = RoomIdentityState::new(room.clone()).await;
486
487 let updates =
489 identity_change(&mut room, user_id, IdentityState::Pinned, false, false).await;
490 let update = state.process_change(updates).await;
491
492 assert_eq!(update, []);
494 }
495
496 #[async_test]
497 async fn test_unpinning_an_already_unpinned_identity_in_the_room_does_nothing() {
498 let user_id = user_id!("@u:s.co");
500 let mut room = FakeRoom::new();
501 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
502 let mut state = RoomIdentityState::new(room.clone()).await;
503
504 let updates =
506 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false).await;
507 let update = state.process_change(updates).await;
508
509 assert_eq!(update, []);
511 }
512
513 #[async_test]
514 async fn test_a_pinned_identity_joining_the_room_does_nothing() {
515 let user_id = user_id!("@u:s.co");
517 let mut room = FakeRoom::new();
518 room.non_member(other_user_identity(user_id).await, IdentityState::Pinned);
519 let mut state = RoomIdentityState::new(room.clone()).await;
520
521 let updates = room_change(user_id, MembershipState::Join);
523 let update = state.process_change(updates).await;
524
525 assert_eq!(update, []);
527 }
528
529 #[async_test]
530 async fn test_a_verified_identity_joining_the_room_does_nothing() {
531 let user_id = user_id!("@u:s.co");
533 let mut room = FakeRoom::new();
534 room.non_member(other_user_identity(user_id).await, IdentityState::Verified);
535 let mut state = RoomIdentityState::new(room).await;
536
537 let updates = room_change(user_id, MembershipState::Join);
539 let update = state.process_change(updates).await;
540
541 assert_eq!(update, []);
543 }
544
545 #[async_test]
546 async fn test_an_unpinned_identity_joining_the_room_notifies() {
547 let user_id = user_id!("@u:s.co");
549 let mut room = FakeRoom::new();
550 room.non_member(other_user_identity(user_id).await, IdentityState::PinViolation);
551 let mut state = RoomIdentityState::new(room.clone()).await;
552
553 let updates = room_change(user_id, MembershipState::Join);
555 let update = state.process_change(updates).await;
556
557 assert_eq!(
559 update,
560 vec![IdentityStatusChange {
561 user_id: user_id.to_owned(),
562 changed_to: IdentityState::PinViolation
563 }]
564 );
565 }
566
567 #[async_test]
568 async fn test_a_pinned_identity_invited_to_the_room_does_nothing() {
569 let user_id = user_id!("@u:s.co");
571 let mut room = FakeRoom::new();
572 room.non_member(other_user_identity(user_id).await, IdentityState::Pinned);
573 let mut state = RoomIdentityState::new(room.clone()).await;
574
575 let updates = room_change(user_id, MembershipState::Invite);
577 let update = state.process_change(updates).await;
578
579 assert_eq!(update, []);
581 }
582
583 #[async_test]
584 async fn test_an_unpinned_identity_invited_to_the_room_notifies() {
585 let user_id = user_id!("@u:s.co");
587 let mut room = FakeRoom::new();
588 room.non_member(other_user_identity(user_id).await, IdentityState::PinViolation);
589 let mut state = RoomIdentityState::new(room.clone()).await;
590
591 let updates = room_change(user_id, MembershipState::Invite);
593 let update = state.process_change(updates).await;
594
595 assert_eq!(
597 update,
598 vec![IdentityStatusChange {
599 user_id: user_id.to_owned(),
600 changed_to: IdentityState::PinViolation
601 }]
602 );
603 }
604
605 #[async_test]
606 async fn test_a_verification_violating_identity_invited_to_the_room_notifies() {
607 let user_id = user_id!("@u:s.co");
609 let mut room = FakeRoom::new();
610 room.non_member(other_user_identity(user_id).await, IdentityState::VerificationViolation);
611 let mut state = RoomIdentityState::new(room).await;
612
613 let updates = room_change(user_id, MembershipState::Invite);
615 let update = state.process_change(updates).await;
616
617 assert_eq!(
619 update,
620 vec![IdentityStatusChange {
621 user_id: user_id.to_owned(),
622 changed_to: IdentityState::VerificationViolation
623 }]
624 );
625 }
626
627 #[async_test]
628 async fn test_own_identity_becoming_unpinned_is_ignored() {
629 let user_id = user_id!("@u:s.co");
631 let mut room = FakeRoom::new();
632 room.member(own_user_identity(user_id).await, IdentityState::Pinned);
633 let mut state = RoomIdentityState::new(room.clone()).await;
634
635 let updates =
637 identity_change(&mut room, user_id, IdentityState::PinViolation, false, true).await;
638 let update = state.process_change(updates).await;
639
640 assert_eq!(update, vec![]);
642 }
643
644 #[async_test]
645 async fn test_own_identity_becoming_pinned_is_ignored() {
646 let user_id = user_id!("@u:s.co");
648 let mut room = FakeRoom::new();
649 room.member(own_user_identity(user_id).await, IdentityState::PinViolation);
650 let mut state = RoomIdentityState::new(room.clone()).await;
651
652 let updates = identity_change(&mut room, user_id, IdentityState::Pinned, false, true).await;
654 let update = state.process_change(updates).await;
655
656 assert_eq!(update, vec![]);
658 }
659
660 #[async_test]
661 async fn test_own_pinned_identity_joining_room_is_ignored() {
662 let user_id = user_id!("@u:s.co");
664 let mut room = FakeRoom::new();
665 room.non_member(own_user_identity(user_id).await, IdentityState::Pinned);
666 let mut state = RoomIdentityState::new(room.clone()).await;
667
668 let updates = room_change(user_id, MembershipState::Join);
670 let update = state.process_change(updates).await;
671
672 assert_eq!(update, []);
674 }
675
676 #[async_test]
677 async fn test_own_unpinned_identity_joining_room_is_ignored() {
678 let user_id = user_id!("@u:s.co");
680 let mut room = FakeRoom::new();
681 room.non_member(own_user_identity(user_id).await, IdentityState::PinViolation);
682 let mut state = RoomIdentityState::new(room.clone()).await;
683
684 let updates = room_change(user_id, MembershipState::Join);
686 let update = state.process_change(updates).await;
687
688 assert_eq!(update, vec![]);
690 }
691
692 #[async_test]
693 async fn test_a_pinned_identity_leaving_the_room_does_nothing() {
694 let user_id = user_id!("@u:s.co");
696 let mut room = FakeRoom::new();
697 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
698 let mut state = RoomIdentityState::new(room.clone()).await;
699
700 let updates = room_change(user_id, MembershipState::Leave);
702 let update = state.process_change(updates).await;
703
704 assert_eq!(update, []);
706 }
707
708 #[async_test]
709 async fn test_a_verified_identity_leaving_the_room_does_nothing() {
710 let user_id = user_id!("@u:s.co");
712 let mut room = FakeRoom::new();
713 room.member(other_user_identity(user_id).await, IdentityState::Verified);
714 let mut state = RoomIdentityState::new(room).await;
715
716 let updates = room_change(user_id, MembershipState::Leave);
718 let update = state.process_change(updates).await;
719
720 assert_eq!(update, []);
722 }
723
724 #[async_test]
725 async fn test_an_unpinned_identity_leaving_the_room_notifies() {
726 let user_id = user_id!("@u:s.co");
728 let mut room = FakeRoom::new();
729 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
730 let mut state = RoomIdentityState::new(room.clone()).await;
731
732 let updates = room_change(user_id, MembershipState::Leave);
734 let update = state.process_change(updates).await;
735
736 assert_eq!(
738 update,
739 vec![IdentityStatusChange {
740 user_id: user_id.to_owned(),
741 changed_to: IdentityState::Pinned
742 }]
743 );
744 }
745
746 #[async_test]
747 async fn test_a_verification_violating_identity_leaving_the_room_notifies() {
748 let user_id = user_id!("@u:s.co");
750 let mut room = FakeRoom::new();
751 room.member(other_user_identity(user_id).await, IdentityState::VerificationViolation);
752 let mut state = RoomIdentityState::new(room).await;
753
754 let updates = room_change(user_id, MembershipState::Leave);
756 let update = state.process_change(updates).await;
757
758 assert_eq!(
760 update,
761 vec![IdentityStatusChange {
762 user_id: user_id.to_owned(),
763 changed_to: IdentityState::Pinned
764 }]
765 );
766 }
767
768 #[async_test]
769 async fn test_a_pinned_identity_being_banned_does_nothing() {
770 let user_id = user_id!("@u:s.co");
772 let mut room = FakeRoom::new();
773 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
774 let mut state = RoomIdentityState::new(room.clone()).await;
775
776 let updates = room_change(user_id, MembershipState::Ban);
778 let update = state.process_change(updates).await;
779
780 assert_eq!(update, []);
782 }
783
784 #[async_test]
785 async fn test_an_unpinned_identity_being_banned_notifies() {
786 let user_id = user_id!("@u:s.co");
788 let mut room = FakeRoom::new();
789 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
790 let mut state = RoomIdentityState::new(room.clone()).await;
791
792 let updates = room_change(user_id, MembershipState::Ban);
794 let update = state.process_change(updates).await;
795
796 assert_eq!(
798 update,
799 vec![IdentityStatusChange {
800 user_id: user_id.to_owned(),
801 changed_to: IdentityState::Pinned
802 }]
803 );
804 }
805
806 #[async_test]
807 async fn test_multiple_simultaneous_identity_updates_are_all_notified() {
808 let user1 = user_id!("@u1:s.co");
810 let user2 = user_id!("@u2:s.co");
811 let user3 = user_id!("@u3:s.co");
812 let mut room = FakeRoom::new();
813 room.member(other_user_identity(user1).await, IdentityState::Pinned);
814 room.member(other_user_identity(user2).await, IdentityState::PinViolation);
815 room.member(other_user_identity(user3).await, IdentityState::Pinned);
816 let mut state = RoomIdentityState::new(room.clone()).await;
817
818 let updates = identity_changes(
820 &mut room,
821 &[
822 IdentityChangeSpec {
823 user_id: user1.to_owned(),
824 changed_to: IdentityState::PinViolation,
825 new: false,
826 own: false,
827 },
828 IdentityChangeSpec {
829 user_id: user2.to_owned(),
830 changed_to: IdentityState::Pinned,
831 new: false,
832 own: false,
833 },
834 IdentityChangeSpec {
835 user_id: user3.to_owned(),
836 changed_to: IdentityState::PinViolation,
837 new: false,
838 own: false,
839 },
840 ],
841 )
842 .await;
843 let update = state.process_change(updates).await;
844
845 assert_eq!(
847 update,
848 vec![
849 IdentityStatusChange {
850 user_id: user1.to_owned(),
851 changed_to: IdentityState::PinViolation
852 },
853 IdentityStatusChange {
854 user_id: user2.to_owned(),
855 changed_to: IdentityState::Pinned
856 },
857 IdentityStatusChange {
858 user_id: user3.to_owned(),
859 changed_to: IdentityState::PinViolation
860 }
861 ]
862 );
863 }
864
865 #[async_test]
866 async fn test_multiple_changes_are_notified() {
867 let user_id = user_id!("@u:s.co");
869 let mut room = FakeRoom::new();
870 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
871 let mut state = RoomIdentityState::new(room.clone()).await;
872
873 let update1 = state
875 .process_change(
876 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false)
877 .await,
878 )
879 .await;
880 let update2 = state
881 .process_change(
882 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false)
883 .await,
884 )
885 .await;
886 let update3 = state
887 .process_change(
888 identity_change(&mut room, user_id, IdentityState::Pinned, false, false).await,
889 )
890 .await;
891 let update4 = state
892 .process_change(
893 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false)
894 .await,
895 )
896 .await;
897
898 assert_eq!(
900 update1,
901 vec![IdentityStatusChange {
902 user_id: user_id.to_owned(),
903 changed_to: IdentityState::PinViolation
904 }]
905 );
906 assert_eq!(update2, vec![]);
908 assert_eq!(
909 update3,
910 vec![IdentityStatusChange {
911 user_id: user_id.to_owned(),
912 changed_to: IdentityState::Pinned
913 }]
914 );
915 assert_eq!(
916 update4,
917 vec![IdentityStatusChange {
918 user_id: user_id.to_owned(),
919 changed_to: IdentityState::PinViolation
920 }]
921 );
922 }
923
924 #[async_test]
925 async fn test_current_state_of_all_pinned_room_is_empty() {
926 let user1 = user_id!("@u1:s.co");
928 let user2 = user_id!("@u2:s.co");
929 let mut room = FakeRoom::new();
930 room.member(other_user_identity(user1).await, IdentityState::Pinned);
931 room.member(other_user_identity(user2).await, IdentityState::Pinned);
932 let state = RoomIdentityState::new(room).await;
933 assert!(state.current_state().is_empty());
934 }
935
936 #[async_test]
937 async fn test_current_state_contains_all_nonpinned_users() {
938 let user1 = user_id!("@u1:s.co");
940 let user2 = user_id!("@u2:s.co");
941 let user3 = user_id!("@u3:s.co");
942 let user4 = user_id!("@u4:s.co");
943 let user5 = user_id!("@u5:s.co");
944 let user6 = user_id!("@u6:s.co");
945 let mut room = FakeRoom::new();
946 room.member(other_user_identity(user1).await, IdentityState::Pinned);
947 room.member(other_user_identity(user2).await, IdentityState::PinViolation);
948 room.member(other_user_identity(user3).await, IdentityState::Pinned);
949 room.member(other_user_identity(user4).await, IdentityState::PinViolation);
950 room.member(other_user_identity(user5).await, IdentityState::Verified);
951 room.member(other_user_identity(user6).await, IdentityState::VerificationViolation);
952 let mut state = RoomIdentityState::new(room).await.current_state();
953 state.sort_by_key(|change| change.user_id.to_owned());
954 assert_eq!(
955 state,
956 vec![
957 IdentityStatusChange {
958 user_id: owned_user_id!("@u2:s.co"),
959 changed_to: IdentityState::PinViolation
960 },
961 IdentityStatusChange {
962 user_id: owned_user_id!("@u4:s.co"),
963 changed_to: IdentityState::PinViolation
964 },
965 IdentityStatusChange {
966 user_id: owned_user_id!("@u5:s.co"),
967 changed_to: IdentityState::Verified
968 },
969 IdentityStatusChange {
970 user_id: owned_user_id!("@u6:s.co"),
971 changed_to: IdentityState::VerificationViolation
972 }
973 ]
974 );
975 }
976
977 #[derive(Debug)]
978 struct Membership {
979 is_member: bool,
980 user_identity: UserIdentity,
981 identity_state: IdentityState,
982 }
983
984 #[derive(Clone, Debug)]
985 struct FakeRoom {
986 users: Arc<Mutex<HashMap<OwnedUserId, Membership>>>,
987 }
988
989 impl FakeRoom {
990 fn new() -> Self {
991 Self { users: Default::default() }
992 }
993
994 fn member(&mut self, user_identity: UserIdentity, identity_state: IdentityState) {
995 self.users.lock().unwrap().insert(
996 user_identity.user_id().to_owned(),
997 Membership { is_member: true, user_identity, identity_state },
998 );
999 }
1000
1001 fn non_member(&mut self, user_identity: UserIdentity, identity_state: IdentityState) {
1002 self.users.lock().unwrap().insert(
1003 user_identity.user_id().to_owned(),
1004 Membership { is_member: false, user_identity, identity_state },
1005 );
1006 }
1007
1008 fn update_state(&self, user_id: &UserId, changed_to: &IdentityState) {
1009 self.users
1010 .lock()
1011 .unwrap()
1012 .entry(user_id.to_owned())
1013 .and_modify(|m| m.identity_state = changed_to.clone());
1014 }
1015 }
1016
1017 impl RoomIdentityProvider for FakeRoom {
1018 fn is_member<'a>(&'a self, user_id: &'a UserId) -> BoxFuture<'a, bool> {
1019 Box::pin(async {
1020 self.users.lock().unwrap().get(user_id).map(|m| m.is_member).unwrap_or(false)
1021 })
1022 }
1023
1024 fn member_identities(&self) -> BoxFuture<'_, Vec<UserIdentity>> {
1025 Box::pin(async {
1026 self.users
1027 .lock()
1028 .unwrap()
1029 .values()
1030 .filter_map(|m| if m.is_member { Some(m.user_identity.clone()) } else { None })
1031 .collect()
1032 })
1033 }
1034
1035 fn user_identity<'a>(&'a self, user_id: &'a UserId) -> BoxFuture<'a, Option<UserIdentity>> {
1036 Box::pin(async {
1037 self.users.lock().unwrap().get(user_id).map(|m| m.user_identity.clone())
1038 })
1039 }
1040
1041 fn state_of(&self, user_identity: &UserIdentity) -> IdentityState {
1042 self.users
1043 .lock()
1044 .unwrap()
1045 .get(user_identity.user_id())
1046 .map(|m| m.identity_state.clone())
1047 .unwrap_or(IdentityState::Pinned)
1048 }
1049 }
1050
1051 fn room_change(user_id: &UserId, new_state: MembershipState) -> RoomIdentityChange {
1052 let event = SyncRoomMemberEvent::Original(OriginalSyncStateEvent {
1053 content: RoomMemberEventContent::new(new_state),
1054 event_id: owned_event_id!("$1"),
1055 sender: owned_user_id!("@admin:b.c"),
1056 origin_server_ts: MilliSecondsSinceUnixEpoch(UInt::new(2123).unwrap()),
1057 unsigned: RoomMemberUnsigned::new(),
1058 state_key: user_id.to_owned(),
1059 });
1060 RoomIdentityChange::SyncRoomMemberEvent(Box::new(event))
1061 }
1062
1063 async fn identity_change(
1064 room: &mut FakeRoom,
1065 user_id: &UserId,
1066 changed_to: IdentityState,
1067 new: bool,
1068 own: bool,
1069 ) -> RoomIdentityChange {
1070 identity_changes(
1071 room,
1072 &[IdentityChangeSpec { user_id: user_id.to_owned(), changed_to, new, own }],
1073 )
1074 .await
1075 }
1076
1077 struct IdentityChangeSpec {
1078 user_id: OwnedUserId,
1079 changed_to: IdentityState,
1080 new: bool,
1081 own: bool,
1082 }
1083
1084 async fn identity_changes(
1085 room: &mut FakeRoom,
1086 changes: &[IdentityChangeSpec],
1087 ) -> RoomIdentityChange {
1088 let mut updates = IdentityUpdates::default();
1089
1090 for change in changes {
1091 let user_identity = if change.own {
1092 own_user_identity(&change.user_id).await
1093 } else {
1094 other_user_identity(&change.user_id).await
1095 };
1096
1097 room.update_state(user_identity.user_id(), &change.changed_to);
1098 if change.new {
1099 updates.new.insert(user_identity.user_id().to_owned(), user_identity);
1100 } else {
1101 updates.changed.insert(user_identity.user_id().to_owned(), user_identity);
1102 }
1103 }
1104 RoomIdentityChange::IdentityUpdates(updates)
1105 }
1106
1107 async fn other_user_identity(user_id: &UserId) -> UserIdentity {
1109 use std::sync::Arc;
1110
1111 use ruma::owned_device_id;
1112 use tokio::sync::Mutex;
1113
1114 use crate::{
1115 olm::PrivateCrossSigningIdentity,
1116 store::{CryptoStoreWrapper, MemoryStore},
1117 verification::VerificationMachine,
1118 Account,
1119 };
1120
1121 let device_id = owned_device_id!("DEV123");
1122 let account = Account::with_device_id(user_id, &device_id);
1123
1124 let private_identity =
1125 Arc::new(Mutex::new(PrivateCrossSigningIdentity::with_account(&account).await.0));
1126
1127 let other_user_identity_data =
1128 OtherUserIdentityData::from_private(&*private_identity.lock().await).await;
1129
1130 UserIdentity::Other(OtherUserIdentity {
1131 inner: other_user_identity_data,
1132 own_identity: None,
1133 verification_machine: VerificationMachine::new(
1134 account.clone(),
1135 Arc::new(Mutex::new(PrivateCrossSigningIdentity::new(
1136 account.user_id().to_owned(),
1137 ))),
1138 Arc::new(CryptoStoreWrapper::new(
1139 account.user_id(),
1140 account.device_id(),
1141 MemoryStore::new(),
1142 )),
1143 ),
1144 })
1145 }
1146
1147 async fn own_user_identity(user_id: &UserId) -> UserIdentity {
1149 use std::sync::Arc;
1150
1151 use ruma::owned_device_id;
1152 use tokio::sync::Mutex;
1153
1154 use crate::{
1155 olm::PrivateCrossSigningIdentity,
1156 store::{CryptoStoreWrapper, MemoryStore},
1157 verification::VerificationMachine,
1158 Account,
1159 };
1160
1161 let device_id = owned_device_id!("DEV123");
1162 let account = Account::with_device_id(user_id, &device_id);
1163
1164 let private_identity =
1165 Arc::new(Mutex::new(PrivateCrossSigningIdentity::with_account(&account).await.0));
1166
1167 let own_user_identity_data =
1168 OwnUserIdentityData::from_private(&*private_identity.lock().await).await;
1169
1170 let cross_signing_identity = PrivateCrossSigningIdentity::new(account.user_id().to_owned());
1171 let verification_machine = VerificationMachine::new(
1172 account.clone(),
1173 Arc::new(Mutex::new(cross_signing_identity.clone())),
1174 Arc::new(CryptoStoreWrapper::new(
1175 account.user_id(),
1176 account.device_id(),
1177 MemoryStore::new(),
1178 )),
1179 );
1180
1181 UserIdentity::Own(own_identity_wrapped(
1182 own_user_identity_data,
1183 verification_machine.clone(),
1184 Store::new(
1185 account.static_data().clone(),
1186 Arc::new(Mutex::new(cross_signing_identity)),
1187 Arc::new(CryptoStoreWrapper::new(
1188 user_id!("@u:s.co"),
1189 device_id!("DEV7"),
1190 MemoryStore::new(),
1191 )),
1192 verification_machine,
1193 ),
1194 ))
1195 }
1196}