mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-05 14:35:20 -04:00
feat(ffi): add support for starting and responding to user verification requests
This commit is contained in:
committed by
Stefan Ceriu
parent
2291a61379
commit
f9ff4fff50
@@ -32,8 +32,11 @@ use matrix_sdk::{
|
||||
user_directory::search_users,
|
||||
},
|
||||
events::{
|
||||
room::{avatar::RoomAvatarEventContent, encryption::RoomEncryptionEventContent},
|
||||
AnyInitialStateEvent, AnyToDeviceEvent, InitialStateEvent,
|
||||
room::{
|
||||
avatar::RoomAvatarEventContent, encryption::RoomEncryptionEventContent,
|
||||
message::MessageType,
|
||||
},
|
||||
AnyInitialStateEvent, InitialStateEvent,
|
||||
},
|
||||
serde::Raw,
|
||||
EventEncryptionAlgorithm, RoomId, TransactionId, UInt, UserId,
|
||||
@@ -53,10 +56,12 @@ use ruma::{
|
||||
},
|
||||
events::{
|
||||
ignored_user_list::IgnoredUserListEventContent,
|
||||
key::verification::request::ToDeviceKeyVerificationRequestEvent,
|
||||
room::{
|
||||
join_rules::{
|
||||
AllowRule as RumaAllowRule, JoinRule as RumaJoinRule, RoomJoinRulesEventContent,
|
||||
},
|
||||
message::OriginalSyncRoomMessageEvent,
|
||||
power_levels::RoomPowerLevelsEventContent,
|
||||
},
|
||||
GlobalAccountDataEventType,
|
||||
@@ -204,12 +209,27 @@ impl Client {
|
||||
tokio::sync::RwLock<Option<SessionVerificationController>>,
|
||||
> = Default::default();
|
||||
let controller = session_verification_controller.clone();
|
||||
sdk_client.add_event_handler(
|
||||
move |event: ToDeviceKeyVerificationRequestEvent| async move {
|
||||
if let Some(session_verification_controller) = &*controller.clone().read().await {
|
||||
session_verification_controller
|
||||
.process_incoming_verification_request(
|
||||
&event.sender,
|
||||
event.content.transaction_id,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
sdk_client.add_event_handler(move |ev: AnyToDeviceEvent| async move {
|
||||
if let Some(session_verification_controller) = &*controller.clone().read().await {
|
||||
session_verification_controller.process_to_device_message(ev).await;
|
||||
} else {
|
||||
debug!("received to-device message, but verification controller isn't ready");
|
||||
let controller = session_verification_controller.clone();
|
||||
sdk_client.add_event_handler(move |event: OriginalSyncRoomMessageEvent| async move {
|
||||
if let MessageType::VerificationRequest(_) = &event.content.msgtype {
|
||||
if let Some(session_verification_controller) = &*controller.clone().read().await {
|
||||
session_verification_controller
|
||||
.process_incoming_verification_request(&event.sender, event.event_id)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -777,8 +797,11 @@ impl Client {
|
||||
.await?
|
||||
.context("Failed retrieving user identity")?;
|
||||
|
||||
let session_verification_controller =
|
||||
SessionVerificationController::new(self.inner.encryption(), user_identity);
|
||||
let session_verification_controller = SessionVerificationController::new(
|
||||
self.inner.encryption(),
|
||||
user_identity,
|
||||
self.inner.account(),
|
||||
);
|
||||
|
||||
*self.session_verification_controller.write().await =
|
||||
Some(session_verification_controller.clone());
|
||||
|
||||
@@ -252,13 +252,13 @@ impl Room {
|
||||
}
|
||||
|
||||
pub async fn member(&self, user_id: String) -> Result<RoomMember, ClientError> {
|
||||
let user_id = UserId::parse(&*user_id).context("Invalid user id.")?;
|
||||
let user_id = UserId::parse(&*user_id)?;
|
||||
let member = self.inner.get_member(&user_id).await?.context("User not found")?;
|
||||
Ok(member.try_into().context("Unknown state membership")?)
|
||||
}
|
||||
|
||||
pub async fn member_avatar_url(&self, user_id: String) -> Result<Option<String>, ClientError> {
|
||||
let user_id = UserId::parse(&*user_id).context("Invalid user id.")?;
|
||||
let user_id = UserId::parse(&*user_id)?;
|
||||
let member = self.inner.get_member(&user_id).await?.context("User not found")?;
|
||||
let avatar_url_string = member.avatar_url().map(|m| m.to_string());
|
||||
Ok(avatar_url_string)
|
||||
@@ -268,7 +268,7 @@ impl Room {
|
||||
&self,
|
||||
user_id: String,
|
||||
) -> Result<Option<String>, ClientError> {
|
||||
let user_id = UserId::parse(&*user_id).context("Invalid user id.")?;
|
||||
let user_id = UserId::parse(&*user_id)?;
|
||||
let member = self.inner.get_member(&user_id).await?.context("User not found")?;
|
||||
let avatar_url_string = member.display_name().map(|m| m.to_owned());
|
||||
Ok(avatar_url_string)
|
||||
|
||||
@@ -7,10 +7,11 @@ use matrix_sdk::{
|
||||
verification::{SasState, SasVerification, VerificationRequest, VerificationRequestState},
|
||||
Encryption,
|
||||
},
|
||||
ruma::events::{key::verification::VerificationMethod, AnyToDeviceEvent},
|
||||
ruma::events::key::verification::VerificationMethod,
|
||||
Account,
|
||||
};
|
||||
use ruma::UserId;
|
||||
use tracing::{error, info};
|
||||
use tracing::error;
|
||||
|
||||
use super::RUNTIME;
|
||||
use crate::{error::ClientError, utils::Timestamp};
|
||||
@@ -42,9 +43,10 @@ pub enum SessionVerificationData {
|
||||
#[derive(Debug, uniffi::Record)]
|
||||
pub struct SessionVerificationRequestDetails {
|
||||
sender_id: String,
|
||||
sender_display_name: Option<String>,
|
||||
flow_id: String,
|
||||
device_id: String,
|
||||
display_name: Option<String>,
|
||||
device_display_name: Option<String>,
|
||||
/// First time this device was seen in milliseconds since epoch.
|
||||
first_seen_timestamp: Timestamp,
|
||||
}
|
||||
@@ -66,6 +68,7 @@ pub type Delegate = Arc<RwLock<Option<Box<dyn SessionVerificationControllerDeleg
|
||||
pub struct SessionVerificationController {
|
||||
encryption: Encryption,
|
||||
user_identity: UserIdentity,
|
||||
account: Account,
|
||||
delegate: Delegate,
|
||||
verification_request: Arc<RwLock<Option<VerificationRequest>>>,
|
||||
sas_verification: Arc<RwLock<Option<SasVerification>>>,
|
||||
@@ -94,15 +97,7 @@ impl SessionVerificationController {
|
||||
.await
|
||||
.ok_or(ClientError::new("Unknown session verification request"))?;
|
||||
|
||||
*self.verification_request.write().unwrap() = Some(verification_request.clone());
|
||||
|
||||
RUNTIME.spawn(Self::listen_to_verification_request_changes(
|
||||
verification_request,
|
||||
self.sas_verification.clone(),
|
||||
self.delegate.clone(),
|
||||
));
|
||||
|
||||
Ok(())
|
||||
self.set_ongoing_verification_request(verification_request)
|
||||
}
|
||||
|
||||
/// Accept the previously acknowledged verification request
|
||||
@@ -118,7 +113,7 @@ impl SessionVerificationController {
|
||||
}
|
||||
|
||||
/// Request verification for the current device
|
||||
pub async fn request_verification(&self) -> Result<(), ClientError> {
|
||||
pub async fn request_device_verification(&self) -> Result<(), ClientError> {
|
||||
let methods = vec![VerificationMethod::SasV1];
|
||||
let verification_request = self
|
||||
.user_identity
|
||||
@@ -126,15 +121,31 @@ impl SessionVerificationController {
|
||||
.await
|
||||
.map_err(anyhow::Error::from)?;
|
||||
|
||||
*self.verification_request.write().unwrap() = Some(verification_request.clone());
|
||||
self.set_ongoing_verification_request(verification_request)
|
||||
}
|
||||
|
||||
RUNTIME.spawn(Self::listen_to_verification_request_changes(
|
||||
verification_request,
|
||||
self.sas_verification.clone(),
|
||||
self.delegate.clone(),
|
||||
));
|
||||
/// Request verification for the given user
|
||||
pub async fn request_user_verification(&self, user_id: String) -> Result<(), ClientError> {
|
||||
let user_id = UserId::parse(user_id)?;
|
||||
|
||||
Ok(())
|
||||
let user_identity = self
|
||||
.encryption
|
||||
.get_user_identity(&user_id)
|
||||
.await?
|
||||
.ok_or(ClientError::new("Unknown user identity"))?;
|
||||
|
||||
if user_identity.is_verified() {
|
||||
return Err(ClientError::new("User is already verified"));
|
||||
}
|
||||
|
||||
let methods = vec![VerificationMethod::SasV1];
|
||||
|
||||
let verification_request = user_identity
|
||||
.request_verification_with_methods(methods)
|
||||
.await
|
||||
.map_err(anyhow::Error::from)?;
|
||||
|
||||
self.set_ongoing_verification_request(verification_request)
|
||||
}
|
||||
|
||||
/// Transition the current verification request into a SAS verification
|
||||
@@ -202,50 +213,79 @@ impl SessionVerificationController {
|
||||
}
|
||||
|
||||
impl SessionVerificationController {
|
||||
pub(crate) fn new(encryption: Encryption, user_identity: UserIdentity) -> Self {
|
||||
pub(crate) fn new(
|
||||
encryption: Encryption,
|
||||
user_identity: UserIdentity,
|
||||
account: Account,
|
||||
) -> Self {
|
||||
SessionVerificationController {
|
||||
encryption,
|
||||
user_identity,
|
||||
account,
|
||||
delegate: Arc::new(RwLock::new(None)),
|
||||
verification_request: Arc::new(RwLock::new(None)),
|
||||
sas_verification: Arc::new(RwLock::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn process_to_device_message(&self, event: AnyToDeviceEvent) {
|
||||
if let AnyToDeviceEvent::KeyVerificationRequest(event) = event {
|
||||
info!("Received verification request: {:}", event.sender);
|
||||
/// Ask the controller to process an incoming request based on the sender
|
||||
/// and flow identifier. It will fetch the request, verify that it's in the
|
||||
/// correct state and then and notify the delegate.
|
||||
pub(crate) async fn process_incoming_verification_request(
|
||||
&self,
|
||||
sender: &UserId,
|
||||
flow_id: impl AsRef<str>,
|
||||
) {
|
||||
let Some(request) = self.encryption.get_verification_request(sender, flow_id).await else {
|
||||
error!("Failed retrieving verification request");
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(request) = self
|
||||
.encryption
|
||||
.get_verification_request(&event.sender, &event.content.transaction_id)
|
||||
.await
|
||||
else {
|
||||
error!("Failed retrieving verification request");
|
||||
return;
|
||||
};
|
||||
let VerificationRequestState::Requested { other_device_data, .. } = request.state() else {
|
||||
error!("Received verification request event but the request is in the wrong state.");
|
||||
return;
|
||||
};
|
||||
|
||||
if !request.is_self_verification() {
|
||||
info!("Received non-self verification request. Ignoring.");
|
||||
return;
|
||||
}
|
||||
let Ok(user_profile) = self.account.fetch_user_profile_of(sender).await else {
|
||||
error!("Failed fetching user profile for verification request");
|
||||
return;
|
||||
};
|
||||
|
||||
let VerificationRequestState::Requested { other_device_data, .. } = request.state()
|
||||
else {
|
||||
error!("Received key verification event but the request is in the wrong state.");
|
||||
return;
|
||||
};
|
||||
if let Some(delegate) = &*self.delegate.read().unwrap() {
|
||||
delegate.did_receive_verification_request(SessionVerificationRequestDetails {
|
||||
sender_id: request.other_user_id().into(),
|
||||
sender_display_name: user_profile.displayname,
|
||||
flow_id: request.flow_id().into(),
|
||||
device_id: other_device_data.device_id().into(),
|
||||
device_display_name: other_device_data.display_name().map(str::to_string),
|
||||
first_seen_timestamp: other_device_data.first_time_seen_ts().into(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(delegate) = &*self.delegate.read().unwrap() {
|
||||
delegate.did_receive_verification_request(SessionVerificationRequestDetails {
|
||||
sender_id: request.other_user_id().into(),
|
||||
flow_id: request.flow_id().into(),
|
||||
device_id: other_device_data.device_id().into(),
|
||||
display_name: other_device_data.display_name().map(str::to_string),
|
||||
first_seen_timestamp: other_device_data.first_time_seen_ts().into(),
|
||||
});
|
||||
fn set_ongoing_verification_request(
|
||||
&self,
|
||||
verification_request: VerificationRequest,
|
||||
) -> Result<(), ClientError> {
|
||||
if let Some(ongoing_verification_request) =
|
||||
self.verification_request.read().unwrap().clone()
|
||||
{
|
||||
if !ongoing_verification_request.is_done()
|
||||
&& !ongoing_verification_request.is_cancelled()
|
||||
{
|
||||
return Err(ClientError::new("There is another verification flow ongoing."));
|
||||
}
|
||||
}
|
||||
|
||||
*self.verification_request.write().unwrap() = Some(verification_request.clone());
|
||||
|
||||
RUNTIME.spawn(Self::listen_to_verification_request_changes(
|
||||
verification_request,
|
||||
self.sas_verification.clone(),
|
||||
self.delegate.clone(),
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn listen_to_verification_request_changes(
|
||||
|
||||
Reference in New Issue
Block a user