use std::collections::HashMap;
use async_trait::async_trait;
use ruma::{
events::{
room::member::{MembershipState, SyncRoomMemberEvent},
SyncStateEvent,
},
OwnedUserId, UserId,
};
use super::UserIdentity;
use crate::store::IdentityUpdates;
#[async_trait]
pub trait RoomIdentityProvider: core::fmt::Debug {
async fn is_member(&self, user_id: &UserId) -> bool;
async fn member_identities(&self) -> Vec<UserIdentity>;
async fn user_identity(&self, user_id: &UserId) -> Option<UserIdentity>;
fn state_of(&self, user_identity: &UserIdentity) -> IdentityState {
if user_identity.is_verified() {
IdentityState::Verified
} else if user_identity.has_verification_violation() {
IdentityState::VerificationViolation
} else if let UserIdentity::Other(u) = user_identity {
if u.identity_needs_user_approval() {
IdentityState::PinViolation
} else {
IdentityState::Pinned
}
} else {
IdentityState::Pinned
}
}
}
#[derive(Debug)]
pub struct RoomIdentityState<R: RoomIdentityProvider> {
room: R,
known_states: KnownStates,
}
impl<R: RoomIdentityProvider> RoomIdentityState<R> {
pub async fn new(room: R) -> Self {
let known_states = KnownStates::from_identities(room.member_identities().await, &room);
Self { room, known_states }
}
pub fn current_state(&self) -> Vec<IdentityStatusChange> {
self.known_states
.known_states
.iter()
.map(|(user_id, state)| IdentityStatusChange {
user_id: user_id.clone(),
changed_to: state.clone(),
})
.collect()
}
pub async fn process_change(&mut self, item: RoomIdentityChange) -> Vec<IdentityStatusChange> {
match item {
RoomIdentityChange::IdentityUpdates(identity_updates) => {
self.process_identity_changes(identity_updates).await
}
RoomIdentityChange::SyncRoomMemberEvent(sync_room_member_event) => {
self.process_membership_change(sync_room_member_event).await
}
}
}
async fn process_identity_changes(
&mut self,
identity_updates: IdentityUpdates,
) -> Vec<IdentityStatusChange> {
let mut ret = vec![];
for user_identity in identity_updates.new.values().chain(identity_updates.changed.values())
{
let user_id = user_identity.user_id();
if self.room.is_member(user_id).await {
let update = self.update_user_state(user_id, user_identity);
if let Some(identity_status_change) = update {
ret.push(identity_status_change);
}
}
}
ret
}
async fn process_membership_change(
&mut self,
sync_room_member_event: SyncRoomMemberEvent,
) -> Vec<IdentityStatusChange> {
if let SyncStateEvent::Original(event) = sync_room_member_event {
let user_id: Result<&UserId, _> = event.state_key.as_str().try_into();
if let Ok(user_id) = user_id {
if let Some(user_identity @ UserIdentity::Other(_)) =
self.room.user_identity(user_id).await
{
match event.content.membership {
MembershipState::Join | MembershipState::Invite => {
if let Some(update) = self.update_user_state(user_id, &user_identity) {
return vec![update];
}
}
MembershipState::Leave | MembershipState::Ban => {
if let Some(update) =
self.update_user_state_to(user_id, IdentityState::Pinned)
{
return vec![update];
}
}
MembershipState::Knock => {
}
_ => {}
}
}
}
}
vec![]
}
fn update_user_state(
&mut self,
user_id: &UserId,
user_identity: &UserIdentity,
) -> Option<IdentityStatusChange> {
if let UserIdentity::Other(_) = &user_identity {
self.update_user_state_to(user_id, self.room.state_of(user_identity))
} else {
None
}
}
fn update_user_state_to(
&mut self,
user_id: &UserId,
new_state: IdentityState,
) -> Option<IdentityStatusChange> {
use IdentityState::*;
let old_state = self.known_states.get(user_id);
match (old_state, &new_state) {
(Pinned, PinViolation) |
(Pinned, VerificationViolation) |
(Verified, PinViolation) |
(Verified, VerificationViolation) |
(PinViolation, Pinned) |
(PinViolation, Verified) |
(VerificationViolation, Pinned) |
(VerificationViolation, Verified) |
(PinViolation, VerificationViolation) |
(VerificationViolation, PinViolation) => Some(self.set_state(user_id, new_state)),
(Pinned, Verified) |
(Verified, Pinned) => {
self.set_state(user_id, new_state);
None
}
(Pinned, Pinned) |
(Verified, Verified) |
(PinViolation, PinViolation) |
(VerificationViolation, VerificationViolation) => None,
}
}
fn set_state(&mut self, user_id: &UserId, new_state: IdentityState) -> IdentityStatusChange {
self.known_states.set(user_id, &new_state);
IdentityStatusChange { user_id: user_id.to_owned(), changed_to: new_state }
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct IdentityStatusChange {
pub user_id: OwnedUserId,
pub changed_to: IdentityState,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
pub enum IdentityState {
Verified,
Pinned,
PinViolation,
VerificationViolation,
}
#[derive(Debug)]
pub enum RoomIdentityChange {
IdentityUpdates(IdentityUpdates),
SyncRoomMemberEvent(SyncRoomMemberEvent),
}
#[derive(Debug)]
struct KnownStates {
known_states: HashMap<OwnedUserId, IdentityState>,
}
impl KnownStates {
fn from_identities(
member_identities: impl IntoIterator<Item = UserIdentity>,
room: &dyn RoomIdentityProvider,
) -> Self {
let mut known_states = HashMap::new();
for user_identity in member_identities {
let state = room.state_of(&user_identity);
if state != IdentityState::Pinned {
known_states.insert(user_identity.user_id().to_owned(), state);
}
}
Self { known_states }
}
fn get(&self, user_id: &UserId) -> IdentityState {
self.known_states.get(user_id).cloned().unwrap_or(IdentityState::Pinned)
}
fn set(&mut self, user_id: &UserId, identity_state: &IdentityState) {
if let IdentityState::Pinned = identity_state {
self.known_states.remove(user_id);
} else {
self.known_states.insert(user_id.to_owned(), identity_state.clone());
}
}
}
#[cfg(test)]
mod tests {
use std::{
collections::HashMap,
sync::{Arc, Mutex},
};
use async_trait::async_trait;
use matrix_sdk_test::async_test;
use ruma::{
device_id,
events::{
room::member::{
MembershipState, RoomMemberEventContent, RoomMemberUnsigned, SyncRoomMemberEvent,
},
OriginalSyncStateEvent,
},
owned_event_id, owned_user_id, user_id, MilliSecondsSinceUnixEpoch, OwnedUserId, UInt,
UserId,
};
use super::{IdentityState, RoomIdentityChange, RoomIdentityProvider, RoomIdentityState};
use crate::{
identities::user::testing::own_identity_wrapped,
store::{IdentityUpdates, Store},
IdentityStatusChange, OtherUserIdentity, OtherUserIdentityData, OwnUserIdentityData,
UserIdentity,
};
#[async_test]
async fn test_unpinning_a_pinned_identity_in_the_room_notifies() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.member(other_user_identity(user_id).await, IdentityState::Pinned);
let mut state = RoomIdentityState::new(room.clone()).await;
let updates =
identity_change(&mut room, user_id, IdentityState::PinViolation, false, false).await;
let update = state.process_change(updates).await;
assert_eq!(
update,
vec![IdentityStatusChange {
user_id: user_id.to_owned(),
changed_to: IdentityState::PinViolation
}]
);
}
#[async_test]
async fn test_verifying_a_pinned_identity_in_the_room_does_nothing() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.member(other_user_identity(user_id).await, IdentityState::Pinned);
let mut state = RoomIdentityState::new(room.clone()).await;
let updates =
identity_change(&mut room, user_id, IdentityState::Verified, false, false).await;
let update = state.process_change(updates).await;
assert_eq!(update, vec![]);
}
#[async_test]
async fn test_pinning_an_unpinned_identity_in_the_room_notifies() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
let mut state = RoomIdentityState::new(room.clone()).await;
let updates =
identity_change(&mut room, user_id, IdentityState::Pinned, false, false).await;
let update = state.process_change(updates).await;
assert_eq!(
update,
vec![IdentityStatusChange {
user_id: user_id.to_owned(),
changed_to: IdentityState::Pinned
}]
);
}
#[async_test]
async fn test_unpinned_identity_becoming_verification_violating_in_the_room_notifies() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
let mut state = RoomIdentityState::new(room.clone()).await;
let updates =
identity_change(&mut room, user_id, IdentityState::VerificationViolation, false, false)
.await;
let update = state.process_change(updates).await;
assert_eq!(
update,
vec![IdentityStatusChange {
user_id: user_id.to_owned(),
changed_to: IdentityState::VerificationViolation
}]
);
}
#[async_test]
async fn test_unpinning_an_identity_not_in_the_room_does_nothing() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
let mut state = RoomIdentityState::new(room.clone()).await;
let updates =
identity_change(&mut room, user_id, IdentityState::PinViolation, true, false).await;
let update = state.process_change(updates).await;
assert_eq!(update, vec![]);
}
#[async_test]
async fn test_pinning_an_identity_not_in_the_room_does_nothing() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
let mut state = RoomIdentityState::new(room.clone()).await;
let updates = identity_change(&mut room, user_id, IdentityState::Pinned, true, false).await;
let update = state.process_change(updates).await;
assert_eq!(update, []);
}
#[async_test]
async fn test_pinning_an_already_pinned_identity_in_the_room_does_nothing() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.member(other_user_identity(user_id).await, IdentityState::Pinned);
let mut state = RoomIdentityState::new(room.clone()).await;
let updates =
identity_change(&mut room, user_id, IdentityState::Pinned, false, false).await;
let update = state.process_change(updates).await;
assert_eq!(update, []);
}
#[async_test]
async fn test_unpinning_an_already_unpinned_identity_in_the_room_does_nothing() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
let mut state = RoomIdentityState::new(room.clone()).await;
let updates =
identity_change(&mut room, user_id, IdentityState::PinViolation, false, false).await;
let update = state.process_change(updates).await;
assert_eq!(update, []);
}
#[async_test]
async fn test_a_pinned_identity_joining_the_room_does_nothing() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.non_member(other_user_identity(user_id).await, IdentityState::Pinned);
let mut state = RoomIdentityState::new(room.clone()).await;
let updates = room_change(user_id, MembershipState::Join);
let update = state.process_change(updates).await;
assert_eq!(update, []);
}
#[async_test]
async fn test_a_verified_identity_joining_the_room_does_nothing() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.non_member(other_user_identity(user_id).await, IdentityState::Verified);
let mut state = RoomIdentityState::new(room).await;
let updates = room_change(user_id, MembershipState::Join);
let update = state.process_change(updates).await;
assert_eq!(update, []);
}
#[async_test]
async fn test_an_unpinned_identity_joining_the_room_notifies() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.non_member(other_user_identity(user_id).await, IdentityState::PinViolation);
let mut state = RoomIdentityState::new(room.clone()).await;
let updates = room_change(user_id, MembershipState::Join);
let update = state.process_change(updates).await;
assert_eq!(
update,
vec![IdentityStatusChange {
user_id: user_id.to_owned(),
changed_to: IdentityState::PinViolation
}]
);
}
#[async_test]
async fn test_a_pinned_identity_invited_to_the_room_does_nothing() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.non_member(other_user_identity(user_id).await, IdentityState::Pinned);
let mut state = RoomIdentityState::new(room.clone()).await;
let updates = room_change(user_id, MembershipState::Invite);
let update = state.process_change(updates).await;
assert_eq!(update, []);
}
#[async_test]
async fn test_an_unpinned_identity_invited_to_the_room_notifies() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.non_member(other_user_identity(user_id).await, IdentityState::PinViolation);
let mut state = RoomIdentityState::new(room.clone()).await;
let updates = room_change(user_id, MembershipState::Invite);
let update = state.process_change(updates).await;
assert_eq!(
update,
vec![IdentityStatusChange {
user_id: user_id.to_owned(),
changed_to: IdentityState::PinViolation
}]
);
}
#[async_test]
async fn test_a_verification_violating_identity_invited_to_the_room_notifies() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.non_member(other_user_identity(user_id).await, IdentityState::VerificationViolation);
let mut state = RoomIdentityState::new(room).await;
let updates = room_change(user_id, MembershipState::Invite);
let update = state.process_change(updates).await;
assert_eq!(
update,
vec![IdentityStatusChange {
user_id: user_id.to_owned(),
changed_to: IdentityState::VerificationViolation
}]
);
}
#[async_test]
async fn test_own_identity_becoming_unpinned_is_ignored() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.member(own_user_identity(user_id).await, IdentityState::Pinned);
let mut state = RoomIdentityState::new(room.clone()).await;
let updates =
identity_change(&mut room, user_id, IdentityState::PinViolation, false, true).await;
let update = state.process_change(updates).await;
assert_eq!(update, vec![]);
}
#[async_test]
async fn test_own_identity_becoming_pinned_is_ignored() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.member(own_user_identity(user_id).await, IdentityState::PinViolation);
let mut state = RoomIdentityState::new(room.clone()).await;
let updates = identity_change(&mut room, user_id, IdentityState::Pinned, false, true).await;
let update = state.process_change(updates).await;
assert_eq!(update, vec![]);
}
#[async_test]
async fn test_own_pinned_identity_joining_room_is_ignored() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.non_member(own_user_identity(user_id).await, IdentityState::Pinned);
let mut state = RoomIdentityState::new(room.clone()).await;
let updates = room_change(user_id, MembershipState::Join);
let update = state.process_change(updates).await;
assert_eq!(update, []);
}
#[async_test]
async fn test_own_unpinned_identity_joining_room_is_ignored() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.non_member(own_user_identity(user_id).await, IdentityState::PinViolation);
let mut state = RoomIdentityState::new(room.clone()).await;
let updates = room_change(user_id, MembershipState::Join);
let update = state.process_change(updates).await;
assert_eq!(update, vec![]);
}
#[async_test]
async fn test_a_pinned_identity_leaving_the_room_does_nothing() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.member(other_user_identity(user_id).await, IdentityState::Pinned);
let mut state = RoomIdentityState::new(room.clone()).await;
let updates = room_change(user_id, MembershipState::Leave);
let update = state.process_change(updates).await;
assert_eq!(update, []);
}
#[async_test]
async fn test_a_verified_identity_leaving_the_room_does_nothing() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.member(other_user_identity(user_id).await, IdentityState::Verified);
let mut state = RoomIdentityState::new(room).await;
let updates = room_change(user_id, MembershipState::Leave);
let update = state.process_change(updates).await;
assert_eq!(update, []);
}
#[async_test]
async fn test_an_unpinned_identity_leaving_the_room_notifies() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
let mut state = RoomIdentityState::new(room.clone()).await;
let updates = room_change(user_id, MembershipState::Leave);
let update = state.process_change(updates).await;
assert_eq!(
update,
vec![IdentityStatusChange {
user_id: user_id.to_owned(),
changed_to: IdentityState::Pinned
}]
);
}
#[async_test]
async fn test_a_verification_violating_identity_leaving_the_room_notifies() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.member(other_user_identity(user_id).await, IdentityState::VerificationViolation);
let mut state = RoomIdentityState::new(room).await;
let updates = room_change(user_id, MembershipState::Leave);
let update = state.process_change(updates).await;
assert_eq!(
update,
vec![IdentityStatusChange {
user_id: user_id.to_owned(),
changed_to: IdentityState::Pinned
}]
);
}
#[async_test]
async fn test_a_pinned_identity_being_banned_does_nothing() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.member(other_user_identity(user_id).await, IdentityState::Pinned);
let mut state = RoomIdentityState::new(room.clone()).await;
let updates = room_change(user_id, MembershipState::Ban);
let update = state.process_change(updates).await;
assert_eq!(update, []);
}
#[async_test]
async fn test_an_unpinned_identity_being_banned_notifies() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.member(other_user_identity(user_id).await, IdentityState::PinViolation);
let mut state = RoomIdentityState::new(room.clone()).await;
let updates = room_change(user_id, MembershipState::Ban);
let update = state.process_change(updates).await;
assert_eq!(
update,
vec![IdentityStatusChange {
user_id: user_id.to_owned(),
changed_to: IdentityState::Pinned
}]
);
}
#[async_test]
async fn test_multiple_simultaneous_identity_updates_are_all_notified() {
let user1 = user_id!("@u1:s.co");
let user2 = user_id!("@u2:s.co");
let user3 = user_id!("@u3:s.co");
let mut room = FakeRoom::new();
room.member(other_user_identity(user1).await, IdentityState::Pinned);
room.member(other_user_identity(user2).await, IdentityState::PinViolation);
room.member(other_user_identity(user3).await, IdentityState::Pinned);
let mut state = RoomIdentityState::new(room.clone()).await;
let updates = identity_changes(
&mut room,
&[
IdentityChangeSpec {
user_id: user1.to_owned(),
changed_to: IdentityState::PinViolation,
new: false,
own: false,
},
IdentityChangeSpec {
user_id: user2.to_owned(),
changed_to: IdentityState::Pinned,
new: false,
own: false,
},
IdentityChangeSpec {
user_id: user3.to_owned(),
changed_to: IdentityState::PinViolation,
new: false,
own: false,
},
],
)
.await;
let update = state.process_change(updates).await;
assert_eq!(
update,
vec![
IdentityStatusChange {
user_id: user1.to_owned(),
changed_to: IdentityState::PinViolation
},
IdentityStatusChange {
user_id: user2.to_owned(),
changed_to: IdentityState::Pinned
},
IdentityStatusChange {
user_id: user3.to_owned(),
changed_to: IdentityState::PinViolation
}
]
);
}
#[async_test]
async fn test_multiple_changes_are_notified() {
let user_id = user_id!("@u:s.co");
let mut room = FakeRoom::new();
room.member(other_user_identity(user_id).await, IdentityState::Pinned);
let mut state = RoomIdentityState::new(room.clone()).await;
let update1 = state
.process_change(
identity_change(&mut room, user_id, IdentityState::PinViolation, false, false)
.await,
)
.await;
let update2 = state
.process_change(
identity_change(&mut room, user_id, IdentityState::PinViolation, false, false)
.await,
)
.await;
let update3 = state
.process_change(
identity_change(&mut room, user_id, IdentityState::Pinned, false, false).await,
)
.await;
let update4 = state
.process_change(
identity_change(&mut room, user_id, IdentityState::PinViolation, false, false)
.await,
)
.await;
assert_eq!(
update1,
vec![IdentityStatusChange {
user_id: user_id.to_owned(),
changed_to: IdentityState::PinViolation
}]
);
assert_eq!(update2, vec![]);
assert_eq!(
update3,
vec![IdentityStatusChange {
user_id: user_id.to_owned(),
changed_to: IdentityState::Pinned
}]
);
assert_eq!(
update4,
vec![IdentityStatusChange {
user_id: user_id.to_owned(),
changed_to: IdentityState::PinViolation
}]
);
}
#[async_test]
async fn test_current_state_of_all_pinned_room_is_empty() {
let user1 = user_id!("@u1:s.co");
let user2 = user_id!("@u2:s.co");
let mut room = FakeRoom::new();
room.member(other_user_identity(user1).await, IdentityState::Pinned);
room.member(other_user_identity(user2).await, IdentityState::Pinned);
let state = RoomIdentityState::new(room).await;
assert!(state.current_state().is_empty());
}
#[async_test]
async fn test_current_state_contains_all_nonpinned_users() {
let user1 = user_id!("@u1:s.co");
let user2 = user_id!("@u2:s.co");
let user3 = user_id!("@u3:s.co");
let user4 = user_id!("@u4:s.co");
let user5 = user_id!("@u5:s.co");
let user6 = user_id!("@u6:s.co");
let mut room = FakeRoom::new();
room.member(other_user_identity(user1).await, IdentityState::Pinned);
room.member(other_user_identity(user2).await, IdentityState::PinViolation);
room.member(other_user_identity(user3).await, IdentityState::Pinned);
room.member(other_user_identity(user4).await, IdentityState::PinViolation);
room.member(other_user_identity(user5).await, IdentityState::Verified);
room.member(other_user_identity(user6).await, IdentityState::VerificationViolation);
let mut state = RoomIdentityState::new(room).await.current_state();
state.sort_by_key(|change| change.user_id.to_owned());
assert_eq!(
state,
vec![
IdentityStatusChange {
user_id: owned_user_id!("@u2:s.co"),
changed_to: IdentityState::PinViolation
},
IdentityStatusChange {
user_id: owned_user_id!("@u4:s.co"),
changed_to: IdentityState::PinViolation
},
IdentityStatusChange {
user_id: owned_user_id!("@u5:s.co"),
changed_to: IdentityState::Verified
},
IdentityStatusChange {
user_id: owned_user_id!("@u6:s.co"),
changed_to: IdentityState::VerificationViolation
}
]
);
}
#[derive(Debug)]
struct Membership {
is_member: bool,
user_identity: UserIdentity,
identity_state: IdentityState,
}
#[derive(Clone, Debug)]
struct FakeRoom {
users: Arc<Mutex<HashMap<OwnedUserId, Membership>>>,
}
impl FakeRoom {
fn new() -> Self {
Self { users: Default::default() }
}
fn member(&mut self, user_identity: UserIdentity, identity_state: IdentityState) {
self.users.lock().unwrap().insert(
user_identity.user_id().to_owned(),
Membership { is_member: true, user_identity, identity_state },
);
}
fn non_member(&mut self, user_identity: UserIdentity, identity_state: IdentityState) {
self.users.lock().unwrap().insert(
user_identity.user_id().to_owned(),
Membership { is_member: false, user_identity, identity_state },
);
}
fn update_state(&self, user_id: &UserId, changed_to: &IdentityState) {
self.users
.lock()
.unwrap()
.entry(user_id.to_owned())
.and_modify(|m| m.identity_state = changed_to.clone());
}
}
#[async_trait]
impl RoomIdentityProvider for FakeRoom {
async fn is_member(&self, user_id: &UserId) -> bool {
self.users.lock().unwrap().get(user_id).map(|m| m.is_member).unwrap_or(false)
}
async fn member_identities(&self) -> Vec<UserIdentity> {
self.users
.lock()
.unwrap()
.values()
.filter_map(|m| if m.is_member { Some(m.user_identity.clone()) } else { None })
.collect()
}
async fn user_identity(&self, user_id: &UserId) -> Option<UserIdentity> {
self.users.lock().unwrap().get(user_id).map(|m| m.user_identity.clone())
}
fn state_of(&self, user_identity: &UserIdentity) -> IdentityState {
self.users
.lock()
.unwrap()
.get(user_identity.user_id())
.map(|m| m.identity_state.clone())
.unwrap_or(IdentityState::Pinned)
}
}
fn room_change(user_id: &UserId, new_state: MembershipState) -> RoomIdentityChange {
let event = SyncRoomMemberEvent::Original(OriginalSyncStateEvent {
content: RoomMemberEventContent::new(new_state),
event_id: owned_event_id!("$1"),
sender: owned_user_id!("@admin:b.c"),
origin_server_ts: MilliSecondsSinceUnixEpoch(UInt::new(2123).unwrap()),
unsigned: RoomMemberUnsigned::new(),
state_key: user_id.to_owned(),
});
RoomIdentityChange::SyncRoomMemberEvent(event)
}
async fn identity_change(
room: &mut FakeRoom,
user_id: &UserId,
changed_to: IdentityState,
new: bool,
own: bool,
) -> RoomIdentityChange {
identity_changes(
room,
&[IdentityChangeSpec { user_id: user_id.to_owned(), changed_to, new, own }],
)
.await
}
struct IdentityChangeSpec {
user_id: OwnedUserId,
changed_to: IdentityState,
new: bool,
own: bool,
}
async fn identity_changes(
room: &mut FakeRoom,
changes: &[IdentityChangeSpec],
) -> RoomIdentityChange {
let mut updates = IdentityUpdates::default();
for change in changes {
let user_identity = if change.own {
own_user_identity(&change.user_id).await
} else {
other_user_identity(&change.user_id).await
};
room.update_state(user_identity.user_id(), &change.changed_to);
if change.new {
updates.new.insert(user_identity.user_id().to_owned(), user_identity);
} else {
updates.changed.insert(user_identity.user_id().to_owned(), user_identity);
}
}
RoomIdentityChange::IdentityUpdates(updates)
}
async fn other_user_identity(user_id: &UserId) -> UserIdentity {
use std::sync::Arc;
use ruma::owned_device_id;
use tokio::sync::Mutex;
use crate::{
olm::PrivateCrossSigningIdentity,
store::{CryptoStoreWrapper, MemoryStore},
verification::VerificationMachine,
Account,
};
let device_id = owned_device_id!("DEV123");
let account = Account::with_device_id(user_id, &device_id);
let private_identity =
Arc::new(Mutex::new(PrivateCrossSigningIdentity::with_account(&account).await.0));
let other_user_identity_data =
OtherUserIdentityData::from_private(&*private_identity.lock().await).await;
UserIdentity::Other(OtherUserIdentity {
inner: other_user_identity_data,
own_identity: None,
verification_machine: VerificationMachine::new(
account.clone(),
Arc::new(Mutex::new(PrivateCrossSigningIdentity::new(
account.user_id().to_owned(),
))),
Arc::new(CryptoStoreWrapper::new(
account.user_id(),
account.device_id(),
MemoryStore::new(),
)),
),
})
}
async fn own_user_identity(user_id: &UserId) -> UserIdentity {
use std::sync::Arc;
use ruma::owned_device_id;
use tokio::sync::Mutex;
use crate::{
olm::PrivateCrossSigningIdentity,
store::{CryptoStoreWrapper, MemoryStore},
verification::VerificationMachine,
Account,
};
let device_id = owned_device_id!("DEV123");
let account = Account::with_device_id(user_id, &device_id);
let private_identity =
Arc::new(Mutex::new(PrivateCrossSigningIdentity::with_account(&account).await.0));
let own_user_identity_data =
OwnUserIdentityData::from_private(&*private_identity.lock().await).await;
let cross_signing_identity = PrivateCrossSigningIdentity::new(account.user_id().to_owned());
let verification_machine = VerificationMachine::new(
account.clone(),
Arc::new(Mutex::new(cross_signing_identity.clone())),
Arc::new(CryptoStoreWrapper::new(
account.user_id(),
account.device_id(),
MemoryStore::new(),
)),
);
UserIdentity::Own(own_identity_wrapped(
own_user_identity_data,
verification_machine.clone(),
Store::new(
account.static_data().clone(),
Arc::new(Mutex::new(cross_signing_identity)),
Arc::new(CryptoStoreWrapper::new(
user_id!("@u:s.co"),
device_id!("DEV7"),
MemoryStore::new(),
)),
verification_machine,
),
))
}
}