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>;
}
#[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);
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) = 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 => {
let leaving_state = state_of(&user_identity);
if leaving_state == IdentityState::PinViolation {
return vec![self.set_state(user_id, IdentityState::Pinned)];
}
}
MembershipState::Knock => {
}
_ => {}
}
}
}
}
vec![]
}
fn update_user_state(
&mut self,
user_id: &UserId,
user_identity: &UserIdentity,
) -> Option<IdentityStatusChange> {
if let UserIdentity::Other(_) = &user_identity {
let new_state = state_of(user_identity);
let old_state = self.known_states.get(user_id);
if new_state != old_state {
Some(self.set_state(user_identity.user_id(), new_state))
} else {
None
}
} else {
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 }
}
}
fn state_of(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(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>) -> Self {
let mut known_states = HashMap::new();
for user_identity in member_identities {
let state = 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::sync::Arc;
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 tokio::sync::Mutex;
use super::{IdentityState, RoomIdentityChange, RoomIdentityProvider, RoomIdentityState};
use crate::{
identities::user::testing::own_identity_wrapped,
olm::PrivateCrossSigningIdentity,
store::{IdentityUpdates, Store},
Account, IdentityStatusChange, OtherUserIdentity, OtherUserIdentityData, OwnUserIdentity,
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_identities(user_id, IdentityState::Pinned).await);
let mut state = RoomIdentityState::new(room).await;
let updates = identity_change(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_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_identities(user_id, IdentityState::PinViolation).await);
let mut state = RoomIdentityState::new(room).await;
let updates = identity_change(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_unpinning_an_identity_not_in_the_room_does_nothing() {
let user_id = user_id!("@u:s.co");
let room = FakeRoom::new();
let mut state = RoomIdentityState::new(room).await;
let updates = identity_change(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 room = FakeRoom::new();
let mut state = RoomIdentityState::new(room).await;
let updates = identity_change(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_identities(user_id, IdentityState::Pinned).await);
let mut state = RoomIdentityState::new(room).await;
let updates = identity_change(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_identities(user_id, IdentityState::PinViolation).await);
let mut state = RoomIdentityState::new(room).await;
let updates = identity_change(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_identities(user_id, IdentityState::Pinned).await);
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_identities(user_id, IdentityState::PinViolation).await);
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,
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_identities(user_id, IdentityState::Pinned).await);
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, []);
}
#[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_identities(user_id, IdentityState::PinViolation).await);
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::PinViolation
}]
);
}
#[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_identities(user_id, IdentityState::Pinned).await);
let mut state = RoomIdentityState::new(room).await;
let updates = identity_change(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_identities(user_id, IdentityState::PinViolation).await);
let mut state = RoomIdentityState::new(room).await;
let updates = identity_change(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_identities(user_id, IdentityState::Pinned).await);
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_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_identities(user_id, IdentityState::PinViolation).await);
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, 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_identities(user_id, IdentityState::Pinned).await);
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_identities(user_id, IdentityState::PinViolation).await);
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_identities(user_id, IdentityState::Pinned).await);
let mut state = RoomIdentityState::new(room).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_identities(user_id, IdentityState::PinViolation).await);
let mut state = RoomIdentityState::new(room).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_identities(user1, IdentityState::Pinned).await);
room.member(other_user_identities(user2, IdentityState::PinViolation).await);
room.member(other_user_identities(user3, IdentityState::Pinned).await);
let mut state = RoomIdentityState::new(room).await;
let updates = identity_changes(&[
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_identities(user_id, IdentityState::Pinned).await);
let mut state = RoomIdentityState::new(room).await;
let update1 = state
.process_change(
identity_change(user_id, IdentityState::PinViolation, false, false).await,
)
.await;
let update2 = state
.process_change(
identity_change(user_id, IdentityState::PinViolation, false, false).await,
)
.await;
let update3 = state
.process_change(identity_change(user_id, IdentityState::Pinned, false, false).await)
.await;
let update4 = state
.process_change(
identity_change(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_identities(user1, IdentityState::Pinned).await);
room.member(other_user_identities(user2, IdentityState::Pinned).await);
let state = RoomIdentityState::new(room).await;
assert!(state.current_state().is_empty());
}
#[async_test]
async fn test_current_state_contains_all_unpinned_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 mut room = FakeRoom::new();
room.member(other_user_identities(user1, IdentityState::Pinned).await);
room.member(other_user_identities(user2, IdentityState::PinViolation).await);
room.member(other_user_identities(user3, IdentityState::Pinned).await);
room.member(other_user_identities(user4, IdentityState::PinViolation).await);
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
}
]
);
}
#[derive(Debug)]
struct FakeRoom {
members: Vec<UserIdentity>,
non_members: Vec<UserIdentity>,
}
impl FakeRoom {
fn new() -> Self {
Self { members: Default::default(), non_members: Default::default() }
}
fn member(&mut self, user_identity: UserIdentity) {
self.members.push(user_identity);
}
fn non_member(&mut self, user_identity: UserIdentity) {
self.non_members.push(user_identity);
}
}
#[async_trait]
impl RoomIdentityProvider for FakeRoom {
async fn is_member(&self, user_id: &UserId) -> bool {
self.members.iter().any(|u| u.user_id() == user_id)
}
async fn member_identities(&self) -> Vec<UserIdentity> {
self.members.clone()
}
async fn user_identity(&self, user_id: &UserId) -> Option<UserIdentity> {
self.non_members
.iter()
.chain(self.members.iter())
.find(|u| u.user_id() == user_id)
.cloned()
}
}
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(
user_id: &UserId,
changed_to: IdentityState,
new: bool,
own: bool,
) -> RoomIdentityChange {
identity_changes(&[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(changes: &[IdentityChangeSpec]) -> RoomIdentityChange {
let mut updates = IdentityUpdates::default();
for change in changes {
let user_identities = if change.own {
let user_identity =
own_user_identity(&change.user_id, change.changed_to.clone()).await;
UserIdentity::Own(user_identity)
} else {
let user_identity =
other_user_identity(&change.user_id, change.changed_to.clone()).await;
UserIdentity::Other(user_identity)
};
if change.new {
updates.new.insert(user_identities.user_id().to_owned(), user_identities);
} else {
updates.changed.insert(user_identities.user_id().to_owned(), user_identities);
}
}
RoomIdentityChange::IdentityUpdates(updates)
}
async fn other_user_identities(
user_id: &UserId,
identity_state: IdentityState,
) -> UserIdentity {
UserIdentity::Other(other_user_identity(user_id, identity_state).await)
}
async fn other_user_identity(
user_id: &UserId,
identity_state: IdentityState,
) -> OtherUserIdentity {
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;
let mut user_identity = 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(),
)),
),
};
match identity_state {
IdentityState::Verified => {
assert!(user_identity.is_verified());
assert!(!user_identity.was_previously_verified());
assert!(!user_identity.has_verification_violation());
assert!(!user_identity.identity_needs_user_approval());
}
IdentityState::Pinned => {
assert!(!user_identity.is_verified());
assert!(!user_identity.was_previously_verified());
assert!(!user_identity.has_verification_violation());
assert!(!user_identity.identity_needs_user_approval());
}
IdentityState::PinViolation => {
change_master_key(&mut user_identity, &account).await;
assert!(!user_identity.is_verified());
assert!(!user_identity.was_previously_verified());
assert!(!user_identity.has_verification_violation());
assert!(user_identity.identity_needs_user_approval());
}
IdentityState::VerificationViolation => {
assert!(!user_identity.is_verified());
assert!(user_identity.was_previously_verified());
assert!(!user_identity.has_verification_violation());
assert!(user_identity.identity_needs_user_approval());
}
}
user_identity
}
async fn own_user_identities(user_id: &UserId, identity_state: IdentityState) -> UserIdentity {
UserIdentity::Own(own_user_identity(user_id, identity_state).await)
}
async fn own_user_identity(user_id: &UserId, identity_state: IdentityState) -> OwnUserIdentity {
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(),
)),
);
let mut user_identity = 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,
),
);
match identity_state {
IdentityState::Verified => {
assert!(user_identity.is_verified());
assert!(!user_identity.was_previously_verified());
assert!(!user_identity.has_verification_violation());
}
IdentityState::Pinned => {
assert!(!user_identity.is_verified());
assert!(!user_identity.was_previously_verified());
assert!(!user_identity.has_verification_violation());
}
IdentityState::PinViolation => {
change_own_master_key(&mut user_identity, &account).await;
assert!(!user_identity.is_verified());
assert!(!user_identity.was_previously_verified());
assert!(!user_identity.has_verification_violation());
}
IdentityState::VerificationViolation => {
assert!(!user_identity.is_verified());
assert!(user_identity.was_previously_verified());
assert!(!user_identity.has_verification_violation());
}
}
user_identity
}
async fn change_master_key(user_identity: &mut OtherUserIdentity, account: &Account) {
let private_identity =
Arc::new(Mutex::new(PrivateCrossSigningIdentity::with_account(account).await.0));
let data = OtherUserIdentityData::from_private(&*private_identity.lock().await).await;
user_identity
.update(data.master_key().clone(), data.self_signing_key().clone(), None)
.unwrap();
}
async fn change_own_master_key(user_identity: &mut OwnUserIdentity, account: &Account) {
let private_identity =
Arc::new(Mutex::new(PrivateCrossSigningIdentity::with_account(account).await.0));
let data = OwnUserIdentityData::from_private(&*private_identity.lock().await).await;
user_identity
.update(
data.master_key().clone(),
data.self_signing_key().clone(),
data.user_signing_key().clone(),
)
.unwrap();
}
}