1use std::{collections::HashMap, ops::Deref};
16
17use matrix_sdk_common::BoxFuture;
18use ruma::{
19 events::{
20 room::member::{MembershipState, SyncRoomMemberEvent},
21 SyncStateEvent,
22 },
23 OwnedUserId, UserId,
24};
25
26use super::UserIdentity;
27use crate::store::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: Result<&UserId, _> = event.state_key.as_str().try_into();
144 if let Ok(user_id) = user_id {
145 if let Some(user_identity @ UserIdentity::Other(_)) =
147 self.room.user_identity(user_id).await
148 {
149 if matches!(
151 self.room.state_of(&user_identity),
152 IdentityState::Verified | IdentityState::Pinned
153 ) {
154 return vec![];
155 }
156
157 match event.content.membership {
158 MembershipState::Join | MembershipState::Invite => {
159 if let Some(update) = self.update_user_state(user_id, &user_identity) {
162 return vec![update];
163 }
164 }
165 MembershipState::Leave | MembershipState::Ban => {
166 if let Some(update) =
171 self.update_user_state_to(user_id, IdentityState::Pinned)
172 {
173 return vec![update];
174 }
175 }
176 MembershipState::Knock => {
177 }
179 _ => {}
180 }
181 }
182 }
183 }
184
185 vec![]
187 }
188
189 fn update_user_state(
190 &mut self,
191 user_id: &UserId,
192 user_identity: &UserIdentity,
193 ) -> Option<IdentityStatusChange> {
194 if let UserIdentity::Other(_) = &user_identity {
195 self.update_user_state_to(user_id, self.room.state_of(user_identity))
196 } else {
197 None
199 }
200 }
201
202 fn update_user_state_to(
206 &mut self,
207 user_id: &UserId,
208 new_state: IdentityState,
209 ) -> Option<IdentityStatusChange> {
210 let old_state = self.known_states.get(user_id);
211
212 if old_state == new_state {
213 return None;
214 }
215
216 Some(self.set_state(user_id, new_state))
217 }
218
219 fn set_state(&mut self, user_id: &UserId, new_state: IdentityState) -> IdentityStatusChange {
220 self.known_states.set(user_id, &new_state);
222
223 IdentityStatusChange { user_id: user_id.to_owned(), changed_to: new_state }
225 }
226}
227
228#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
240pub struct IdentityStatusChange {
241 pub user_id: OwnedUserId,
243
244 pub changed_to: IdentityState,
246}
247
248#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
250#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
251pub enum IdentityState {
252 Verified,
254
255 Pinned,
259
260 PinViolation,
266
267 VerificationViolation,
272}
273
274#[derive(Debug)]
278pub enum RoomIdentityChange {
279 IdentityUpdates(IdentityUpdates),
281
282 SyncRoomMemberEvent(Box<SyncRoomMemberEvent>),
285}
286
287#[derive(Debug)]
290struct KnownStates {
291 known_states: HashMap<OwnedUserId, IdentityState>,
292}
293
294impl KnownStates {
295 fn from_identities(
296 member_identities: impl IntoIterator<Item = UserIdentity>,
297 room: &dyn RoomIdentityProvider,
298 ) -> Self {
299 let mut known_states = HashMap::new();
300 for user_identity in member_identities {
301 let state = room.state_of(&user_identity);
302 if state != IdentityState::Pinned {
303 known_states.insert(user_identity.user_id().to_owned(), state);
304 }
305 }
306 Self { known_states }
307 }
308
309 fn get(&self, user_id: &UserId) -> IdentityState {
312 self.known_states.get(user_id).cloned().unwrap_or(IdentityState::Pinned)
313 }
314
315 fn set(&mut self, user_id: &UserId, identity_state: &IdentityState) {
318 if let IdentityState::Pinned = identity_state {
319 self.known_states.remove(user_id);
320 } else {
321 self.known_states.insert(user_id.to_owned(), identity_state.clone());
322 }
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use std::{
329 collections::HashMap,
330 sync::{Arc, Mutex},
331 };
332
333 use matrix_sdk_common::BoxFuture;
334 use matrix_sdk_test::{async_test, event_factory::EventFactory};
335 use ruma::{
336 device_id, events::room::member::MembershipState, owned_user_id, user_id, OwnedUserId,
337 UserId,
338 };
339
340 use super::{IdentityState, RoomIdentityChange, RoomIdentityProvider, RoomIdentityState};
341 use crate::{
342 identities::user::testing::own_identity_wrapped,
343 store::{types::IdentityUpdates, Store},
344 IdentityStatusChange, OtherUserIdentity, OtherUserIdentityData, OwnUserIdentityData,
345 UserIdentity,
346 };
347
348 #[async_test]
349 async fn test_unpinning_a_pinned_identity_in_the_room_notifies() {
350 let user_id = user_id!("@u:s.co");
352 let mut room = FakeRoom::new();
353 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
354 let mut state = RoomIdentityState::new(room.clone()).await;
355
356 let updates =
358 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false).await;
359 let update = state.process_change(updates).await;
360
361 assert_eq!(
363 update,
364 vec![IdentityStatusChange {
365 user_id: user_id.to_owned(),
366 changed_to: IdentityState::PinViolation
367 }]
368 );
369 }
370
371 #[async_test]
372 async fn test_verifying_a_pinned_identity_in_the_room_notifies() {
373 let user_id = user_id!("@u:s.co");
375 let mut room = FakeRoom::new();
376 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
377 let mut state = RoomIdentityState::new(room.clone()).await;
378
379 let updates =
381 identity_change(&mut room, user_id, IdentityState::Verified, false, false).await;
382 let update = state.process_change(updates).await;
383
384 assert_eq!(
386 update,
387 vec![IdentityStatusChange {
388 user_id: user_id.to_owned(),
389 changed_to: IdentityState::Verified
390 }]
391 );
392 }
393
394 #[async_test]
395 async fn test_pinning_an_unpinned_identity_in_the_room_notifies() {
396 let user_id = user_id!("@u:s.co");
398 let mut room = FakeRoom::new();
399 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
400 let mut state = RoomIdentityState::new(room.clone()).await;
401
402 let updates =
404 identity_change(&mut room, user_id, IdentityState::Pinned, false, false).await;
405 let update = state.process_change(updates).await;
406
407 assert_eq!(
409 update,
410 vec![IdentityStatusChange {
411 user_id: user_id.to_owned(),
412 changed_to: IdentityState::Pinned
413 }]
414 );
415 }
416
417 #[async_test]
418 async fn test_unpinned_identity_becoming_verification_violating_in_the_room_notifies() {
419 let user_id = user_id!("@u:s.co");
421 let mut room = FakeRoom::new();
422 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
423 let mut state = RoomIdentityState::new(room.clone()).await;
424
425 let updates =
427 identity_change(&mut room, user_id, IdentityState::VerificationViolation, false, false)
428 .await;
429 let update = state.process_change(updates).await;
430
431 assert_eq!(
433 update,
434 vec![IdentityStatusChange {
435 user_id: user_id.to_owned(),
436 changed_to: IdentityState::VerificationViolation
437 }]
438 );
439 }
440
441 #[async_test]
442 async fn test_unpinning_an_identity_not_in_the_room_does_nothing() {
443 let user_id = user_id!("@u:s.co");
445 let mut room = FakeRoom::new();
446 let mut state = RoomIdentityState::new(room.clone()).await;
447
448 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 olm::PrivateCrossSigningIdentity,
1106 store::{CryptoStoreWrapper, MemoryStore},
1107 verification::VerificationMachine,
1108 Account,
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 =
1115 Arc::new(Mutex::new(PrivateCrossSigningIdentity::for_account(&account)));
1116
1117 let other_user_identity_data =
1118 OtherUserIdentityData::from_private(&*private_identity.lock().await).await;
1119
1120 UserIdentity::Other(OtherUserIdentity {
1121 inner: other_user_identity_data,
1122 own_identity: None,
1123 verification_machine: VerificationMachine::new(
1124 account.clone(),
1125 Arc::new(Mutex::new(PrivateCrossSigningIdentity::new(
1126 account.user_id().to_owned(),
1127 ))),
1128 Arc::new(CryptoStoreWrapper::new(
1129 account.user_id(),
1130 account.device_id(),
1131 MemoryStore::new(),
1132 )),
1133 ),
1134 })
1135 }
1136
1137 async fn own_user_identity(user_id: &UserId) -> UserIdentity {
1139 use std::sync::Arc;
1140
1141 use ruma::owned_device_id;
1142 use tokio::sync::Mutex;
1143
1144 use crate::{
1145 olm::PrivateCrossSigningIdentity,
1146 store::{CryptoStoreWrapper, MemoryStore},
1147 verification::VerificationMachine,
1148 Account,
1149 };
1150
1151 let device_id = owned_device_id!("DEV123");
1152 let account = Account::with_device_id(user_id, &device_id);
1153
1154 let private_identity =
1155 Arc::new(Mutex::new(PrivateCrossSigningIdentity::for_account(&account)));
1156
1157 let own_user_identity_data =
1158 OwnUserIdentityData::from_private(&*private_identity.lock().await).await;
1159
1160 let cross_signing_identity = PrivateCrossSigningIdentity::new(account.user_id().to_owned());
1161 let verification_machine = VerificationMachine::new(
1162 account.clone(),
1163 Arc::new(Mutex::new(cross_signing_identity.clone())),
1164 Arc::new(CryptoStoreWrapper::new(
1165 account.user_id(),
1166 account.device_id(),
1167 MemoryStore::new(),
1168 )),
1169 );
1170
1171 UserIdentity::Own(own_identity_wrapped(
1172 own_user_identity_data,
1173 verification_machine.clone(),
1174 Store::new(
1175 account.static_data().clone(),
1176 Arc::new(Mutex::new(cross_signing_identity)),
1177 Arc::new(CryptoStoreWrapper::new(
1178 user_id!("@u:s.co"),
1179 device_id!("DEV7"),
1180 MemoryStore::new(),
1181 )),
1182 verification_machine,
1183 ),
1184 ))
1185 }
1186}