diff --git a/crates/matrix-sdk/src/encryption/identities/users.rs b/crates/matrix-sdk/src/encryption/identities/users.rs index 8663b78e2ce..b2f989e46a8 100644 --- a/crates/matrix-sdk/src/encryption/identities/users.rs +++ b/crates/matrix-sdk/src/encryption/identities/users.rs @@ -108,6 +108,7 @@ impl UserIdentity { Self { inner: identity, client } } + #[cfg(all(feature = "e2e-encryption", not(target_arch = "wasm32")))] pub(crate) fn underlying_identity(&self) -> CryptoUserIdentities { self.inner.clone() } diff --git a/crates/matrix-sdk/src/room/identity_status_changes.rs b/crates/matrix-sdk/src/room/identity_status_changes.rs index 8185648f09c..ce93f326a46 100644 --- a/crates/matrix-sdk/src/room/identity_status_changes.rs +++ b/crates/matrix-sdk/src/room/identity_status_changes.rs @@ -13,6 +13,7 @@ // limitations under the License. //! Facility to track changes to the identity of members of rooms. +#![cfg(all(feature = "e2e-encryption", not(target_arch = "wasm32")))] use std::collections::BTreeMap; @@ -62,7 +63,7 @@ impl IdentityStatusChanges { /// For example, if an identity is "pinned" i.e. not manually verified, but /// known, and it becomes a "unpinned" i.e. unknown, because the /// encryption keys are different and the user has not acknowledged - /// this, then this constitues a status change. Also, if an identity is + /// this, then this constitutes a status change. Also, if an identity is /// "unpinned" and becomes "pinned", this is also a status change. /// /// The supplied stream is intended to provide enough information for a @@ -612,7 +613,7 @@ mod tests { .respond_with( ResponseTemplate::new(200).set_body_json(&*test_json::members::MEMBERS), ) - .mount(&server) + .mount(server) .await; } @@ -623,7 +624,7 @@ mod tests { )) .and(header("authorization", "Bearer 1234")) .respond_with(ResponseTemplate::new(200).set_body_json(json!({}))) - .mount(&server) + .mount(server) .await; } diff --git a/crates/matrix-sdk/src/room/mod.rs b/crates/matrix-sdk/src/room/mod.rs index 20ad04d6adb..4c5a83ced86 100644 --- a/crates/matrix-sdk/src/room/mod.rs +++ b/crates/matrix-sdk/src/room/mod.rs @@ -8,14 +8,20 @@ use std::{ time::Duration, }; +#[cfg(all(feature = "e2e-encryption", not(target_arch = "wasm32")))] +use async_trait::async_trait; use eyeball::SharedObservable; use futures_core::Stream; use futures_util::{ future::{try_join, try_join_all}, stream::FuturesUnordered, }; +#[cfg(all(feature = "e2e-encryption", not(target_arch = "wasm32")))] +pub use identity_status_changes::IdentityStatusChanges; #[cfg(feature = "e2e-encryption")] use matrix_sdk_base::crypto::DecryptionSettings; +#[cfg(all(feature = "e2e-encryption", not(target_arch = "wasm32")))] +use matrix_sdk_base::crypto::{IdentityStatusChange, RoomIdentityProvider, UserIdentity}; use matrix_sdk_base::{ deserialized_responses::{ RawAnySyncOrStrippedState, RawSyncOrStrippedState, SyncOrStrippedState, TimelineEvent, @@ -114,6 +120,7 @@ use crate::{ pub mod edit; pub mod futures; +pub mod identity_status_changes; mod member; mod messages; pub mod power_levels; @@ -367,6 +374,35 @@ impl Room { (drop_guard, receiver) } + /// Subscribe to updates about users who are in "pin violation" i.e. their + /// identity has changed and the user has not yet acknowledged this. + /// + /// The returned receiver will receive a new vector of + /// [`IdentityStatusChange`] each time a /keys/query response shows a + /// changed identity for a member of this room, or a sync shows a change + /// to the membership of an affected user. (Changes to the current user are + /// not directly included, but some changes to the current user's identity + /// can trigger changes to how we see other users' identities, which + /// will be included.) + /// + /// The first item in the stream provides the current state of the room: + /// each member of the room who is not in "pinned" state will be + /// included (except the current user). + /// + /// If the `changed_to` property of an [`IdentityStatusChange`] is set to + /// `PinViolation` then a warning should be displayed to the user. If it is + /// set to `Pinned` then no warning should be displayed. + /// + /// Note that if a user who is in pin violation leaves the room, a `Pinned` + /// update is sent, to indicate that the warning should be removed, even + /// though the user's identity is not necessarily pinned. + #[cfg(all(feature = "e2e-encryption", not(target_arch = "wasm32")))] + pub async fn subscribe_to_identity_status_changes( + &self, + ) -> Result>> { + IdentityStatusChanges::create_stream(self.clone()).await + } + /// Returns a wrapping `TimelineEvent` for the input `AnyTimelineEvent`, /// decrypted if needs be. /// @@ -2925,6 +2961,38 @@ impl Room { } } +#[cfg(all(feature = "e2e-encryption", not(target_arch = "wasm32")))] +#[async_trait] +impl RoomIdentityProvider for Room { + async fn is_member(&self, user_id: &UserId) -> bool { + self.get_member(user_id).await.unwrap_or(None).is_some() + } + + async fn member_identities(&self) -> Vec { + let members = self + .members(RoomMemberships::JOIN | RoomMemberships::INVITE) + .await + .unwrap_or_else(|_| Default::default()); + + let mut ret: Vec = Vec::new(); + for member in members { + if let Some(i) = self.user_identity(member.user_id()).await { + ret.push(i); + } + } + ret + } + + async fn user_identity(&self, user_id: &UserId) -> Option { + self.client + .encryption() + .get_user_identity(user_id) + .await + .unwrap_or(None) + .map(|u| u.underlying_identity()) + } +} + /// A wrapper for a weak client and a room id that allows to lazily retrieve a /// room, only when needed. #[derive(Clone)]