1use std::collections::HashMap;
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: SyncRoomMemberEvent,
138 ) -> Vec<IdentityStatusChange> {
139 if let SyncStateEvent::Original(event) = sync_room_member_event {
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(SyncRoomMemberEvent),
284}
285
286#[derive(Debug)]
289struct KnownStates {
290 known_states: HashMap<OwnedUserId, IdentityState>,
291}
292
293impl KnownStates {
294 fn from_identities(
295 member_identities: impl IntoIterator<Item = UserIdentity>,
296 room: &dyn RoomIdentityProvider,
297 ) -> Self {
298 let mut known_states = HashMap::new();
299 for user_identity in member_identities {
300 let state = room.state_of(&user_identity);
301 if state != IdentityState::Pinned {
302 known_states.insert(user_identity.user_id().to_owned(), state);
303 }
304 }
305 Self { known_states }
306 }
307
308 fn get(&self, user_id: &UserId) -> IdentityState {
311 self.known_states.get(user_id).cloned().unwrap_or(IdentityState::Pinned)
312 }
313
314 fn set(&mut self, user_id: &UserId, identity_state: &IdentityState) {
317 if let IdentityState::Pinned = identity_state {
318 self.known_states.remove(user_id);
319 } else {
320 self.known_states.insert(user_id.to_owned(), identity_state.clone());
321 }
322 }
323}
324
325#[cfg(test)]
326mod tests {
327 use std::{
328 collections::HashMap,
329 sync::{Arc, Mutex},
330 };
331
332 use matrix_sdk_common::BoxFuture;
333 use matrix_sdk_test::async_test;
334 use ruma::{
335 device_id,
336 events::{
337 room::member::{
338 MembershipState, RoomMemberEventContent, RoomMemberUnsigned, SyncRoomMemberEvent,
339 },
340 OriginalSyncStateEvent,
341 },
342 owned_event_id, owned_user_id, user_id, MilliSecondsSinceUnixEpoch, OwnedUserId, UInt,
343 UserId,
344 };
345
346 use super::{IdentityState, RoomIdentityChange, RoomIdentityProvider, RoomIdentityState};
347 use crate::{
348 identities::user::testing::own_identity_wrapped,
349 store::{IdentityUpdates, Store},
350 IdentityStatusChange, OtherUserIdentity, OtherUserIdentityData, OwnUserIdentityData,
351 UserIdentity,
352 };
353
354 #[async_test]
355 async fn test_unpinning_a_pinned_identity_in_the_room_notifies() {
356 let user_id = user_id!("@u:s.co");
358 let mut room = FakeRoom::new();
359 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
360 let mut state = RoomIdentityState::new(room.clone()).await;
361
362 let updates =
364 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false).await;
365 let update = state.process_change(updates).await;
366
367 assert_eq!(
369 update,
370 vec![IdentityStatusChange {
371 user_id: user_id.to_owned(),
372 changed_to: IdentityState::PinViolation
373 }]
374 );
375 }
376
377 #[async_test]
378 async fn test_verifying_a_pinned_identity_in_the_room_notifies() {
379 let user_id = user_id!("@u:s.co");
381 let mut room = FakeRoom::new();
382 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
383 let mut state = RoomIdentityState::new(room.clone()).await;
384
385 let updates =
387 identity_change(&mut room, user_id, IdentityState::Verified, false, false).await;
388 let update = state.process_change(updates).await;
389
390 assert_eq!(
392 update,
393 vec![IdentityStatusChange {
394 user_id: user_id.to_owned(),
395 changed_to: IdentityState::Verified
396 }]
397 );
398 }
399
400 #[async_test]
401 async fn test_pinning_an_unpinned_identity_in_the_room_notifies() {
402 let user_id = user_id!("@u:s.co");
404 let mut room = FakeRoom::new();
405 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
406 let mut state = RoomIdentityState::new(room.clone()).await;
407
408 let updates =
410 identity_change(&mut room, user_id, IdentityState::Pinned, false, false).await;
411 let update = state.process_change(updates).await;
412
413 assert_eq!(
415 update,
416 vec![IdentityStatusChange {
417 user_id: user_id.to_owned(),
418 changed_to: IdentityState::Pinned
419 }]
420 );
421 }
422
423 #[async_test]
424 async fn test_unpinned_identity_becoming_verification_violating_in_the_room_notifies() {
425 let user_id = user_id!("@u:s.co");
427 let mut room = FakeRoom::new();
428 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
429 let mut state = RoomIdentityState::new(room.clone()).await;
430
431 let updates =
433 identity_change(&mut room, user_id, IdentityState::VerificationViolation, false, false)
434 .await;
435 let update = state.process_change(updates).await;
436
437 assert_eq!(
439 update,
440 vec![IdentityStatusChange {
441 user_id: user_id.to_owned(),
442 changed_to: IdentityState::VerificationViolation
443 }]
444 );
445 }
446
447 #[async_test]
448 async fn test_unpinning_an_identity_not_in_the_room_does_nothing() {
449 let user_id = user_id!("@u:s.co");
451 let mut room = FakeRoom::new();
452 let mut state = RoomIdentityState::new(room.clone()).await;
453
454 let updates =
456 identity_change(&mut room, user_id, IdentityState::PinViolation, true, false).await;
457 let update = state.process_change(updates).await;
458
459 assert_eq!(update, vec![]);
461 }
462
463 #[async_test]
464 async fn test_pinning_an_identity_not_in_the_room_does_nothing() {
465 let user_id = user_id!("@u:s.co");
467 let mut room = FakeRoom::new();
468 let mut state = RoomIdentityState::new(room.clone()).await;
469
470 let updates = identity_change(&mut room, user_id, IdentityState::Pinned, true, false).await;
472 let update = state.process_change(updates).await;
473
474 assert_eq!(update, []);
476 }
477
478 #[async_test]
479 async fn test_pinning_an_already_pinned_identity_in_the_room_does_nothing() {
480 let user_id = user_id!("@u:s.co");
482 let mut room = FakeRoom::new();
483 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
484 let mut state = RoomIdentityState::new(room.clone()).await;
485
486 let updates =
488 identity_change(&mut room, user_id, IdentityState::Pinned, false, false).await;
489 let update = state.process_change(updates).await;
490
491 assert_eq!(update, []);
493 }
494
495 #[async_test]
496 async fn test_unpinning_an_already_unpinned_identity_in_the_room_does_nothing() {
497 let user_id = user_id!("@u:s.co");
499 let mut room = FakeRoom::new();
500 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
501 let mut state = RoomIdentityState::new(room.clone()).await;
502
503 let updates =
505 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false).await;
506 let update = state.process_change(updates).await;
507
508 assert_eq!(update, []);
510 }
511
512 #[async_test]
513 async fn test_a_pinned_identity_joining_the_room_does_nothing() {
514 let user_id = user_id!("@u:s.co");
516 let mut room = FakeRoom::new();
517 room.non_member(other_user_identity(user_id).await, IdentityState::Pinned);
518 let mut state = RoomIdentityState::new(room.clone()).await;
519
520 let updates = room_change(user_id, MembershipState::Join);
522 let update = state.process_change(updates).await;
523
524 assert_eq!(update, []);
526 }
527
528 #[async_test]
529 async fn test_a_verified_identity_joining_the_room_does_nothing() {
530 let user_id = user_id!("@u:s.co");
532 let mut room = FakeRoom::new();
533 room.non_member(other_user_identity(user_id).await, IdentityState::Verified);
534 let mut state = RoomIdentityState::new(room).await;
535
536 let updates = room_change(user_id, MembershipState::Join);
538 let update = state.process_change(updates).await;
539
540 assert_eq!(update, []);
542 }
543
544 #[async_test]
545 async fn test_an_unpinned_identity_joining_the_room_notifies() {
546 let user_id = user_id!("@u:s.co");
548 let mut room = FakeRoom::new();
549 room.non_member(other_user_identity(user_id).await, IdentityState::PinViolation);
550 let mut state = RoomIdentityState::new(room.clone()).await;
551
552 let updates = room_change(user_id, MembershipState::Join);
554 let update = state.process_change(updates).await;
555
556 assert_eq!(
558 update,
559 vec![IdentityStatusChange {
560 user_id: user_id.to_owned(),
561 changed_to: IdentityState::PinViolation
562 }]
563 );
564 }
565
566 #[async_test]
567 async fn test_a_pinned_identity_invited_to_the_room_does_nothing() {
568 let user_id = user_id!("@u:s.co");
570 let mut room = FakeRoom::new();
571 room.non_member(other_user_identity(user_id).await, IdentityState::Pinned);
572 let mut state = RoomIdentityState::new(room.clone()).await;
573
574 let updates = room_change(user_id, MembershipState::Invite);
576 let update = state.process_change(updates).await;
577
578 assert_eq!(update, []);
580 }
581
582 #[async_test]
583 async fn test_an_unpinned_identity_invited_to_the_room_notifies() {
584 let user_id = user_id!("@u:s.co");
586 let mut room = FakeRoom::new();
587 room.non_member(other_user_identity(user_id).await, IdentityState::PinViolation);
588 let mut state = RoomIdentityState::new(room.clone()).await;
589
590 let updates = room_change(user_id, MembershipState::Invite);
592 let update = state.process_change(updates).await;
593
594 assert_eq!(
596 update,
597 vec![IdentityStatusChange {
598 user_id: user_id.to_owned(),
599 changed_to: IdentityState::PinViolation
600 }]
601 );
602 }
603
604 #[async_test]
605 async fn test_a_verification_violating_identity_invited_to_the_room_notifies() {
606 let user_id = user_id!("@u:s.co");
608 let mut room = FakeRoom::new();
609 room.non_member(other_user_identity(user_id).await, IdentityState::VerificationViolation);
610 let mut state = RoomIdentityState::new(room).await;
611
612 let updates = room_change(user_id, MembershipState::Invite);
614 let update = state.process_change(updates).await;
615
616 assert_eq!(
618 update,
619 vec![IdentityStatusChange {
620 user_id: user_id.to_owned(),
621 changed_to: IdentityState::VerificationViolation
622 }]
623 );
624 }
625
626 #[async_test]
627 async fn test_own_identity_becoming_unpinned_is_ignored() {
628 let user_id = user_id!("@u:s.co");
630 let mut room = FakeRoom::new();
631 room.member(own_user_identity(user_id).await, IdentityState::Pinned);
632 let mut state = RoomIdentityState::new(room.clone()).await;
633
634 let updates =
636 identity_change(&mut room, user_id, IdentityState::PinViolation, false, true).await;
637 let update = state.process_change(updates).await;
638
639 assert_eq!(update, vec![]);
641 }
642
643 #[async_test]
644 async fn test_own_identity_becoming_pinned_is_ignored() {
645 let user_id = user_id!("@u:s.co");
647 let mut room = FakeRoom::new();
648 room.member(own_user_identity(user_id).await, IdentityState::PinViolation);
649 let mut state = RoomIdentityState::new(room.clone()).await;
650
651 let updates = identity_change(&mut room, user_id, IdentityState::Pinned, false, true).await;
653 let update = state.process_change(updates).await;
654
655 assert_eq!(update, vec![]);
657 }
658
659 #[async_test]
660 async fn test_own_pinned_identity_joining_room_is_ignored() {
661 let user_id = user_id!("@u:s.co");
663 let mut room = FakeRoom::new();
664 room.non_member(own_user_identity(user_id).await, IdentityState::Pinned);
665 let mut state = RoomIdentityState::new(room.clone()).await;
666
667 let updates = room_change(user_id, MembershipState::Join);
669 let update = state.process_change(updates).await;
670
671 assert_eq!(update, []);
673 }
674
675 #[async_test]
676 async fn test_own_unpinned_identity_joining_room_is_ignored() {
677 let user_id = user_id!("@u:s.co");
679 let mut room = FakeRoom::new();
680 room.non_member(own_user_identity(user_id).await, IdentityState::PinViolation);
681 let mut state = RoomIdentityState::new(room.clone()).await;
682
683 let updates = room_change(user_id, MembershipState::Join);
685 let update = state.process_change(updates).await;
686
687 assert_eq!(update, vec![]);
689 }
690
691 #[async_test]
692 async fn test_a_pinned_identity_leaving_the_room_does_nothing() {
693 let user_id = user_id!("@u:s.co");
695 let mut room = FakeRoom::new();
696 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
697 let mut state = RoomIdentityState::new(room.clone()).await;
698
699 let updates = room_change(user_id, MembershipState::Leave);
701 let update = state.process_change(updates).await;
702
703 assert_eq!(update, []);
705 }
706
707 #[async_test]
708 async fn test_a_verified_identity_leaving_the_room_does_nothing() {
709 let user_id = user_id!("@u:s.co");
711 let mut room = FakeRoom::new();
712 room.member(other_user_identity(user_id).await, IdentityState::Verified);
713 let mut state = RoomIdentityState::new(room).await;
714
715 let updates = room_change(user_id, MembershipState::Leave);
717 let update = state.process_change(updates).await;
718
719 assert_eq!(update, []);
721 }
722
723 #[async_test]
724 async fn test_an_unpinned_identity_leaving_the_room_notifies() {
725 let user_id = user_id!("@u:s.co");
727 let mut room = FakeRoom::new();
728 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
729 let mut state = RoomIdentityState::new(room.clone()).await;
730
731 let updates = room_change(user_id, MembershipState::Leave);
733 let update = state.process_change(updates).await;
734
735 assert_eq!(
737 update,
738 vec![IdentityStatusChange {
739 user_id: user_id.to_owned(),
740 changed_to: IdentityState::Pinned
741 }]
742 );
743 }
744
745 #[async_test]
746 async fn test_a_verification_violating_identity_leaving_the_room_notifies() {
747 let user_id = user_id!("@u:s.co");
749 let mut room = FakeRoom::new();
750 room.member(other_user_identity(user_id).await, IdentityState::VerificationViolation);
751 let mut state = RoomIdentityState::new(room).await;
752
753 let updates = room_change(user_id, MembershipState::Leave);
755 let update = state.process_change(updates).await;
756
757 assert_eq!(
759 update,
760 vec![IdentityStatusChange {
761 user_id: user_id.to_owned(),
762 changed_to: IdentityState::Pinned
763 }]
764 );
765 }
766
767 #[async_test]
768 async fn test_a_pinned_identity_being_banned_does_nothing() {
769 let user_id = user_id!("@u:s.co");
771 let mut room = FakeRoom::new();
772 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
773 let mut state = RoomIdentityState::new(room.clone()).await;
774
775 let updates = room_change(user_id, MembershipState::Ban);
777 let update = state.process_change(updates).await;
778
779 assert_eq!(update, []);
781 }
782
783 #[async_test]
784 async fn test_an_unpinned_identity_being_banned_notifies() {
785 let user_id = user_id!("@u:s.co");
787 let mut room = FakeRoom::new();
788 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
789 let mut state = RoomIdentityState::new(room.clone()).await;
790
791 let updates = room_change(user_id, MembershipState::Ban);
793 let update = state.process_change(updates).await;
794
795 assert_eq!(
797 update,
798 vec![IdentityStatusChange {
799 user_id: user_id.to_owned(),
800 changed_to: IdentityState::Pinned
801 }]
802 );
803 }
804
805 #[async_test]
806 async fn test_multiple_simultaneous_identity_updates_are_all_notified() {
807 let user1 = user_id!("@u1:s.co");
809 let user2 = user_id!("@u2:s.co");
810 let user3 = user_id!("@u3:s.co");
811 let mut room = FakeRoom::new();
812 room.member(other_user_identity(user1).await, IdentityState::Pinned);
813 room.member(other_user_identity(user2).await, IdentityState::PinViolation);
814 room.member(other_user_identity(user3).await, IdentityState::Pinned);
815 let mut state = RoomIdentityState::new(room.clone()).await;
816
817 let updates = identity_changes(
819 &mut room,
820 &[
821 IdentityChangeSpec {
822 user_id: user1.to_owned(),
823 changed_to: IdentityState::PinViolation,
824 new: false,
825 own: false,
826 },
827 IdentityChangeSpec {
828 user_id: user2.to_owned(),
829 changed_to: IdentityState::Pinned,
830 new: false,
831 own: false,
832 },
833 IdentityChangeSpec {
834 user_id: user3.to_owned(),
835 changed_to: IdentityState::PinViolation,
836 new: false,
837 own: false,
838 },
839 ],
840 )
841 .await;
842 let update = state.process_change(updates).await;
843
844 assert_eq!(
846 update,
847 vec![
848 IdentityStatusChange {
849 user_id: user1.to_owned(),
850 changed_to: IdentityState::PinViolation
851 },
852 IdentityStatusChange {
853 user_id: user2.to_owned(),
854 changed_to: IdentityState::Pinned
855 },
856 IdentityStatusChange {
857 user_id: user3.to_owned(),
858 changed_to: IdentityState::PinViolation
859 }
860 ]
861 );
862 }
863
864 #[async_test]
865 async fn test_multiple_changes_are_notified() {
866 let user_id = user_id!("@u:s.co");
868 let mut room = FakeRoom::new();
869 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
870 let mut state = RoomIdentityState::new(room.clone()).await;
871
872 let update1 = state
874 .process_change(
875 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false)
876 .await,
877 )
878 .await;
879 let update2 = state
880 .process_change(
881 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false)
882 .await,
883 )
884 .await;
885 let update3 = state
886 .process_change(
887 identity_change(&mut room, user_id, IdentityState::Pinned, false, false).await,
888 )
889 .await;
890 let update4 = state
891 .process_change(
892 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false)
893 .await,
894 )
895 .await;
896
897 assert_eq!(
899 update1,
900 vec![IdentityStatusChange {
901 user_id: user_id.to_owned(),
902 changed_to: IdentityState::PinViolation
903 }]
904 );
905 assert_eq!(update2, vec![]);
907 assert_eq!(
908 update3,
909 vec![IdentityStatusChange {
910 user_id: user_id.to_owned(),
911 changed_to: IdentityState::Pinned
912 }]
913 );
914 assert_eq!(
915 update4,
916 vec![IdentityStatusChange {
917 user_id: user_id.to_owned(),
918 changed_to: IdentityState::PinViolation
919 }]
920 );
921 }
922
923 #[async_test]
924 async fn test_current_state_of_all_pinned_room_is_empty() {
925 let user1 = user_id!("@u1:s.co");
927 let user2 = user_id!("@u2:s.co");
928 let mut room = FakeRoom::new();
929 room.member(other_user_identity(user1).await, IdentityState::Pinned);
930 room.member(other_user_identity(user2).await, IdentityState::Pinned);
931 let state = RoomIdentityState::new(room).await;
932 assert!(state.current_state().is_empty());
933 }
934
935 #[async_test]
936 async fn test_current_state_contains_all_nonpinned_users() {
937 let user1 = user_id!("@u1:s.co");
939 let user2 = user_id!("@u2:s.co");
940 let user3 = user_id!("@u3:s.co");
941 let user4 = user_id!("@u4:s.co");
942 let user5 = user_id!("@u5:s.co");
943 let user6 = user_id!("@u6:s.co");
944 let mut room = FakeRoom::new();
945 room.member(other_user_identity(user1).await, IdentityState::Pinned);
946 room.member(other_user_identity(user2).await, IdentityState::PinViolation);
947 room.member(other_user_identity(user3).await, IdentityState::Pinned);
948 room.member(other_user_identity(user4).await, IdentityState::PinViolation);
949 room.member(other_user_identity(user5).await, IdentityState::Verified);
950 room.member(other_user_identity(user6).await, IdentityState::VerificationViolation);
951 let mut state = RoomIdentityState::new(room).await.current_state();
952 state.sort_by_key(|change| change.user_id.to_owned());
953 assert_eq!(
954 state,
955 vec![
956 IdentityStatusChange {
957 user_id: owned_user_id!("@u2:s.co"),
958 changed_to: IdentityState::PinViolation
959 },
960 IdentityStatusChange {
961 user_id: owned_user_id!("@u4:s.co"),
962 changed_to: IdentityState::PinViolation
963 },
964 IdentityStatusChange {
965 user_id: owned_user_id!("@u5:s.co"),
966 changed_to: IdentityState::Verified
967 },
968 IdentityStatusChange {
969 user_id: owned_user_id!("@u6:s.co"),
970 changed_to: IdentityState::VerificationViolation
971 }
972 ]
973 );
974 }
975
976 #[derive(Debug)]
977 struct Membership {
978 is_member: bool,
979 user_identity: UserIdentity,
980 identity_state: IdentityState,
981 }
982
983 #[derive(Clone, Debug)]
984 struct FakeRoom {
985 users: Arc<Mutex<HashMap<OwnedUserId, Membership>>>,
986 }
987
988 impl FakeRoom {
989 fn new() -> Self {
990 Self { users: Default::default() }
991 }
992
993 fn member(&mut self, user_identity: UserIdentity, identity_state: IdentityState) {
994 self.users.lock().unwrap().insert(
995 user_identity.user_id().to_owned(),
996 Membership { is_member: true, user_identity, identity_state },
997 );
998 }
999
1000 fn non_member(&mut self, user_identity: UserIdentity, identity_state: IdentityState) {
1001 self.users.lock().unwrap().insert(
1002 user_identity.user_id().to_owned(),
1003 Membership { is_member: false, user_identity, identity_state },
1004 );
1005 }
1006
1007 fn update_state(&self, user_id: &UserId, changed_to: &IdentityState) {
1008 self.users
1009 .lock()
1010 .unwrap()
1011 .entry(user_id.to_owned())
1012 .and_modify(|m| m.identity_state = changed_to.clone());
1013 }
1014 }
1015
1016 impl RoomIdentityProvider for FakeRoom {
1017 fn is_member<'a>(&'a self, user_id: &'a UserId) -> BoxFuture<'a, bool> {
1018 Box::pin(async {
1019 self.users.lock().unwrap().get(user_id).map(|m| m.is_member).unwrap_or(false)
1020 })
1021 }
1022
1023 fn member_identities(&self) -> BoxFuture<'_, Vec<UserIdentity>> {
1024 Box::pin(async {
1025 self.users
1026 .lock()
1027 .unwrap()
1028 .values()
1029 .filter_map(|m| if m.is_member { Some(m.user_identity.clone()) } else { None })
1030 .collect()
1031 })
1032 }
1033
1034 fn user_identity<'a>(&'a self, user_id: &'a UserId) -> BoxFuture<'a, Option<UserIdentity>> {
1035 Box::pin(async {
1036 self.users.lock().unwrap().get(user_id).map(|m| m.user_identity.clone())
1037 })
1038 }
1039
1040 fn state_of(&self, user_identity: &UserIdentity) -> IdentityState {
1041 self.users
1042 .lock()
1043 .unwrap()
1044 .get(user_identity.user_id())
1045 .map(|m| m.identity_state.clone())
1046 .unwrap_or(IdentityState::Pinned)
1047 }
1048 }
1049
1050 fn room_change(user_id: &UserId, new_state: MembershipState) -> RoomIdentityChange {
1051 let event = SyncRoomMemberEvent::Original(OriginalSyncStateEvent {
1052 content: RoomMemberEventContent::new(new_state),
1053 event_id: owned_event_id!("$1"),
1054 sender: owned_user_id!("@admin:b.c"),
1055 origin_server_ts: MilliSecondsSinceUnixEpoch(UInt::new(2123).unwrap()),
1056 unsigned: RoomMemberUnsigned::new(),
1057 state_key: user_id.to_owned(),
1058 });
1059 RoomIdentityChange::SyncRoomMemberEvent(event)
1060 }
1061
1062 async fn identity_change(
1063 room: &mut FakeRoom,
1064 user_id: &UserId,
1065 changed_to: IdentityState,
1066 new: bool,
1067 own: bool,
1068 ) -> RoomIdentityChange {
1069 identity_changes(
1070 room,
1071 &[IdentityChangeSpec { user_id: user_id.to_owned(), changed_to, new, own }],
1072 )
1073 .await
1074 }
1075
1076 struct IdentityChangeSpec {
1077 user_id: OwnedUserId,
1078 changed_to: IdentityState,
1079 new: bool,
1080 own: bool,
1081 }
1082
1083 async fn identity_changes(
1084 room: &mut FakeRoom,
1085 changes: &[IdentityChangeSpec],
1086 ) -> RoomIdentityChange {
1087 let mut updates = IdentityUpdates::default();
1088
1089 for change in changes {
1090 let user_identity = if change.own {
1091 own_user_identity(&change.user_id).await
1092 } else {
1093 other_user_identity(&change.user_id).await
1094 };
1095
1096 room.update_state(user_identity.user_id(), &change.changed_to);
1097 if change.new {
1098 updates.new.insert(user_identity.user_id().to_owned(), user_identity);
1099 } else {
1100 updates.changed.insert(user_identity.user_id().to_owned(), user_identity);
1101 }
1102 }
1103 RoomIdentityChange::IdentityUpdates(updates)
1104 }
1105
1106 async fn other_user_identity(user_id: &UserId) -> UserIdentity {
1108 use std::sync::Arc;
1109
1110 use ruma::owned_device_id;
1111 use tokio::sync::Mutex;
1112
1113 use crate::{
1114 olm::PrivateCrossSigningIdentity,
1115 store::{CryptoStoreWrapper, MemoryStore},
1116 verification::VerificationMachine,
1117 Account,
1118 };
1119
1120 let device_id = owned_device_id!("DEV123");
1121 let account = Account::with_device_id(user_id, &device_id);
1122
1123 let private_identity =
1124 Arc::new(Mutex::new(PrivateCrossSigningIdentity::with_account(&account).await.0));
1125
1126 let other_user_identity_data =
1127 OtherUserIdentityData::from_private(&*private_identity.lock().await).await;
1128
1129 UserIdentity::Other(OtherUserIdentity {
1130 inner: other_user_identity_data,
1131 own_identity: None,
1132 verification_machine: VerificationMachine::new(
1133 account.clone(),
1134 Arc::new(Mutex::new(PrivateCrossSigningIdentity::new(
1135 account.user_id().to_owned(),
1136 ))),
1137 Arc::new(CryptoStoreWrapper::new(
1138 account.user_id(),
1139 account.device_id(),
1140 MemoryStore::new(),
1141 )),
1142 ),
1143 })
1144 }
1145
1146 async fn own_user_identity(user_id: &UserId) -> UserIdentity {
1148 use std::sync::Arc;
1149
1150 use ruma::owned_device_id;
1151 use tokio::sync::Mutex;
1152
1153 use crate::{
1154 olm::PrivateCrossSigningIdentity,
1155 store::{CryptoStoreWrapper, MemoryStore},
1156 verification::VerificationMachine,
1157 Account,
1158 };
1159
1160 let device_id = owned_device_id!("DEV123");
1161 let account = Account::with_device_id(user_id, &device_id);
1162
1163 let private_identity =
1164 Arc::new(Mutex::new(PrivateCrossSigningIdentity::with_account(&account).await.0));
1165
1166 let own_user_identity_data =
1167 OwnUserIdentityData::from_private(&*private_identity.lock().await).await;
1168
1169 let cross_signing_identity = PrivateCrossSigningIdentity::new(account.user_id().to_owned());
1170 let verification_machine = VerificationMachine::new(
1171 account.clone(),
1172 Arc::new(Mutex::new(cross_signing_identity.clone())),
1173 Arc::new(CryptoStoreWrapper::new(
1174 account.user_id(),
1175 account.device_id(),
1176 MemoryStore::new(),
1177 )),
1178 );
1179
1180 UserIdentity::Own(own_identity_wrapped(
1181 own_user_identity_data,
1182 verification_machine.clone(),
1183 Store::new(
1184 account.static_data().clone(),
1185 Arc::new(Mutex::new(cross_signing_identity)),
1186 Arc::new(CryptoStoreWrapper::new(
1187 user_id!("@u:s.co"),
1188 device_id!("DEV7"),
1189 MemoryStore::new(),
1190 )),
1191 verification_machine,
1192 ),
1193 ))
1194 }
1195}