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 match event.content.membership {
150 MembershipState::Join | MembershipState::Invite => {
151 if let Some(update) = self.update_user_state(user_id, &user_identity) {
154 return vec![update];
155 }
156 }
157 MembershipState::Leave | MembershipState::Ban => {
158 if let Some(update) =
163 self.update_user_state_to(user_id, IdentityState::Pinned)
164 {
165 return vec![update];
166 }
167 }
168 MembershipState::Knock => {
169 }
171 _ => {}
172 }
173 }
174 }
175 }
176
177 vec![]
179 }
180
181 fn update_user_state(
182 &mut self,
183 user_id: &UserId,
184 user_identity: &UserIdentity,
185 ) -> Option<IdentityStatusChange> {
186 if let UserIdentity::Other(_) = &user_identity {
187 self.update_user_state_to(user_id, self.room.state_of(user_identity))
188 } else {
189 None
191 }
192 }
193
194 fn update_user_state_to(
199 &mut self,
200 user_id: &UserId,
201 new_state: IdentityState,
202 ) -> Option<IdentityStatusChange> {
203 use IdentityState::*;
204
205 let old_state = self.known_states.get(user_id);
206
207 match (old_state, &new_state) {
208 (Pinned, PinViolation) |
210 (Pinned, VerificationViolation) |
211 (Verified, PinViolation) |
212 (Verified, VerificationViolation) |
213
214 (PinViolation, Pinned) |
216 (PinViolation, Verified) |
217 (VerificationViolation, Pinned) |
218 (VerificationViolation, Verified) |
219
220 (PinViolation, VerificationViolation) |
222 (VerificationViolation, PinViolation) => Some(self.set_state(user_id, new_state)),
223
224 (Pinned, Verified) |
226 (Verified, Pinned) => {
227 self.set_state(user_id, new_state);
229 None
231 }
232
233 (Pinned, Pinned) |
235 (Verified, Verified) |
236 (PinViolation, PinViolation) |
237 (VerificationViolation, VerificationViolation) => None,
238 }
239 }
240
241 fn set_state(&mut self, user_id: &UserId, new_state: IdentityState) -> IdentityStatusChange {
242 self.known_states.set(user_id, &new_state);
244
245 IdentityStatusChange { user_id: user_id.to_owned(), changed_to: new_state }
247 }
248}
249
250#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
262pub struct IdentityStatusChange {
263 pub user_id: OwnedUserId,
265
266 pub changed_to: IdentityState,
268}
269
270#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
272#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
273pub enum IdentityState {
274 Verified,
276
277 Pinned,
281
282 PinViolation,
288
289 VerificationViolation,
294}
295
296#[derive(Debug)]
300pub enum RoomIdentityChange {
301 IdentityUpdates(IdentityUpdates),
303
304 SyncRoomMemberEvent(SyncRoomMemberEvent),
306}
307
308#[derive(Debug)]
311struct KnownStates {
312 known_states: HashMap<OwnedUserId, IdentityState>,
313}
314
315impl KnownStates {
316 fn from_identities(
317 member_identities: impl IntoIterator<Item = UserIdentity>,
318 room: &dyn RoomIdentityProvider,
319 ) -> Self {
320 let mut known_states = HashMap::new();
321 for user_identity in member_identities {
322 let state = room.state_of(&user_identity);
323 if state != IdentityState::Pinned {
324 known_states.insert(user_identity.user_id().to_owned(), state);
325 }
326 }
327 Self { known_states }
328 }
329
330 fn get(&self, user_id: &UserId) -> IdentityState {
333 self.known_states.get(user_id).cloned().unwrap_or(IdentityState::Pinned)
334 }
335
336 fn set(&mut self, user_id: &UserId, identity_state: &IdentityState) {
339 if let IdentityState::Pinned = identity_state {
340 self.known_states.remove(user_id);
341 } else {
342 self.known_states.insert(user_id.to_owned(), identity_state.clone());
343 }
344 }
345}
346
347#[cfg(test)]
348mod tests {
349 use std::{
350 collections::HashMap,
351 sync::{Arc, Mutex},
352 };
353
354 use matrix_sdk_common::BoxFuture;
355 use matrix_sdk_test::async_test;
356 use ruma::{
357 device_id,
358 events::{
359 room::member::{
360 MembershipState, RoomMemberEventContent, RoomMemberUnsigned, SyncRoomMemberEvent,
361 },
362 OriginalSyncStateEvent,
363 },
364 owned_event_id, owned_user_id, user_id, MilliSecondsSinceUnixEpoch, OwnedUserId, UInt,
365 UserId,
366 };
367
368 use super::{IdentityState, RoomIdentityChange, RoomIdentityProvider, RoomIdentityState};
369 use crate::{
370 identities::user::testing::own_identity_wrapped,
371 store::{IdentityUpdates, Store},
372 IdentityStatusChange, OtherUserIdentity, OtherUserIdentityData, OwnUserIdentityData,
373 UserIdentity,
374 };
375
376 #[async_test]
377 async fn test_unpinning_a_pinned_identity_in_the_room_notifies() {
378 let user_id = user_id!("@u:s.co");
380 let mut room = FakeRoom::new();
381 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
382 let mut state = RoomIdentityState::new(room.clone()).await;
383
384 let updates =
386 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false).await;
387 let update = state.process_change(updates).await;
388
389 assert_eq!(
391 update,
392 vec![IdentityStatusChange {
393 user_id: user_id.to_owned(),
394 changed_to: IdentityState::PinViolation
395 }]
396 );
397 }
398
399 #[async_test]
400 async fn test_verifying_a_pinned_identity_in_the_room_does_nothing() {
401 let user_id = user_id!("@u:s.co");
403 let mut room = FakeRoom::new();
404 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
405 let mut state = RoomIdentityState::new(room.clone()).await;
406
407 let updates =
409 identity_change(&mut room, user_id, IdentityState::Verified, false, false).await;
410 let update = state.process_change(updates).await;
411
412 assert_eq!(update, vec![]);
414 }
415
416 #[async_test]
417 async fn test_pinning_an_unpinned_identity_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::Pinned, false, false).await;
427 let update = state.process_change(updates).await;
428
429 assert_eq!(
431 update,
432 vec![IdentityStatusChange {
433 user_id: user_id.to_owned(),
434 changed_to: IdentityState::Pinned
435 }]
436 );
437 }
438
439 #[async_test]
440 async fn test_unpinned_identity_becoming_verification_violating_in_the_room_notifies() {
441 let user_id = user_id!("@u:s.co");
443 let mut room = FakeRoom::new();
444 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
445 let mut state = RoomIdentityState::new(room.clone()).await;
446
447 let updates =
449 identity_change(&mut room, user_id, IdentityState::VerificationViolation, false, false)
450 .await;
451 let update = state.process_change(updates).await;
452
453 assert_eq!(
455 update,
456 vec![IdentityStatusChange {
457 user_id: user_id.to_owned(),
458 changed_to: IdentityState::VerificationViolation
459 }]
460 );
461 }
462
463 #[async_test]
464 async fn test_unpinning_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 =
472 identity_change(&mut room, user_id, IdentityState::PinViolation, true, false).await;
473 let update = state.process_change(updates).await;
474
475 assert_eq!(update, vec![]);
477 }
478
479 #[async_test]
480 async fn test_pinning_an_identity_not_in_the_room_does_nothing() {
481 let user_id = user_id!("@u:s.co");
483 let mut room = FakeRoom::new();
484 let mut state = RoomIdentityState::new(room.clone()).await;
485
486 let updates = identity_change(&mut room, user_id, IdentityState::Pinned, true, false).await;
488 let update = state.process_change(updates).await;
489
490 assert_eq!(update, []);
492 }
493
494 #[async_test]
495 async fn test_pinning_an_already_pinned_identity_in_the_room_does_nothing() {
496 let user_id = user_id!("@u:s.co");
498 let mut room = FakeRoom::new();
499 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
500 let mut state = RoomIdentityState::new(room.clone()).await;
501
502 let updates =
504 identity_change(&mut room, user_id, IdentityState::Pinned, false, false).await;
505 let update = state.process_change(updates).await;
506
507 assert_eq!(update, []);
509 }
510
511 #[async_test]
512 async fn test_unpinning_an_already_unpinned_identity_in_the_room_does_nothing() {
513 let user_id = user_id!("@u:s.co");
515 let mut room = FakeRoom::new();
516 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
517 let mut state = RoomIdentityState::new(room.clone()).await;
518
519 let updates =
521 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false).await;
522 let update = state.process_change(updates).await;
523
524 assert_eq!(update, []);
526 }
527
528 #[async_test]
529 async fn test_a_pinned_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::Pinned);
534 let mut state = RoomIdentityState::new(room.clone()).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_a_verified_identity_joining_the_room_does_nothing() {
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::Verified);
550 let mut state = RoomIdentityState::new(room).await;
551
552 let updates = room_change(user_id, MembershipState::Join);
554 let update = state.process_change(updates).await;
555
556 assert_eq!(update, []);
558 }
559
560 #[async_test]
561 async fn test_an_unpinned_identity_joining_the_room_notifies() {
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::PinViolation);
566 let mut state = RoomIdentityState::new(room.clone()).await;
567
568 let updates = room_change(user_id, MembershipState::Join);
570 let update = state.process_change(updates).await;
571
572 assert_eq!(
574 update,
575 vec![IdentityStatusChange {
576 user_id: user_id.to_owned(),
577 changed_to: IdentityState::PinViolation
578 }]
579 );
580 }
581
582 #[async_test]
583 async fn test_a_pinned_identity_invited_to_the_room_does_nothing() {
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::Pinned);
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!(update, []);
596 }
597
598 #[async_test]
599 async fn test_an_unpinned_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::PinViolation);
604 let mut state = RoomIdentityState::new(room.clone()).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::PinViolation
616 }]
617 );
618 }
619
620 #[async_test]
621 async fn test_a_verification_violating_identity_invited_to_the_room_notifies() {
622 let user_id = user_id!("@u:s.co");
624 let mut room = FakeRoom::new();
625 room.non_member(other_user_identity(user_id).await, IdentityState::VerificationViolation);
626 let mut state = RoomIdentityState::new(room).await;
627
628 let updates = room_change(user_id, MembershipState::Invite);
630 let update = state.process_change(updates).await;
631
632 assert_eq!(
634 update,
635 vec![IdentityStatusChange {
636 user_id: user_id.to_owned(),
637 changed_to: IdentityState::VerificationViolation
638 }]
639 );
640 }
641
642 #[async_test]
643 async fn test_own_identity_becoming_unpinned_is_ignored() {
644 let user_id = user_id!("@u:s.co");
646 let mut room = FakeRoom::new();
647 room.member(own_user_identity(user_id).await, IdentityState::Pinned);
648 let mut state = RoomIdentityState::new(room.clone()).await;
649
650 let updates =
652 identity_change(&mut room, user_id, IdentityState::PinViolation, 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_identity_becoming_pinned_is_ignored() {
661 let user_id = user_id!("@u:s.co");
663 let mut room = FakeRoom::new();
664 room.member(own_user_identity(user_id).await, IdentityState::PinViolation);
665 let mut state = RoomIdentityState::new(room.clone()).await;
666
667 let updates = identity_change(&mut room, user_id, IdentityState::Pinned, false, true).await;
669 let update = state.process_change(updates).await;
670
671 assert_eq!(update, vec![]);
673 }
674
675 #[async_test]
676 async fn test_own_pinned_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::Pinned);
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, []);
689 }
690
691 #[async_test]
692 async fn test_own_unpinned_identity_joining_room_is_ignored() {
693 let user_id = user_id!("@u:s.co");
695 let mut room = FakeRoom::new();
696 room.non_member(own_user_identity(user_id).await, IdentityState::PinViolation);
697 let mut state = RoomIdentityState::new(room.clone()).await;
698
699 let updates = room_change(user_id, MembershipState::Join);
701 let update = state.process_change(updates).await;
702
703 assert_eq!(update, vec![]);
705 }
706
707 #[async_test]
708 async fn test_a_pinned_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::Pinned);
713 let mut state = RoomIdentityState::new(room.clone()).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_a_verified_identity_leaving_the_room_does_nothing() {
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::Verified);
729 let mut state = RoomIdentityState::new(room).await;
730
731 let updates = room_change(user_id, MembershipState::Leave);
733 let update = state.process_change(updates).await;
734
735 assert_eq!(update, []);
737 }
738
739 #[async_test]
740 async fn test_an_unpinned_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::PinViolation);
745 let mut state = RoomIdentityState::new(room.clone()).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_verification_violating_identity_leaving_the_room_notifies() {
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::VerificationViolation);
767 let mut state = RoomIdentityState::new(room).await;
768
769 let updates = room_change(user_id, MembershipState::Leave);
771 let update = state.process_change(updates).await;
772
773 assert_eq!(
775 update,
776 vec![IdentityStatusChange {
777 user_id: user_id.to_owned(),
778 changed_to: IdentityState::Pinned
779 }]
780 );
781 }
782
783 #[async_test]
784 async fn test_a_pinned_identity_being_banned_does_nothing() {
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::Pinned);
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!(update, []);
797 }
798
799 #[async_test]
800 async fn test_an_unpinned_identity_being_banned_notifies() {
801 let user_id = user_id!("@u:s.co");
803 let mut room = FakeRoom::new();
804 room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
805 let mut state = RoomIdentityState::new(room.clone()).await;
806
807 let updates = room_change(user_id, MembershipState::Ban);
809 let update = state.process_change(updates).await;
810
811 assert_eq!(
813 update,
814 vec![IdentityStatusChange {
815 user_id: user_id.to_owned(),
816 changed_to: IdentityState::Pinned
817 }]
818 );
819 }
820
821 #[async_test]
822 async fn test_multiple_simultaneous_identity_updates_are_all_notified() {
823 let user1 = user_id!("@u1:s.co");
825 let user2 = user_id!("@u2:s.co");
826 let user3 = user_id!("@u3:s.co");
827 let mut room = FakeRoom::new();
828 room.member(other_user_identity(user1).await, IdentityState::Pinned);
829 room.member(other_user_identity(user2).await, IdentityState::PinViolation);
830 room.member(other_user_identity(user3).await, IdentityState::Pinned);
831 let mut state = RoomIdentityState::new(room.clone()).await;
832
833 let updates = identity_changes(
835 &mut room,
836 &[
837 IdentityChangeSpec {
838 user_id: user1.to_owned(),
839 changed_to: IdentityState::PinViolation,
840 new: false,
841 own: false,
842 },
843 IdentityChangeSpec {
844 user_id: user2.to_owned(),
845 changed_to: IdentityState::Pinned,
846 new: false,
847 own: false,
848 },
849 IdentityChangeSpec {
850 user_id: user3.to_owned(),
851 changed_to: IdentityState::PinViolation,
852 new: false,
853 own: false,
854 },
855 ],
856 )
857 .await;
858 let update = state.process_change(updates).await;
859
860 assert_eq!(
862 update,
863 vec![
864 IdentityStatusChange {
865 user_id: user1.to_owned(),
866 changed_to: IdentityState::PinViolation
867 },
868 IdentityStatusChange {
869 user_id: user2.to_owned(),
870 changed_to: IdentityState::Pinned
871 },
872 IdentityStatusChange {
873 user_id: user3.to_owned(),
874 changed_to: IdentityState::PinViolation
875 }
876 ]
877 );
878 }
879
880 #[async_test]
881 async fn test_multiple_changes_are_notified() {
882 let user_id = user_id!("@u:s.co");
884 let mut room = FakeRoom::new();
885 room.member(other_user_identity(user_id).await, IdentityState::Pinned);
886 let mut state = RoomIdentityState::new(room.clone()).await;
887
888 let update1 = state
890 .process_change(
891 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false)
892 .await,
893 )
894 .await;
895 let update2 = state
896 .process_change(
897 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false)
898 .await,
899 )
900 .await;
901 let update3 = state
902 .process_change(
903 identity_change(&mut room, user_id, IdentityState::Pinned, false, false).await,
904 )
905 .await;
906 let update4 = state
907 .process_change(
908 identity_change(&mut room, user_id, IdentityState::PinViolation, false, false)
909 .await,
910 )
911 .await;
912
913 assert_eq!(
915 update1,
916 vec![IdentityStatusChange {
917 user_id: user_id.to_owned(),
918 changed_to: IdentityState::PinViolation
919 }]
920 );
921 assert_eq!(update2, vec![]);
923 assert_eq!(
924 update3,
925 vec![IdentityStatusChange {
926 user_id: user_id.to_owned(),
927 changed_to: IdentityState::Pinned
928 }]
929 );
930 assert_eq!(
931 update4,
932 vec![IdentityStatusChange {
933 user_id: user_id.to_owned(),
934 changed_to: IdentityState::PinViolation
935 }]
936 );
937 }
938
939 #[async_test]
940 async fn test_current_state_of_all_pinned_room_is_empty() {
941 let user1 = user_id!("@u1:s.co");
943 let user2 = user_id!("@u2: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::Pinned);
947 let state = RoomIdentityState::new(room).await;
948 assert!(state.current_state().is_empty());
949 }
950
951 #[async_test]
952 async fn test_current_state_contains_all_nonpinned_users() {
953 let user1 = user_id!("@u1:s.co");
955 let user2 = user_id!("@u2:s.co");
956 let user3 = user_id!("@u3:s.co");
957 let user4 = user_id!("@u4:s.co");
958 let user5 = user_id!("@u5:s.co");
959 let user6 = user_id!("@u6:s.co");
960 let mut room = FakeRoom::new();
961 room.member(other_user_identity(user1).await, IdentityState::Pinned);
962 room.member(other_user_identity(user2).await, IdentityState::PinViolation);
963 room.member(other_user_identity(user3).await, IdentityState::Pinned);
964 room.member(other_user_identity(user4).await, IdentityState::PinViolation);
965 room.member(other_user_identity(user5).await, IdentityState::Verified);
966 room.member(other_user_identity(user6).await, IdentityState::VerificationViolation);
967 let mut state = RoomIdentityState::new(room).await.current_state();
968 state.sort_by_key(|change| change.user_id.to_owned());
969 assert_eq!(
970 state,
971 vec![
972 IdentityStatusChange {
973 user_id: owned_user_id!("@u2:s.co"),
974 changed_to: IdentityState::PinViolation
975 },
976 IdentityStatusChange {
977 user_id: owned_user_id!("@u4:s.co"),
978 changed_to: IdentityState::PinViolation
979 },
980 IdentityStatusChange {
981 user_id: owned_user_id!("@u5:s.co"),
982 changed_to: IdentityState::Verified
983 },
984 IdentityStatusChange {
985 user_id: owned_user_id!("@u6:s.co"),
986 changed_to: IdentityState::VerificationViolation
987 }
988 ]
989 );
990 }
991
992 #[derive(Debug)]
993 struct Membership {
994 is_member: bool,
995 user_identity: UserIdentity,
996 identity_state: IdentityState,
997 }
998
999 #[derive(Clone, Debug)]
1000 struct FakeRoom {
1001 users: Arc<Mutex<HashMap<OwnedUserId, Membership>>>,
1002 }
1003
1004 impl FakeRoom {
1005 fn new() -> Self {
1006 Self { users: Default::default() }
1007 }
1008
1009 fn member(&mut self, user_identity: UserIdentity, identity_state: IdentityState) {
1010 self.users.lock().unwrap().insert(
1011 user_identity.user_id().to_owned(),
1012 Membership { is_member: true, user_identity, identity_state },
1013 );
1014 }
1015
1016 fn non_member(&mut self, user_identity: UserIdentity, identity_state: IdentityState) {
1017 self.users.lock().unwrap().insert(
1018 user_identity.user_id().to_owned(),
1019 Membership { is_member: false, user_identity, identity_state },
1020 );
1021 }
1022
1023 fn update_state(&self, user_id: &UserId, changed_to: &IdentityState) {
1024 self.users
1025 .lock()
1026 .unwrap()
1027 .entry(user_id.to_owned())
1028 .and_modify(|m| m.identity_state = changed_to.clone());
1029 }
1030 }
1031
1032 impl RoomIdentityProvider for FakeRoom {
1033 fn is_member<'a>(&'a self, user_id: &'a UserId) -> BoxFuture<'a, bool> {
1034 Box::pin(async {
1035 self.users.lock().unwrap().get(user_id).map(|m| m.is_member).unwrap_or(false)
1036 })
1037 }
1038
1039 fn member_identities(&self) -> BoxFuture<'_, Vec<UserIdentity>> {
1040 Box::pin(async {
1041 self.users
1042 .lock()
1043 .unwrap()
1044 .values()
1045 .filter_map(|m| if m.is_member { Some(m.user_identity.clone()) } else { None })
1046 .collect()
1047 })
1048 }
1049
1050 fn user_identity<'a>(&'a self, user_id: &'a UserId) -> BoxFuture<'a, Option<UserIdentity>> {
1051 Box::pin(async {
1052 self.users.lock().unwrap().get(user_id).map(|m| m.user_identity.clone())
1053 })
1054 }
1055
1056 fn state_of(&self, user_identity: &UserIdentity) -> IdentityState {
1057 self.users
1058 .lock()
1059 .unwrap()
1060 .get(user_identity.user_id())
1061 .map(|m| m.identity_state.clone())
1062 .unwrap_or(IdentityState::Pinned)
1063 }
1064 }
1065
1066 fn room_change(user_id: &UserId, new_state: MembershipState) -> RoomIdentityChange {
1067 let event = SyncRoomMemberEvent::Original(OriginalSyncStateEvent {
1068 content: RoomMemberEventContent::new(new_state),
1069 event_id: owned_event_id!("$1"),
1070 sender: owned_user_id!("@admin:b.c"),
1071 origin_server_ts: MilliSecondsSinceUnixEpoch(UInt::new(2123).unwrap()),
1072 unsigned: RoomMemberUnsigned::new(),
1073 state_key: user_id.to_owned(),
1074 });
1075 RoomIdentityChange::SyncRoomMemberEvent(event)
1076 }
1077
1078 async fn identity_change(
1079 room: &mut FakeRoom,
1080 user_id: &UserId,
1081 changed_to: IdentityState,
1082 new: bool,
1083 own: bool,
1084 ) -> RoomIdentityChange {
1085 identity_changes(
1086 room,
1087 &[IdentityChangeSpec { user_id: user_id.to_owned(), changed_to, new, own }],
1088 )
1089 .await
1090 }
1091
1092 struct IdentityChangeSpec {
1093 user_id: OwnedUserId,
1094 changed_to: IdentityState,
1095 new: bool,
1096 own: bool,
1097 }
1098
1099 async fn identity_changes(
1100 room: &mut FakeRoom,
1101 changes: &[IdentityChangeSpec],
1102 ) -> RoomIdentityChange {
1103 let mut updates = IdentityUpdates::default();
1104
1105 for change in changes {
1106 let user_identity = if change.own {
1107 own_user_identity(&change.user_id).await
1108 } else {
1109 other_user_identity(&change.user_id).await
1110 };
1111
1112 room.update_state(user_identity.user_id(), &change.changed_to);
1113 if change.new {
1114 updates.new.insert(user_identity.user_id().to_owned(), user_identity);
1115 } else {
1116 updates.changed.insert(user_identity.user_id().to_owned(), user_identity);
1117 }
1118 }
1119 RoomIdentityChange::IdentityUpdates(updates)
1120 }
1121
1122 async fn other_user_identity(user_id: &UserId) -> UserIdentity {
1124 use std::sync::Arc;
1125
1126 use ruma::owned_device_id;
1127 use tokio::sync::Mutex;
1128
1129 use crate::{
1130 olm::PrivateCrossSigningIdentity,
1131 store::{CryptoStoreWrapper, MemoryStore},
1132 verification::VerificationMachine,
1133 Account,
1134 };
1135
1136 let device_id = owned_device_id!("DEV123");
1137 let account = Account::with_device_id(user_id, &device_id);
1138
1139 let private_identity =
1140 Arc::new(Mutex::new(PrivateCrossSigningIdentity::with_account(&account).await.0));
1141
1142 let other_user_identity_data =
1143 OtherUserIdentityData::from_private(&*private_identity.lock().await).await;
1144
1145 UserIdentity::Other(OtherUserIdentity {
1146 inner: other_user_identity_data,
1147 own_identity: None,
1148 verification_machine: VerificationMachine::new(
1149 account.clone(),
1150 Arc::new(Mutex::new(PrivateCrossSigningIdentity::new(
1151 account.user_id().to_owned(),
1152 ))),
1153 Arc::new(CryptoStoreWrapper::new(
1154 account.user_id(),
1155 account.device_id(),
1156 MemoryStore::new(),
1157 )),
1158 ),
1159 })
1160 }
1161
1162 async fn own_user_identity(user_id: &UserId) -> UserIdentity {
1164 use std::sync::Arc;
1165
1166 use ruma::owned_device_id;
1167 use tokio::sync::Mutex;
1168
1169 use crate::{
1170 olm::PrivateCrossSigningIdentity,
1171 store::{CryptoStoreWrapper, MemoryStore},
1172 verification::VerificationMachine,
1173 Account,
1174 };
1175
1176 let device_id = owned_device_id!("DEV123");
1177 let account = Account::with_device_id(user_id, &device_id);
1178
1179 let private_identity =
1180 Arc::new(Mutex::new(PrivateCrossSigningIdentity::with_account(&account).await.0));
1181
1182 let own_user_identity_data =
1183 OwnUserIdentityData::from_private(&*private_identity.lock().await).await;
1184
1185 let cross_signing_identity = PrivateCrossSigningIdentity::new(account.user_id().to_owned());
1186 let verification_machine = VerificationMachine::new(
1187 account.clone(),
1188 Arc::new(Mutex::new(cross_signing_identity.clone())),
1189 Arc::new(CryptoStoreWrapper::new(
1190 account.user_id(),
1191 account.device_id(),
1192 MemoryStore::new(),
1193 )),
1194 );
1195
1196 UserIdentity::Own(own_identity_wrapped(
1197 own_user_identity_data,
1198 verification_machine.clone(),
1199 Store::new(
1200 account.static_data().clone(),
1201 Arc::new(Mutex::new(cross_signing_identity)),
1202 Arc::new(CryptoStoreWrapper::new(
1203 user_id!("@u:s.co"),
1204 device_id!("DEV7"),
1205 MemoryStore::new(),
1206 )),
1207 verification_machine,
1208 ),
1209 ))
1210 }
1211}