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>>;
44
45 fn state_of(&self, user_identity: &UserIdentity) -> IdentityState {
48 if user_identity.is_verified() {
49 IdentityState::Verified
50 } else if user_identity.has_verification_violation() {
51 IdentityState::VerificationViolation
52 } else if let UserIdentity::Other(u) = user_identity {
53 if u.identity_needs_user_approval() {
54 IdentityState::PinViolation
55 } else {
56 IdentityState::Pinned
57 }
58 } else {
59 IdentityState::Pinned
60 }
61 }
62}
63
64#[derive(Debug)]
72pub struct RoomIdentityState<R: RoomIdentityProvider> {
73 room: R,
74 known_states: KnownStates,
75}
76
77impl<R: RoomIdentityProvider> RoomIdentityState<R> {
78 pub async fn new(room: R) -> Self {
81 let known_states = KnownStates::from_identities(room.member_identities().await, &room);
82 Self { room, known_states }
83 }
84
85 pub fn current_state(&self) -> Vec<IdentityStatusChange> {
88 self.known_states
89 .known_states
90 .iter()
91 .map(|(user_id, state)| IdentityStatusChange {
92 user_id: user_id.clone(),
93 changed_to: state.clone(),
94 })
95 .collect()
96 }
97
98 pub async fn process_change(&mut self, item: RoomIdentityChange) -> Vec<IdentityStatusChange> {
104 match item {
105 RoomIdentityChange::IdentityUpdates(identity_updates) => {
106 self.process_identity_changes(identity_updates).await
107 }
108 RoomIdentityChange::SyncRoomMemberEvent(sync_room_member_event) => {
109 self.process_membership_change(sync_room_member_event).await
110 }
111 }
112 }
113
114 async fn process_identity_changes(
115 &mut self,
116 identity_updates: IdentityUpdates,
117 ) -> Vec<IdentityStatusChange> {
118 let mut ret = vec![];
119
120 for user_identity in identity_updates.new.values().chain(identity_updates.changed.values())
121 {
122 let user_id = user_identity.user_id();
123 if self.room.is_member(user_id).await {
124 let update = self.update_user_state(user_id, user_identity);
125 if let Some(identity_status_change) = update {
126 ret.push(identity_status_change);
127 }
128 }
129 }
130
131 ret
132 }
133
134 async fn process_membership_change(
135 &mut self,
136 sync_room_member_event: Box<SyncRoomMemberEvent>,
137 ) -> Vec<IdentityStatusChange> {
138 if let SyncStateEvent::Original(event) = sync_room_member_event.deref() {
141 let user_id = &event.state_key;
142 if let Some(user_identity @ UserIdentity::Other(_)) =
144 self.room.user_identity(user_id).await
145 {
146 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)]
239pub struct IdentityStatusChange {
240 pub user_id: OwnedUserId,
242
243 pub changed_to: IdentityState,
245}
246
247#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
249#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
250pub enum IdentityState {
251 Verified,
253
254 Pinned,
258
259 PinViolation,
265
266 VerificationViolation,
271}
272
273#[derive(Debug)]
277pub enum RoomIdentityChange {
278 IdentityUpdates(IdentityUpdates),
280
281 SyncRoomMemberEvent(Box<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, event_factory::EventFactory};
334 use ruma::{
335 OwnedUserId, UserId, device_id, events::room::member::MembershipState, owned_user_id,
336 user_id,
337 };
338
339 use super::{IdentityState, RoomIdentityChange, RoomIdentityProvider, RoomIdentityState};
340 use crate::{
341 IdentityStatusChange, OtherUserIdentity, OtherUserIdentityData, OwnUserIdentityData,
342 UserIdentity,
343 identities::user::testing::own_identity_wrapped,
344 store::{Store, types::IdentityUpdates},
345 };
346
347 #[async_test]
348 async fn test_unpinning_a_pinned_identity_in_the_room_notifies() {
349 let user_id = user_id!("@u:s.co");
351 let mut room = FakeRoom::new();
352 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
353 let mut state = RoomIdentityState::new(room.clone()).await;
354
355 let updates =
357 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false).await;
358 let update = state.process_change(updates).await;
359
360 assert_eq!(
362 update,
363 vec![IdentityStatusChange {
364 user_id: user_id.to_owned(),
365 changed_to: IdentityState::PinViolation
366 }]
367 );
368 }
369
370 #[async_test]
371 async fn test_verifying_a_pinned_identity_in_the_room_notifies() {
372 let user_id = user_id!("@u:s.co");
374 let mut room = FakeRoom::new();
375 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
376 let mut state = RoomIdentityState::new(room.clone()).await;
377
378 let updates =
380 identity_change(&mut room, user_id, IdentityState::Verified, false, false).await;
381 let update = state.process_change(updates).await;
382
383 assert_eq!(
385 update,
386 vec![IdentityStatusChange {
387 user_id: user_id.to_owned(),
388 changed_to: IdentityState::Verified
389 }]
390 );
391 }
392
393 #[async_test]
394 async fn test_pinning_an_unpinned_identity_in_the_room_notifies() {
395 let user_id = user_id!("@u:s.co");
397 let mut room = FakeRoom::new();
398 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
399 let mut state = RoomIdentityState::new(room.clone()).await;
400
401 let updates =
403 identity_change(&mut room, user_id, IdentityState::Pinned, false, false).await;
404 let update = state.process_change(updates).await;
405
406 assert_eq!(
408 update,
409 vec![IdentityStatusChange {
410 user_id: user_id.to_owned(),
411 changed_to: IdentityState::Pinned
412 }]
413 );
414 }
415
416 #[async_test]
417 async fn test_unpinned_identity_becoming_verification_violating_in_the_room_notifies() {
418 let user_id = user_id!("@u:s.co");
420 let mut room = FakeRoom::new();
421 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
422 let mut state = RoomIdentityState::new(room.clone()).await;
423
424 let updates =
426 identity_change(&mut room, user_id, IdentityState::VerificationViolation, false, false)
427 .await;
428 let update = state.process_change(updates).await;
429
430 assert_eq!(
432 update,
433 vec![IdentityStatusChange {
434 user_id: user_id.to_owned(),
435 changed_to: IdentityState::VerificationViolation
436 }]
437 );
438 }
439
440 #[async_test]
441 async fn test_unpinning_an_identity_not_in_the_room_does_nothing() {
442 let user_id = user_id!("@u:s.co");
444 let mut room = FakeRoom::new();
445 let mut state = RoomIdentityState::new(room.clone()).await;
446
447 let updates =
450 identity_change(&mut room, user_id, IdentityState::PinViolation, true, false).await;
451 let update = state.process_change(updates).await;
452
453 assert_eq!(update, vec![]);
455 }
456
457 #[async_test]
458 async fn test_pinning_an_identity_not_in_the_room_does_nothing() {
459 let user_id = user_id!("@u:s.co");
461 let mut room = FakeRoom::new();
462 let mut state = RoomIdentityState::new(room.clone()).await;
463
464 let updates = identity_change(&mut room, user_id, IdentityState::Pinned, true, false).await;
466 let update = state.process_change(updates).await;
467
468 assert_eq!(update, []);
470 }
471
472 #[async_test]
473 async fn test_pinning_an_already_pinned_identity_in_the_room_does_nothing() {
474 let user_id = user_id!("@u:s.co");
476 let mut room = FakeRoom::new();
477 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
478 let mut state = RoomIdentityState::new(room.clone()).await;
479
480 let updates =
482 identity_change(&mut room, user_id, IdentityState::Pinned, false, false).await;
483 let update = state.process_change(updates).await;
484
485 assert_eq!(update, []);
487 }
488
489 #[async_test]
490 async fn test_unpinning_an_already_unpinned_identity_in_the_room_does_nothing() {
491 let user_id = user_id!("@u:s.co");
493 let mut room = FakeRoom::new();
494 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
495 let mut state = RoomIdentityState::new(room.clone()).await;
496
497 let updates =
499 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false).await;
500 let update = state.process_change(updates).await;
501
502 assert_eq!(update, []);
504 }
505
506 #[async_test]
507 async fn test_a_pinned_identity_joining_the_room_does_nothing() {
508 let user_id = user_id!("@u:s.co");
510 let mut room = FakeRoom::new();
511 room.non_member(other_user_identity(user_id).await, IdentityState::Pinned);
512 let mut state = RoomIdentityState::new(room.clone()).await;
513
514 let updates = room_change(user_id, MembershipState::Join);
516 let update = state.process_change(updates).await;
517
518 assert_eq!(update, []);
520 }
521
522 #[async_test]
523 async fn test_a_verified_identity_joining_the_room_does_nothing() {
524 let user_id = user_id!("@u:s.co");
526 let mut room = FakeRoom::new();
527 room.non_member(other_user_identity(user_id).await, IdentityState::Verified);
528 let mut state = RoomIdentityState::new(room).await;
529
530 let updates = room_change(user_id, MembershipState::Join);
532 let update = state.process_change(updates).await;
533
534 assert_eq!(update, []);
536 }
537
538 #[async_test]
539 async fn test_an_unpinned_identity_joining_the_room_notifies() {
540 let user_id = user_id!("@u:s.co");
542 let mut room = FakeRoom::new();
543 room.non_member(other_user_identity(user_id).await, IdentityState::PinViolation);
544 let mut state = RoomIdentityState::new(room.clone()).await;
545
546 let updates = room_change(user_id, MembershipState::Join);
548 let update = state.process_change(updates).await;
549
550 assert_eq!(
552 update,
553 vec![IdentityStatusChange {
554 user_id: user_id.to_owned(),
555 changed_to: IdentityState::PinViolation
556 }]
557 );
558 }
559
560 #[async_test]
561 async fn test_a_pinned_identity_invited_to_the_room_does_nothing() {
562 let user_id = user_id!("@u:s.co");
564 let mut room = FakeRoom::new();
565 room.non_member(other_user_identity(user_id).await, IdentityState::Pinned);
566 let mut state = RoomIdentityState::new(room.clone()).await;
567
568 let updates = room_change(user_id, MembershipState::Invite);
570 let update = state.process_change(updates).await;
571
572 assert_eq!(update, []);
574 }
575
576 #[async_test]
577 async fn test_an_unpinned_identity_invited_to_the_room_notifies() {
578 let user_id = user_id!("@u:s.co");
580 let mut room = FakeRoom::new();
581 room.non_member(other_user_identity(user_id).await, IdentityState::PinViolation);
582 let mut state = RoomIdentityState::new(room.clone()).await;
583
584 let updates = room_change(user_id, MembershipState::Invite);
586 let update = state.process_change(updates).await;
587
588 assert_eq!(
590 update,
591 vec![IdentityStatusChange {
592 user_id: user_id.to_owned(),
593 changed_to: IdentityState::PinViolation
594 }]
595 );
596 }
597
598 #[async_test]
599 async fn test_a_verification_violating_identity_invited_to_the_room_notifies() {
600 let user_id = user_id!("@u:s.co");
602 let mut room = FakeRoom::new();
603 room.non_member(other_user_identity(user_id).await, IdentityState::VerificationViolation);
604 let mut state = RoomIdentityState::new(room).await;
605
606 let updates = room_change(user_id, MembershipState::Invite);
608 let update = state.process_change(updates).await;
609
610 assert_eq!(
612 update,
613 vec![IdentityStatusChange {
614 user_id: user_id.to_owned(),
615 changed_to: IdentityState::VerificationViolation
616 }]
617 );
618 }
619
620 #[async_test]
621 async fn test_own_identity_becoming_unpinned_is_ignored() {
622 let user_id = user_id!("@u:s.co");
624 let mut room = FakeRoom::new();
625 room.member(own_user_identity(user_id).await, IdentityState::Pinned);
626 let mut state = RoomIdentityState::new(room.clone()).await;
627
628 let updates =
630 identity_change(&mut room, user_id, IdentityState::PinViolation, false, true).await;
631 let update = state.process_change(updates).await;
632
633 assert_eq!(update, vec![]);
635 }
636
637 #[async_test]
638 async fn test_own_identity_becoming_pinned_is_ignored() {
639 let user_id = user_id!("@u:s.co");
641 let mut room = FakeRoom::new();
642 room.member(own_user_identity(user_id).await, IdentityState::PinViolation);
643 let mut state = RoomIdentityState::new(room.clone()).await;
644
645 let updates = identity_change(&mut room, user_id, IdentityState::Pinned, false, true).await;
647 let update = state.process_change(updates).await;
648
649 assert_eq!(update, vec![]);
651 }
652
653 #[async_test]
654 async fn test_own_pinned_identity_joining_room_is_ignored() {
655 let user_id = user_id!("@u:s.co");
657 let mut room = FakeRoom::new();
658 room.non_member(own_user_identity(user_id).await, IdentityState::Pinned);
659 let mut state = RoomIdentityState::new(room.clone()).await;
660
661 let updates = room_change(user_id, MembershipState::Join);
663 let update = state.process_change(updates).await;
664
665 assert_eq!(update, []);
667 }
668
669 #[async_test]
670 async fn test_own_unpinned_identity_joining_room_is_ignored() {
671 let user_id = user_id!("@u:s.co");
673 let mut room = FakeRoom::new();
674 room.non_member(own_user_identity(user_id).await, IdentityState::PinViolation);
675 let mut state = RoomIdentityState::new(room.clone()).await;
676
677 let updates = room_change(user_id, MembershipState::Join);
679 let update = state.process_change(updates).await;
680
681 assert_eq!(update, vec![]);
683 }
684
685 #[async_test]
686 async fn test_a_pinned_identity_leaving_the_room_does_nothing() {
687 let user_id = user_id!("@u:s.co");
689 let mut room = FakeRoom::new();
690 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
691 let mut state = RoomIdentityState::new(room.clone()).await;
692
693 let updates = room_change(user_id, MembershipState::Leave);
695 let update = state.process_change(updates).await;
696
697 assert_eq!(update, []);
699 }
700
701 #[async_test]
702 async fn test_a_verified_identity_leaving_the_room_does_nothing() {
703 let user_id = user_id!("@u:s.co");
705 let mut room = FakeRoom::new();
706 room.member(other_user_identity(user_id).await, IdentityState::Verified);
707 let mut state = RoomIdentityState::new(room).await;
708
709 let updates = room_change(user_id, MembershipState::Leave);
711 let update = state.process_change(updates).await;
712
713 assert_eq!(update, []);
715 }
716
717 #[async_test]
718 async fn test_an_unpinned_identity_leaving_the_room_notifies() {
719 let user_id = user_id!("@u:s.co");
721 let mut room = FakeRoom::new();
722 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
723 let mut state = RoomIdentityState::new(room.clone()).await;
724
725 let updates = room_change(user_id, MembershipState::Leave);
727 let update = state.process_change(updates).await;
728
729 assert_eq!(
731 update,
732 vec![IdentityStatusChange {
733 user_id: user_id.to_owned(),
734 changed_to: IdentityState::Pinned
735 }]
736 );
737 }
738
739 #[async_test]
740 async fn test_a_verification_violating_identity_leaving_the_room_notifies() {
741 let user_id = user_id!("@u:s.co");
743 let mut room = FakeRoom::new();
744 room.member(other_user_identity(user_id).await, IdentityState::VerificationViolation);
745 let mut state = RoomIdentityState::new(room).await;
746
747 let updates = room_change(user_id, MembershipState::Leave);
749 let update = state.process_change(updates).await;
750
751 assert_eq!(
753 update,
754 vec![IdentityStatusChange {
755 user_id: user_id.to_owned(),
756 changed_to: IdentityState::Pinned
757 }]
758 );
759 }
760
761 #[async_test]
762 async fn test_a_pinned_identity_being_banned_does_nothing() {
763 let user_id = user_id!("@u:s.co");
765 let mut room = FakeRoom::new();
766 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
767 let mut state = RoomIdentityState::new(room.clone()).await;
768
769 let updates = room_change(user_id, MembershipState::Ban);
771 let update = state.process_change(updates).await;
772
773 assert_eq!(update, []);
775 }
776
777 #[async_test]
778 async fn test_an_unpinned_identity_being_banned_notifies() {
779 let user_id = user_id!("@u:s.co");
781 let mut room = FakeRoom::new();
782 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
783 let mut state = RoomIdentityState::new(room.clone()).await;
784
785 let updates = room_change(user_id, MembershipState::Ban);
787 let update = state.process_change(updates).await;
788
789 assert_eq!(
791 update,
792 vec![IdentityStatusChange {
793 user_id: user_id.to_owned(),
794 changed_to: IdentityState::Pinned
795 }]
796 );
797 }
798
799 #[async_test]
800 async fn test_multiple_simultaneous_identity_updates_are_all_notified() {
801 let user1 = user_id!("@u1:s.co");
803 let user2 = user_id!("@u2:s.co");
804 let user3 = user_id!("@u3:s.co");
805 let mut room = FakeRoom::new();
806 room.member(other_user_identity(user1).await, IdentityState::Pinned);
807 room.member(other_user_identity(user2).await, IdentityState::PinViolation);
808 room.member(other_user_identity(user3).await, IdentityState::Pinned);
809 let mut state = RoomIdentityState::new(room.clone()).await;
810
811 let updates = identity_changes(
813 &mut room,
814 &[
815 IdentityChangeSpec {
816 user_id: user1.to_owned(),
817 changed_to: IdentityState::PinViolation,
818 new: false,
819 own: false,
820 },
821 IdentityChangeSpec {
822 user_id: user2.to_owned(),
823 changed_to: IdentityState::Pinned,
824 new: false,
825 own: false,
826 },
827 IdentityChangeSpec {
828 user_id: user3.to_owned(),
829 changed_to: IdentityState::PinViolation,
830 new: false,
831 own: false,
832 },
833 ],
834 )
835 .await;
836 let update = state.process_change(updates).await;
837
838 assert_eq!(
840 update,
841 vec![
842 IdentityStatusChange {
843 user_id: user1.to_owned(),
844 changed_to: IdentityState::PinViolation
845 },
846 IdentityStatusChange {
847 user_id: user2.to_owned(),
848 changed_to: IdentityState::Pinned
849 },
850 IdentityStatusChange {
851 user_id: user3.to_owned(),
852 changed_to: IdentityState::PinViolation
853 }
854 ]
855 );
856 }
857
858 #[async_test]
859 async fn test_multiple_changes_are_notified() {
860 let user_id = user_id!("@u:s.co");
862 let mut room = FakeRoom::new();
863 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
864 let mut state = RoomIdentityState::new(room.clone()).await;
865
866 let update1 = state
868 .process_change(
869 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false)
870 .await,
871 )
872 .await;
873 let update2 = state
874 .process_change(
875 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false)
876 .await,
877 )
878 .await;
879 let update3 = state
880 .process_change(
881 identity_change(&mut room, user_id, IdentityState::Pinned, false, false).await,
882 )
883 .await;
884 let update4 = state
885 .process_change(
886 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false)
887 .await,
888 )
889 .await;
890
891 assert_eq!(
893 update1,
894 vec![IdentityStatusChange {
895 user_id: user_id.to_owned(),
896 changed_to: IdentityState::PinViolation
897 }]
898 );
899 assert_eq!(update2, vec![]);
901 assert_eq!(
902 update3,
903 vec![IdentityStatusChange {
904 user_id: user_id.to_owned(),
905 changed_to: IdentityState::Pinned
906 }]
907 );
908 assert_eq!(
909 update4,
910 vec![IdentityStatusChange {
911 user_id: user_id.to_owned(),
912 changed_to: IdentityState::PinViolation
913 }]
914 );
915 }
916
917 #[async_test]
918 async fn test_current_state_of_all_pinned_room_is_empty() {
919 let user1 = user_id!("@u1:s.co");
921 let user2 = user_id!("@u2:s.co");
922 let mut room = FakeRoom::new();
923 room.member(other_user_identity(user1).await, IdentityState::Pinned);
924 room.member(other_user_identity(user2).await, IdentityState::Pinned);
925 let state = RoomIdentityState::new(room).await;
926 assert!(state.current_state().is_empty());
927 }
928
929 #[async_test]
930 async fn test_current_state_contains_all_nonpinned_users() {
931 let user1 = user_id!("@u1:s.co");
933 let user2 = user_id!("@u2:s.co");
934 let user3 = user_id!("@u3:s.co");
935 let user4 = user_id!("@u4:s.co");
936 let user5 = user_id!("@u5:s.co");
937 let user6 = user_id!("@u6:s.co");
938 let mut room = FakeRoom::new();
939 room.member(other_user_identity(user1).await, IdentityState::Pinned);
940 room.member(other_user_identity(user2).await, IdentityState::PinViolation);
941 room.member(other_user_identity(user3).await, IdentityState::Pinned);
942 room.member(other_user_identity(user4).await, IdentityState::PinViolation);
943 room.member(other_user_identity(user5).await, IdentityState::Verified);
944 room.member(other_user_identity(user6).await, IdentityState::VerificationViolation);
945 let mut state = RoomIdentityState::new(room).await.current_state();
946 state.sort_by_key(|change| change.user_id.to_owned());
947 assert_eq!(
948 state,
949 vec![
950 IdentityStatusChange {
951 user_id: owned_user_id!("@u2:s.co"),
952 changed_to: IdentityState::PinViolation
953 },
954 IdentityStatusChange {
955 user_id: owned_user_id!("@u4:s.co"),
956 changed_to: IdentityState::PinViolation
957 },
958 IdentityStatusChange {
959 user_id: owned_user_id!("@u5:s.co"),
960 changed_to: IdentityState::Verified
961 },
962 IdentityStatusChange {
963 user_id: owned_user_id!("@u6:s.co"),
964 changed_to: IdentityState::VerificationViolation
965 }
966 ]
967 );
968 }
969
970 #[derive(Debug)]
971 struct Membership {
972 is_member: bool,
973 user_identity: UserIdentity,
974 identity_state: IdentityState,
975 }
976
977 #[derive(Clone, Debug)]
978 struct FakeRoom {
979 users: Arc<Mutex<HashMap<OwnedUserId, Membership>>>,
980 }
981
982 impl FakeRoom {
983 fn new() -> Self {
984 Self { users: Default::default() }
985 }
986
987 fn member(&mut self, user_identity: UserIdentity, identity_state: IdentityState) {
988 self.users.lock().unwrap().insert(
989 user_identity.user_id().to_owned(),
990 Membership { is_member: true, user_identity, identity_state },
991 );
992 }
993
994 fn non_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: false, user_identity, identity_state },
998 );
999 }
1000
1001 fn update_state(&self, user_id: &UserId, changed_to: &IdentityState) {
1002 self.users
1003 .lock()
1004 .unwrap()
1005 .entry(user_id.to_owned())
1006 .and_modify(|m| m.identity_state = changed_to.clone());
1007 }
1008 }
1009
1010 impl RoomIdentityProvider for FakeRoom {
1011 fn is_member<'a>(&'a self, user_id: &'a UserId) -> BoxFuture<'a, bool> {
1012 Box::pin(async {
1013 self.users.lock().unwrap().get(user_id).map(|m| m.is_member).unwrap_or(false)
1014 })
1015 }
1016
1017 fn member_identities(&self) -> BoxFuture<'_, Vec<UserIdentity>> {
1018 Box::pin(async {
1019 self.users
1020 .lock()
1021 .unwrap()
1022 .values()
1023 .filter_map(|m| if m.is_member { Some(m.user_identity.clone()) } else { None })
1024 .collect()
1025 })
1026 }
1027
1028 fn user_identity<'a>(&'a self, user_id: &'a UserId) -> BoxFuture<'a, Option<UserIdentity>> {
1029 Box::pin(async {
1030 self.users.lock().unwrap().get(user_id).map(|m| m.user_identity.clone())
1031 })
1032 }
1033
1034 fn state_of(&self, user_identity: &UserIdentity) -> IdentityState {
1035 self.users
1036 .lock()
1037 .unwrap()
1038 .get(user_identity.user_id())
1039 .map(|m| m.identity_state.clone())
1040 .unwrap_or(IdentityState::Pinned)
1041 }
1042 }
1043
1044 fn room_change(user_id: &UserId, new_state: MembershipState) -> RoomIdentityChange {
1045 let event = EventFactory::new()
1046 .sender(user_id!("@admin:b.c"))
1047 .member(user_id)
1048 .membership(new_state)
1049 .into();
1050 RoomIdentityChange::SyncRoomMemberEvent(Box::new(event))
1051 }
1052
1053 async fn identity_change(
1054 room: &mut FakeRoom,
1055 user_id: &UserId,
1056 changed_to: IdentityState,
1057 new: bool,
1058 own: bool,
1059 ) -> RoomIdentityChange {
1060 identity_changes(
1061 room,
1062 &[IdentityChangeSpec { user_id: user_id.to_owned(), changed_to, new, own }],
1063 )
1064 .await
1065 }
1066
1067 struct IdentityChangeSpec {
1068 user_id: OwnedUserId,
1069 changed_to: IdentityState,
1070 new: bool,
1071 own: bool,
1072 }
1073
1074 async fn identity_changes(
1075 room: &mut FakeRoom,
1076 changes: &[IdentityChangeSpec],
1077 ) -> RoomIdentityChange {
1078 let mut updates = IdentityUpdates::default();
1079
1080 for change in changes {
1081 let user_identity = if change.own {
1082 own_user_identity(&change.user_id).await
1083 } else {
1084 other_user_identity(&change.user_id).await
1085 };
1086
1087 room.update_state(user_identity.user_id(), &change.changed_to);
1088 if change.new {
1089 updates.new.insert(user_identity.user_id().to_owned(), user_identity);
1090 } else {
1091 updates.changed.insert(user_identity.user_id().to_owned(), user_identity);
1092 }
1093 }
1094 RoomIdentityChange::IdentityUpdates(updates)
1095 }
1096
1097 async fn other_user_identity(user_id: &UserId) -> UserIdentity {
1099 use std::sync::Arc;
1100
1101 use ruma::owned_device_id;
1102 use tokio::sync::Mutex;
1103
1104 use crate::{
1105 Account,
1106 olm::PrivateCrossSigningIdentity,
1107 store::{CryptoStoreWrapper, MemoryStore},
1108 verification::VerificationMachine,
1109 };
1110
1111 let device_id = owned_device_id!("DEV123");
1112 let account = Account::with_device_id(user_id, &device_id);
1113
1114 let private_identity = Arc::new(Mutex::new(
1115 PrivateCrossSigningIdentity::for_account(
1116 &account,
1117 #[cfg(feature = "experimental-x509-identity-verification")]
1118 None,
1119 )
1120 .await
1121 .unwrap(),
1122 ));
1123
1124 let other_user_identity_data =
1125 OtherUserIdentityData::from_private(&*private_identity.lock().await).await;
1126
1127 UserIdentity::Other(OtherUserIdentity {
1128 inner: other_user_identity_data,
1129 own_identity: None,
1130 verification_machine: VerificationMachine::new(
1131 account.clone(),
1132 Arc::new(Mutex::new(PrivateCrossSigningIdentity::new(
1133 account.user_id().to_owned(),
1134 ))),
1135 Arc::new(CryptoStoreWrapper::new(
1136 account.user_id(),
1137 account.device_id(),
1138 MemoryStore::new(),
1139 )),
1140 ),
1141 #[cfg(feature = "experimental-x509-identity-verification")]
1142 x509_verifier: None,
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 Account,
1155 olm::PrivateCrossSigningIdentity,
1156 store::{CryptoStoreWrapper, MemoryStore},
1157 verification::VerificationMachine,
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 = Arc::new(Mutex::new(
1164 PrivateCrossSigningIdentity::for_account(
1165 &account,
1166 #[cfg(feature = "experimental-x509-identity-verification")]
1167 None,
1168 )
1169 .await
1170 .unwrap(),
1171 ));
1172
1173 let own_user_identity_data =
1174 OwnUserIdentityData::from_private(&*private_identity.lock().await).await;
1175
1176 let cross_signing_identity = PrivateCrossSigningIdentity::new(account.user_id().to_owned());
1177 let verification_machine = VerificationMachine::new(
1178 account.clone(),
1179 Arc::new(Mutex::new(cross_signing_identity.clone())),
1180 Arc::new(CryptoStoreWrapper::new(
1181 account.user_id(),
1182 account.device_id(),
1183 MemoryStore::new(),
1184 )),
1185 );
1186
1187 UserIdentity::Own(own_identity_wrapped(
1188 own_user_identity_data,
1189 verification_machine.clone(),
1190 Store::new(
1191 account.static_data().clone(),
1192 Arc::new(Mutex::new(cross_signing_identity)),
1193 Arc::new(CryptoStoreWrapper::new(
1194 user_id!("@u:s.co"),
1195 device_id!("DEV7"),
1196 MemoryStore::new(),
1197 )),
1198 verification_machine,
1199 ),
1200 ))
1201 }
1202}