1use std::{collections::HashMap, ops::Deref};
16
17use matrix_sdk_common::BoxFuture;
18use ruma::{
19 OwnedUserId, UserId,
20 events::{
21 SyncStateEvent,
22 room::member::{MembershipState, SyncRoomMemberEvent},
23 },
24};
25
26use super::UserIdentity;
27use crate::store::types::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 = &event.state_key;
143 if let Some(user_identity @ UserIdentity::Other(_)) =
145 self.room.user_identity(user_id).await
146 {
147 if matches!(
149 self.room.state_of(&user_identity),
150 IdentityState::Verified | IdentityState::Pinned
151 ) {
152 return vec![];
153 }
154
155 match event.content.membership {
156 MembershipState::Join | MembershipState::Invite => {
157 if let Some(update) = self.update_user_state(user_id, &user_identity) {
160 return vec![update];
161 }
162 }
163 MembershipState::Leave | MembershipState::Ban => {
164 if let Some(update) =
169 self.update_user_state_to(user_id, IdentityState::Pinned)
170 {
171 return vec![update];
172 }
173 }
174 MembershipState::Knock => {
175 }
177 _ => {}
178 }
179 }
180 }
181
182 vec![]
184 }
185
186 fn update_user_state(
187 &mut self,
188 user_id: &UserId,
189 user_identity: &UserIdentity,
190 ) -> Option<IdentityStatusChange> {
191 if let UserIdentity::Other(_) = &user_identity {
192 self.update_user_state_to(user_id, self.room.state_of(user_identity))
193 } else {
194 None
196 }
197 }
198
199 fn update_user_state_to(
203 &mut self,
204 user_id: &UserId,
205 new_state: IdentityState,
206 ) -> Option<IdentityStatusChange> {
207 let old_state = self.known_states.get(user_id);
208
209 if old_state == new_state {
210 return None;
211 }
212
213 Some(self.set_state(user_id, new_state))
214 }
215
216 fn set_state(&mut self, user_id: &UserId, new_state: IdentityState) -> IdentityStatusChange {
217 self.known_states.set(user_id, &new_state);
219
220 IdentityStatusChange { user_id: user_id.to_owned(), changed_to: new_state }
222 }
223}
224
225#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
237pub struct IdentityStatusChange {
238 pub user_id: OwnedUserId,
240
241 pub changed_to: IdentityState,
243}
244
245#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
247#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
248pub enum IdentityState {
249 Verified,
251
252 Pinned,
256
257 PinViolation,
263
264 VerificationViolation,
269}
270
271#[derive(Debug)]
275pub enum RoomIdentityChange {
276 IdentityUpdates(IdentityUpdates),
278
279 SyncRoomMemberEvent(Box<SyncRoomMemberEvent>),
282}
283
284#[derive(Debug)]
287struct KnownStates {
288 known_states: HashMap<OwnedUserId, IdentityState>,
289}
290
291impl KnownStates {
292 fn from_identities(
293 member_identities: impl IntoIterator<Item = UserIdentity>,
294 room: &dyn RoomIdentityProvider,
295 ) -> Self {
296 let mut known_states = HashMap::new();
297 for user_identity in member_identities {
298 let state = room.state_of(&user_identity);
299 if state != IdentityState::Pinned {
300 known_states.insert(user_identity.user_id().to_owned(), state);
301 }
302 }
303 Self { known_states }
304 }
305
306 fn get(&self, user_id: &UserId) -> IdentityState {
309 self.known_states.get(user_id).cloned().unwrap_or(IdentityState::Pinned)
310 }
311
312 fn set(&mut self, user_id: &UserId, identity_state: &IdentityState) {
315 if let IdentityState::Pinned = identity_state {
316 self.known_states.remove(user_id);
317 } else {
318 self.known_states.insert(user_id.to_owned(), identity_state.clone());
319 }
320 }
321}
322
323#[cfg(test)]
324mod tests {
325 use std::{
326 collections::HashMap,
327 sync::{Arc, Mutex},
328 };
329
330 use matrix_sdk_common::BoxFuture;
331 use matrix_sdk_test::{async_test, event_factory::EventFactory};
332 use ruma::{
333 OwnedUserId, UserId, device_id, events::room::member::MembershipState, owned_user_id,
334 user_id,
335 };
336
337 use super::{IdentityState, RoomIdentityChange, RoomIdentityProvider, RoomIdentityState};
338 use crate::{
339 IdentityStatusChange, OtherUserIdentity, OtherUserIdentityData, OwnUserIdentityData,
340 UserIdentity,
341 identities::user::testing::own_identity_wrapped,
342 store::{Store, types::IdentityUpdates},
343 };
344
345 #[async_test]
346 async fn test_unpinning_a_pinned_identity_in_the_room_notifies() {
347 let user_id = user_id!("@u:s.co");
349 let mut room = FakeRoom::new();
350 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
351 let mut state = RoomIdentityState::new(room.clone()).await;
352
353 let updates =
355 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false).await;
356 let update = state.process_change(updates).await;
357
358 assert_eq!(
360 update,
361 vec![IdentityStatusChange {
362 user_id: user_id.to_owned(),
363 changed_to: IdentityState::PinViolation
364 }]
365 );
366 }
367
368 #[async_test]
369 async fn test_verifying_a_pinned_identity_in_the_room_notifies() {
370 let user_id = user_id!("@u:s.co");
372 let mut room = FakeRoom::new();
373 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
374 let mut state = RoomIdentityState::new(room.clone()).await;
375
376 let updates =
378 identity_change(&mut room, user_id, IdentityState::Verified, false, false).await;
379 let update = state.process_change(updates).await;
380
381 assert_eq!(
383 update,
384 vec![IdentityStatusChange {
385 user_id: user_id.to_owned(),
386 changed_to: IdentityState::Verified
387 }]
388 );
389 }
390
391 #[async_test]
392 async fn test_pinning_an_unpinned_identity_in_the_room_notifies() {
393 let user_id = user_id!("@u:s.co");
395 let mut room = FakeRoom::new();
396 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
397 let mut state = RoomIdentityState::new(room.clone()).await;
398
399 let updates =
401 identity_change(&mut room, user_id, IdentityState::Pinned, false, false).await;
402 let update = state.process_change(updates).await;
403
404 assert_eq!(
406 update,
407 vec![IdentityStatusChange {
408 user_id: user_id.to_owned(),
409 changed_to: IdentityState::Pinned
410 }]
411 );
412 }
413
414 #[async_test]
415 async fn test_unpinned_identity_becoming_verification_violating_in_the_room_notifies() {
416 let user_id = user_id!("@u:s.co");
418 let mut room = FakeRoom::new();
419 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
420 let mut state = RoomIdentityState::new(room.clone()).await;
421
422 let updates =
424 identity_change(&mut room, user_id, IdentityState::VerificationViolation, false, false)
425 .await;
426 let update = state.process_change(updates).await;
427
428 assert_eq!(
430 update,
431 vec![IdentityStatusChange {
432 user_id: user_id.to_owned(),
433 changed_to: IdentityState::VerificationViolation
434 }]
435 );
436 }
437
438 #[async_test]
439 async fn test_unpinning_an_identity_not_in_the_room_does_nothing() {
440 let user_id = user_id!("@u:s.co");
442 let mut room = FakeRoom::new();
443 let mut state = RoomIdentityState::new(room.clone()).await;
444
445 let updates =
447 identity_change(&mut room, user_id, IdentityState::PinViolation, true, false).await;
448 let update = state.process_change(updates).await;
449
450 assert_eq!(update, vec![]);
452 }
453
454 #[async_test]
455 async fn test_pinning_an_identity_not_in_the_room_does_nothing() {
456 let user_id = user_id!("@u:s.co");
458 let mut room = FakeRoom::new();
459 let mut state = RoomIdentityState::new(room.clone()).await;
460
461 let updates = identity_change(&mut room, user_id, IdentityState::Pinned, true, false).await;
463 let update = state.process_change(updates).await;
464
465 assert_eq!(update, []);
467 }
468
469 #[async_test]
470 async fn test_pinning_an_already_pinned_identity_in_the_room_does_nothing() {
471 let user_id = user_id!("@u:s.co");
473 let mut room = FakeRoom::new();
474 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
475 let mut state = RoomIdentityState::new(room.clone()).await;
476
477 let updates =
479 identity_change(&mut room, user_id, IdentityState::Pinned, false, false).await;
480 let update = state.process_change(updates).await;
481
482 assert_eq!(update, []);
484 }
485
486 #[async_test]
487 async fn test_unpinning_an_already_unpinned_identity_in_the_room_does_nothing() {
488 let user_id = user_id!("@u:s.co");
490 let mut room = FakeRoom::new();
491 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
492 let mut state = RoomIdentityState::new(room.clone()).await;
493
494 let updates =
496 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false).await;
497 let update = state.process_change(updates).await;
498
499 assert_eq!(update, []);
501 }
502
503 #[async_test]
504 async fn test_a_pinned_identity_joining_the_room_does_nothing() {
505 let user_id = user_id!("@u:s.co");
507 let mut room = FakeRoom::new();
508 room.non_member(other_user_identity(user_id).await, IdentityState::Pinned);
509 let mut state = RoomIdentityState::new(room.clone()).await;
510
511 let updates = room_change(user_id, MembershipState::Join);
513 let update = state.process_change(updates).await;
514
515 assert_eq!(update, []);
517 }
518
519 #[async_test]
520 async fn test_a_verified_identity_joining_the_room_does_nothing() {
521 let user_id = user_id!("@u:s.co");
523 let mut room = FakeRoom::new();
524 room.non_member(other_user_identity(user_id).await, IdentityState::Verified);
525 let mut state = RoomIdentityState::new(room).await;
526
527 let updates = room_change(user_id, MembershipState::Join);
529 let update = state.process_change(updates).await;
530
531 assert_eq!(update, []);
533 }
534
535 #[async_test]
536 async fn test_an_unpinned_identity_joining_the_room_notifies() {
537 let user_id = user_id!("@u:s.co");
539 let mut room = FakeRoom::new();
540 room.non_member(other_user_identity(user_id).await, IdentityState::PinViolation);
541 let mut state = RoomIdentityState::new(room.clone()).await;
542
543 let updates = room_change(user_id, MembershipState::Join);
545 let update = state.process_change(updates).await;
546
547 assert_eq!(
549 update,
550 vec![IdentityStatusChange {
551 user_id: user_id.to_owned(),
552 changed_to: IdentityState::PinViolation
553 }]
554 );
555 }
556
557 #[async_test]
558 async fn test_a_pinned_identity_invited_to_the_room_does_nothing() {
559 let user_id = user_id!("@u:s.co");
561 let mut room = FakeRoom::new();
562 room.non_member(other_user_identity(user_id).await, IdentityState::Pinned);
563 let mut state = RoomIdentityState::new(room.clone()).await;
564
565 let updates = room_change(user_id, MembershipState::Invite);
567 let update = state.process_change(updates).await;
568
569 assert_eq!(update, []);
571 }
572
573 #[async_test]
574 async fn test_an_unpinned_identity_invited_to_the_room_notifies() {
575 let user_id = user_id!("@u:s.co");
577 let mut room = FakeRoom::new();
578 room.non_member(other_user_identity(user_id).await, IdentityState::PinViolation);
579 let mut state = RoomIdentityState::new(room.clone()).await;
580
581 let updates = room_change(user_id, MembershipState::Invite);
583 let update = state.process_change(updates).await;
584
585 assert_eq!(
587 update,
588 vec![IdentityStatusChange {
589 user_id: user_id.to_owned(),
590 changed_to: IdentityState::PinViolation
591 }]
592 );
593 }
594
595 #[async_test]
596 async fn test_a_verification_violating_identity_invited_to_the_room_notifies() {
597 let user_id = user_id!("@u:s.co");
599 let mut room = FakeRoom::new();
600 room.non_member(other_user_identity(user_id).await, IdentityState::VerificationViolation);
601 let mut state = RoomIdentityState::new(room).await;
602
603 let updates = room_change(user_id, MembershipState::Invite);
605 let update = state.process_change(updates).await;
606
607 assert_eq!(
609 update,
610 vec![IdentityStatusChange {
611 user_id: user_id.to_owned(),
612 changed_to: IdentityState::VerificationViolation
613 }]
614 );
615 }
616
617 #[async_test]
618 async fn test_own_identity_becoming_unpinned_is_ignored() {
619 let user_id = user_id!("@u:s.co");
621 let mut room = FakeRoom::new();
622 room.member(own_user_identity(user_id).await, IdentityState::Pinned);
623 let mut state = RoomIdentityState::new(room.clone()).await;
624
625 let updates =
627 identity_change(&mut room, user_id, IdentityState::PinViolation, false, true).await;
628 let update = state.process_change(updates).await;
629
630 assert_eq!(update, vec![]);
632 }
633
634 #[async_test]
635 async fn test_own_identity_becoming_pinned_is_ignored() {
636 let user_id = user_id!("@u:s.co");
638 let mut room = FakeRoom::new();
639 room.member(own_user_identity(user_id).await, IdentityState::PinViolation);
640 let mut state = RoomIdentityState::new(room.clone()).await;
641
642 let updates = identity_change(&mut room, user_id, IdentityState::Pinned, false, true).await;
644 let update = state.process_change(updates).await;
645
646 assert_eq!(update, vec![]);
648 }
649
650 #[async_test]
651 async fn test_own_pinned_identity_joining_room_is_ignored() {
652 let user_id = user_id!("@u:s.co");
654 let mut room = FakeRoom::new();
655 room.non_member(own_user_identity(user_id).await, IdentityState::Pinned);
656 let mut state = RoomIdentityState::new(room.clone()).await;
657
658 let updates = room_change(user_id, MembershipState::Join);
660 let update = state.process_change(updates).await;
661
662 assert_eq!(update, []);
664 }
665
666 #[async_test]
667 async fn test_own_unpinned_identity_joining_room_is_ignored() {
668 let user_id = user_id!("@u:s.co");
670 let mut room = FakeRoom::new();
671 room.non_member(own_user_identity(user_id).await, IdentityState::PinViolation);
672 let mut state = RoomIdentityState::new(room.clone()).await;
673
674 let updates = room_change(user_id, MembershipState::Join);
676 let update = state.process_change(updates).await;
677
678 assert_eq!(update, vec![]);
680 }
681
682 #[async_test]
683 async fn test_a_pinned_identity_leaving_the_room_does_nothing() {
684 let user_id = user_id!("@u:s.co");
686 let mut room = FakeRoom::new();
687 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
688 let mut state = RoomIdentityState::new(room.clone()).await;
689
690 let updates = room_change(user_id, MembershipState::Leave);
692 let update = state.process_change(updates).await;
693
694 assert_eq!(update, []);
696 }
697
698 #[async_test]
699 async fn test_a_verified_identity_leaving_the_room_does_nothing() {
700 let user_id = user_id!("@u:s.co");
702 let mut room = FakeRoom::new();
703 room.member(other_user_identity(user_id).await, IdentityState::Verified);
704 let mut state = RoomIdentityState::new(room).await;
705
706 let updates = room_change(user_id, MembershipState::Leave);
708 let update = state.process_change(updates).await;
709
710 assert_eq!(update, []);
712 }
713
714 #[async_test]
715 async fn test_an_unpinned_identity_leaving_the_room_notifies() {
716 let user_id = user_id!("@u:s.co");
718 let mut room = FakeRoom::new();
719 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
720 let mut state = RoomIdentityState::new(room.clone()).await;
721
722 let updates = room_change(user_id, MembershipState::Leave);
724 let update = state.process_change(updates).await;
725
726 assert_eq!(
728 update,
729 vec![IdentityStatusChange {
730 user_id: user_id.to_owned(),
731 changed_to: IdentityState::Pinned
732 }]
733 );
734 }
735
736 #[async_test]
737 async fn test_a_verification_violating_identity_leaving_the_room_notifies() {
738 let user_id = user_id!("@u:s.co");
740 let mut room = FakeRoom::new();
741 room.member(other_user_identity(user_id).await, IdentityState::VerificationViolation);
742 let mut state = RoomIdentityState::new(room).await;
743
744 let updates = room_change(user_id, MembershipState::Leave);
746 let update = state.process_change(updates).await;
747
748 assert_eq!(
750 update,
751 vec![IdentityStatusChange {
752 user_id: user_id.to_owned(),
753 changed_to: IdentityState::Pinned
754 }]
755 );
756 }
757
758 #[async_test]
759 async fn test_a_pinned_identity_being_banned_does_nothing() {
760 let user_id = user_id!("@u:s.co");
762 let mut room = FakeRoom::new();
763 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
764 let mut state = RoomIdentityState::new(room.clone()).await;
765
766 let updates = room_change(user_id, MembershipState::Ban);
768 let update = state.process_change(updates).await;
769
770 assert_eq!(update, []);
772 }
773
774 #[async_test]
775 async fn test_an_unpinned_identity_being_banned_notifies() {
776 let user_id = user_id!("@u:s.co");
778 let mut room = FakeRoom::new();
779 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
780 let mut state = RoomIdentityState::new(room.clone()).await;
781
782 let updates = room_change(user_id, MembershipState::Ban);
784 let update = state.process_change(updates).await;
785
786 assert_eq!(
788 update,
789 vec![IdentityStatusChange {
790 user_id: user_id.to_owned(),
791 changed_to: IdentityState::Pinned
792 }]
793 );
794 }
795
796 #[async_test]
797 async fn test_multiple_simultaneous_identity_updates_are_all_notified() {
798 let user1 = user_id!("@u1:s.co");
800 let user2 = user_id!("@u2:s.co");
801 let user3 = user_id!("@u3:s.co");
802 let mut room = FakeRoom::new();
803 room.member(other_user_identity(user1).await, IdentityState::Pinned);
804 room.member(other_user_identity(user2).await, IdentityState::PinViolation);
805 room.member(other_user_identity(user3).await, IdentityState::Pinned);
806 let mut state = RoomIdentityState::new(room.clone()).await;
807
808 let updates = identity_changes(
810 &mut room,
811 &[
812 IdentityChangeSpec {
813 user_id: user1.to_owned(),
814 changed_to: IdentityState::PinViolation,
815 new: false,
816 own: false,
817 },
818 IdentityChangeSpec {
819 user_id: user2.to_owned(),
820 changed_to: IdentityState::Pinned,
821 new: false,
822 own: false,
823 },
824 IdentityChangeSpec {
825 user_id: user3.to_owned(),
826 changed_to: IdentityState::PinViolation,
827 new: false,
828 own: false,
829 },
830 ],
831 )
832 .await;
833 let update = state.process_change(updates).await;
834
835 assert_eq!(
837 update,
838 vec![
839 IdentityStatusChange {
840 user_id: user1.to_owned(),
841 changed_to: IdentityState::PinViolation
842 },
843 IdentityStatusChange {
844 user_id: user2.to_owned(),
845 changed_to: IdentityState::Pinned
846 },
847 IdentityStatusChange {
848 user_id: user3.to_owned(),
849 changed_to: IdentityState::PinViolation
850 }
851 ]
852 );
853 }
854
855 #[async_test]
856 async fn test_multiple_changes_are_notified() {
857 let user_id = user_id!("@u:s.co");
859 let mut room = FakeRoom::new();
860 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
861 let mut state = RoomIdentityState::new(room.clone()).await;
862
863 let update1 = state
865 .process_change(
866 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false)
867 .await,
868 )
869 .await;
870 let update2 = state
871 .process_change(
872 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false)
873 .await,
874 )
875 .await;
876 let update3 = state
877 .process_change(
878 identity_change(&mut room, user_id, IdentityState::Pinned, false, false).await,
879 )
880 .await;
881 let update4 = state
882 .process_change(
883 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false)
884 .await,
885 )
886 .await;
887
888 assert_eq!(
890 update1,
891 vec![IdentityStatusChange {
892 user_id: user_id.to_owned(),
893 changed_to: IdentityState::PinViolation
894 }]
895 );
896 assert_eq!(update2, vec![]);
898 assert_eq!(
899 update3,
900 vec![IdentityStatusChange {
901 user_id: user_id.to_owned(),
902 changed_to: IdentityState::Pinned
903 }]
904 );
905 assert_eq!(
906 update4,
907 vec![IdentityStatusChange {
908 user_id: user_id.to_owned(),
909 changed_to: IdentityState::PinViolation
910 }]
911 );
912 }
913
914 #[async_test]
915 async fn test_current_state_of_all_pinned_room_is_empty() {
916 let user1 = user_id!("@u1:s.co");
918 let user2 = user_id!("@u2:s.co");
919 let mut room = FakeRoom::new();
920 room.member(other_user_identity(user1).await, IdentityState::Pinned);
921 room.member(other_user_identity(user2).await, IdentityState::Pinned);
922 let state = RoomIdentityState::new(room).await;
923 assert!(state.current_state().is_empty());
924 }
925
926 #[async_test]
927 async fn test_current_state_contains_all_nonpinned_users() {
928 let user1 = user_id!("@u1:s.co");
930 let user2 = user_id!("@u2:s.co");
931 let user3 = user_id!("@u3:s.co");
932 let user4 = user_id!("@u4:s.co");
933 let user5 = user_id!("@u5:s.co");
934 let user6 = user_id!("@u6:s.co");
935 let mut room = FakeRoom::new();
936 room.member(other_user_identity(user1).await, IdentityState::Pinned);
937 room.member(other_user_identity(user2).await, IdentityState::PinViolation);
938 room.member(other_user_identity(user3).await, IdentityState::Pinned);
939 room.member(other_user_identity(user4).await, IdentityState::PinViolation);
940 room.member(other_user_identity(user5).await, IdentityState::Verified);
941 room.member(other_user_identity(user6).await, IdentityState::VerificationViolation);
942 let mut state = RoomIdentityState::new(room).await.current_state();
943 state.sort_by_key(|change| change.user_id.to_owned());
944 assert_eq!(
945 state,
946 vec![
947 IdentityStatusChange {
948 user_id: owned_user_id!("@u2:s.co"),
949 changed_to: IdentityState::PinViolation
950 },
951 IdentityStatusChange {
952 user_id: owned_user_id!("@u4:s.co"),
953 changed_to: IdentityState::PinViolation
954 },
955 IdentityStatusChange {
956 user_id: owned_user_id!("@u5:s.co"),
957 changed_to: IdentityState::Verified
958 },
959 IdentityStatusChange {
960 user_id: owned_user_id!("@u6:s.co"),
961 changed_to: IdentityState::VerificationViolation
962 }
963 ]
964 );
965 }
966
967 #[derive(Debug)]
968 struct Membership {
969 is_member: bool,
970 user_identity: UserIdentity,
971 identity_state: IdentityState,
972 }
973
974 #[derive(Clone, Debug)]
975 struct FakeRoom {
976 users: Arc<Mutex<HashMap<OwnedUserId, Membership>>>,
977 }
978
979 impl FakeRoom {
980 fn new() -> Self {
981 Self { users: Default::default() }
982 }
983
984 fn member(&mut self, user_identity: UserIdentity, identity_state: IdentityState) {
985 self.users.lock().unwrap().insert(
986 user_identity.user_id().to_owned(),
987 Membership { is_member: true, user_identity, identity_state },
988 );
989 }
990
991 fn non_member(&mut self, user_identity: UserIdentity, identity_state: IdentityState) {
992 self.users.lock().unwrap().insert(
993 user_identity.user_id().to_owned(),
994 Membership { is_member: false, user_identity, identity_state },
995 );
996 }
997
998 fn update_state(&self, user_id: &UserId, changed_to: &IdentityState) {
999 self.users
1000 .lock()
1001 .unwrap()
1002 .entry(user_id.to_owned())
1003 .and_modify(|m| m.identity_state = changed_to.clone());
1004 }
1005 }
1006
1007 impl RoomIdentityProvider for FakeRoom {
1008 fn is_member<'a>(&'a self, user_id: &'a UserId) -> BoxFuture<'a, bool> {
1009 Box::pin(async {
1010 self.users.lock().unwrap().get(user_id).map(|m| m.is_member).unwrap_or(false)
1011 })
1012 }
1013
1014 fn member_identities(&self) -> BoxFuture<'_, Vec<UserIdentity>> {
1015 Box::pin(async {
1016 self.users
1017 .lock()
1018 .unwrap()
1019 .values()
1020 .filter_map(|m| if m.is_member { Some(m.user_identity.clone()) } else { None })
1021 .collect()
1022 })
1023 }
1024
1025 fn user_identity<'a>(&'a self, user_id: &'a UserId) -> BoxFuture<'a, Option<UserIdentity>> {
1026 Box::pin(async {
1027 self.users.lock().unwrap().get(user_id).map(|m| m.user_identity.clone())
1028 })
1029 }
1030
1031 fn state_of(&self, user_identity: &UserIdentity) -> IdentityState {
1032 self.users
1033 .lock()
1034 .unwrap()
1035 .get(user_identity.user_id())
1036 .map(|m| m.identity_state.clone())
1037 .unwrap_or(IdentityState::Pinned)
1038 }
1039 }
1040
1041 fn room_change(user_id: &UserId, new_state: MembershipState) -> RoomIdentityChange {
1042 let event = EventFactory::new()
1043 .sender(user_id!("@admin:b.c"))
1044 .member(user_id)
1045 .membership(new_state)
1046 .into();
1047 RoomIdentityChange::SyncRoomMemberEvent(Box::new(event))
1048 }
1049
1050 async fn identity_change(
1051 room: &mut FakeRoom,
1052 user_id: &UserId,
1053 changed_to: IdentityState,
1054 new: bool,
1055 own: bool,
1056 ) -> RoomIdentityChange {
1057 identity_changes(
1058 room,
1059 &[IdentityChangeSpec { user_id: user_id.to_owned(), changed_to, new, own }],
1060 )
1061 .await
1062 }
1063
1064 struct IdentityChangeSpec {
1065 user_id: OwnedUserId,
1066 changed_to: IdentityState,
1067 new: bool,
1068 own: bool,
1069 }
1070
1071 async fn identity_changes(
1072 room: &mut FakeRoom,
1073 changes: &[IdentityChangeSpec],
1074 ) -> RoomIdentityChange {
1075 let mut updates = IdentityUpdates::default();
1076
1077 for change in changes {
1078 let user_identity = if change.own {
1079 own_user_identity(&change.user_id).await
1080 } else {
1081 other_user_identity(&change.user_id).await
1082 };
1083
1084 room.update_state(user_identity.user_id(), &change.changed_to);
1085 if change.new {
1086 updates.new.insert(user_identity.user_id().to_owned(), user_identity);
1087 } else {
1088 updates.changed.insert(user_identity.user_id().to_owned(), user_identity);
1089 }
1090 }
1091 RoomIdentityChange::IdentityUpdates(updates)
1092 }
1093
1094 async fn other_user_identity(user_id: &UserId) -> UserIdentity {
1096 use std::sync::Arc;
1097
1098 use ruma::owned_device_id;
1099 use tokio::sync::Mutex;
1100
1101 use crate::{
1102 Account,
1103 olm::PrivateCrossSigningIdentity,
1104 store::{CryptoStoreWrapper, MemoryStore},
1105 verification::VerificationMachine,
1106 };
1107
1108 let device_id = owned_device_id!("DEV123");
1109 let account = Account::with_device_id(user_id, &device_id);
1110
1111 let private_identity = Arc::new(Mutex::new(
1112 PrivateCrossSigningIdentity::for_account(
1113 &account,
1114 #[cfg(feature = "experimental-x509-identity-verification")]
1115 None,
1116 )
1117 .await
1118 .unwrap(),
1119 ));
1120
1121 let other_user_identity_data =
1122 OtherUserIdentityData::from_private(&*private_identity.lock().await).await;
1123
1124 UserIdentity::Other(OtherUserIdentity {
1125 inner: other_user_identity_data,
1126 own_identity: None,
1127 verification_machine: VerificationMachine::new(
1128 account.clone(),
1129 Arc::new(Mutex::new(PrivateCrossSigningIdentity::new(
1130 account.user_id().to_owned(),
1131 ))),
1132 Arc::new(CryptoStoreWrapper::new(
1133 account.user_id(),
1134 account.device_id(),
1135 MemoryStore::new(),
1136 )),
1137 ),
1138 #[cfg(feature = "experimental-x509-identity-verification")]
1139 x509_verifier: None,
1140 })
1141 }
1142
1143 async fn own_user_identity(user_id: &UserId) -> UserIdentity {
1145 use std::sync::Arc;
1146
1147 use ruma::owned_device_id;
1148 use tokio::sync::Mutex;
1149
1150 use crate::{
1151 Account,
1152 olm::PrivateCrossSigningIdentity,
1153 store::{CryptoStoreWrapper, MemoryStore},
1154 verification::VerificationMachine,
1155 };
1156
1157 let device_id = owned_device_id!("DEV123");
1158 let account = Account::with_device_id(user_id, &device_id);
1159
1160 let private_identity = Arc::new(Mutex::new(
1161 PrivateCrossSigningIdentity::for_account(
1162 &account,
1163 #[cfg(feature = "experimental-x509-identity-verification")]
1164 None,
1165 )
1166 .await
1167 .unwrap(),
1168 ));
1169
1170 let own_user_identity_data =
1171 OwnUserIdentityData::from_private(&*private_identity.lock().await).await;
1172
1173 let cross_signing_identity = PrivateCrossSigningIdentity::new(account.user_id().to_owned());
1174 let verification_machine = VerificationMachine::new(
1175 account.clone(),
1176 Arc::new(Mutex::new(cross_signing_identity.clone())),
1177 Arc::new(CryptoStoreWrapper::new(
1178 account.user_id(),
1179 account.device_id(),
1180 MemoryStore::new(),
1181 )),
1182 );
1183
1184 UserIdentity::Own(own_identity_wrapped(
1185 own_user_identity_data,
1186 verification_machine.clone(),
1187 Store::new(
1188 account.static_data().clone(),
1189 Arc::new(Mutex::new(cross_signing_identity)),
1190 Arc::new(CryptoStoreWrapper::new(
1191 user_id!("@u:s.co"),
1192 device_id!("DEV7"),
1193 MemoryStore::new(),
1194 )),
1195 verification_machine,
1196 ),
1197 ))
1198 }
1199}