From 3ce7126d584e04a4f7938fbf5de11383c623e8dc Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Thu, 19 Oct 2023 11:32:39 +0200 Subject: [PATCH] widget: Add support for read-state-event fromWidget requests Co-authored-by: Daniel Abramov --- .../src/widget/machine/driver_req.rs | 25 ++++-- .../src/widget/machine/from_widget.rs | 29 ++++++ crates/matrix-sdk/src/widget/machine/mod.rs | 89 +++++++++++++++++-- 3 files changed, 132 insertions(+), 11 deletions(-) diff --git a/crates/matrix-sdk/src/widget/machine/driver_req.rs b/crates/matrix-sdk/src/widget/machine/driver_req.rs index 7032b943f..3b61f298a 100644 --- a/crates/matrix-sdk/src/widget/machine/driver_req.rs +++ b/crates/matrix-sdk/src/widget/machine/driver_req.rs @@ -24,7 +24,7 @@ use ruma::{ use tracing::error; use super::{ - actions::{MatrixDriverRequestData, ReadMessageLikeEventCommand}, + actions::{MatrixDriverRequestData, ReadMessageLikeEventCommand, ReadStateEventCommand}, incoming::MatrixDriverResponse, MatrixDriverRequestMeta, SendEventCommand, WidgetMachine, }; @@ -129,15 +129,15 @@ impl FromMatrixDriverResponse for request_openid_token::v3::Response { /// Ask the client to read matrix event(s) that corresponds to the given /// description and return a list of events as a response. #[derive(Debug)] -pub(crate) struct ReadMatrixEvent(pub(crate) ReadMessageLikeEventCommand); +pub(crate) struct ReadMatrixMessageLikeEvent(pub(crate) ReadMessageLikeEventCommand); -impl From for MatrixDriverRequestData { - fn from(value: ReadMatrixEvent) -> Self { +impl From for MatrixDriverRequestData { + fn from(value: ReadMatrixMessageLikeEvent) -> Self { MatrixDriverRequestData::ReadMessageLikeEvent(value.0) } } -impl MatrixDriverRequest for ReadMatrixEvent { +impl MatrixDriverRequest for ReadMatrixMessageLikeEvent { type Response = Vec>; } @@ -153,6 +153,21 @@ impl FromMatrixDriverResponse for Vec> { } } +/// Ask the client to read matrix event(s) that corresponds to the given +/// description and return a list of events as a response. +#[derive(Debug)] +pub(crate) struct ReadMatrixStateEvent(pub(crate) ReadStateEventCommand); + +impl From for MatrixDriverRequestData { + fn from(value: ReadMatrixStateEvent) -> Self { + MatrixDriverRequestData::ReadStateEvent(value.0) + } +} + +impl MatrixDriverRequest for ReadMatrixStateEvent { + type Response = Vec>; +} + /// Ask the client to send matrix event that corresponds to the given /// description and return an event ID as a response. #[derive(Debug)] diff --git a/crates/matrix-sdk/src/widget/machine/from_widget.rs b/crates/matrix-sdk/src/widget/machine/from_widget.rs index 47b3c423d..de305993c 100644 --- a/crates/matrix-sdk/src/widget/machine/from_widget.rs +++ b/crates/matrix-sdk/src/widget/machine/from_widget.rs @@ -14,13 +14,21 @@ use std::fmt; +use ruma::{ + events::{AnyTimelineEvent, MessageLikeEventType, StateEventType}, + serde::Raw, +}; use serde::{Deserialize, Serialize}; +use crate::widget::StateKeySelector; + #[derive(Deserialize)] #[serde(tag = "action", rename_all = "snake_case", content = "data")] pub(super) enum FromWidgetRequest { SupportedApiVersions {}, ContentLoaded {}, + #[serde(rename = "org.matrix.msc2876.read_events")] + ReadEvent(ReadEventRequest), } #[derive(Serialize)] @@ -97,3 +105,24 @@ pub(super) enum ApiVersion { #[serde(rename = "town.robin.msc3846")] MSC3846, } + +#[derive(Deserialize)] +#[serde(untagged)] +pub(super) enum ReadEventRequest { + ReadStateEvent { + #[serde(rename = "type")] + event_type: StateEventType, + state_key: StateKeySelector, + }, + #[allow(dead_code)] + ReadMessageLikeEvent { + #[serde(rename = "type")] + event_type: MessageLikeEventType, + limit: Option, + }, +} + +#[derive(Debug, Serialize)] +pub(super) struct ReadEventResponse { + pub(super) events: Vec>, +} diff --git a/crates/matrix-sdk/src/widget/machine/mod.rs b/crates/matrix-sdk/src/widget/machine/mod.rs index ec83cc237..7d35de2f3 100644 --- a/crates/matrix-sdk/src/widget/machine/mod.rs +++ b/crates/matrix-sdk/src/widget/machine/mod.rs @@ -16,6 +16,8 @@ #![warn(unreachable_pub)] +use std::fmt; + use indexmap::{map::Entry, IndexMap}; use ruma::serde::{JsonObject, Raw}; use serde::Serialize; @@ -25,14 +27,26 @@ use tracing::{error, info_span, instrument, trace, warn}; use uuid::Uuid; use self::{ - driver_req::{AcquireCapabilities, MatrixDriverRequest, MatrixDriverRequestHandle}, - from_widget::{FromWidgetErrorResponse, FromWidgetRequest, SupportedApiVersionsResponse}, + actions::ReadStateEventCommand, + driver_req::{ + AcquireCapabilities, MatrixDriverRequest, MatrixDriverRequestHandle, ReadMatrixStateEvent, + }, + from_widget::{ + FromWidgetErrorResponse, FromWidgetRequest, ReadEventRequest, ReadEventResponse, + SupportedApiVersionsResponse, + }, incoming::{IncomingWidgetMessage, IncomingWidgetMessageKind}, to_widget::{ NotifyPermissionsChanged, RequestPermissions, ToWidgetRequest, ToWidgetRequestHandle, ToWidgetResponse, }, }; +#[cfg(doc)] +use super::WidgetDriver; +use super::{ + filter::{MatrixEventContent, MatrixEventFilterInput}, + Capabilities, StateKeySelector, +}; mod actions; mod driver_req; @@ -47,9 +61,6 @@ pub(crate) use self::{ actions::{Action, MatrixDriverRequestData, SendEventCommand}, incoming::{IncomingMessage, MatrixDriverResponse}, }; -use super::Capabilities; -#[cfg(doc)] -use super::WidgetDriver; /// No I/O state machine. /// @@ -134,7 +145,7 @@ impl WidgetMachine { let request = match raw_request.deserialize() { Ok(r) => r, Err(e) => { - self.send_from_widget_response(raw_request, FromWidgetErrorResponse::new(e)); + self.send_from_widget_error_response(raw_request, e); return; } }; @@ -143,12 +154,70 @@ impl WidgetMachine { FromWidgetRequest::SupportedApiVersions {} => { self.send_from_widget_response(raw_request, SupportedApiVersionsResponse::new()); } + FromWidgetRequest::ContentLoaded {} => { self.send_from_widget_response(raw_request, JsonObject::new()); if self.capabilities.is_unset() { self.negotiate_capabilities(); } } + + FromWidgetRequest::ReadEvent(req) => { + self.process_read_event_request(req, raw_request); + } + } + } + + fn process_read_event_request( + &mut self, + request: ReadEventRequest, + raw_request: Raw, + ) { + let CapabilitiesState::Negotiated(capabilities) = &self.capabilities else { + self.send_from_widget_error_response( + raw_request, + "Received read event request before capabilities were negotiated", + ); + return; + }; + + match request { + ReadEventRequest::ReadMessageLikeEvent { .. } => { + self.send_from_widget_error_response( + raw_request, + "Reading of message events is not yet supported", + ); + } + ReadEventRequest::ReadStateEvent { event_type, state_key } => { + let allowed = match &state_key { + StateKeySelector::Any => capabilities + .read + .iter() + .any(|filter| filter.matches_state_event_with_any_state_key(&event_type)), + + StateKeySelector::Key(state_key) => { + let filter_in = MatrixEventFilterInput { + event_type: event_type.to_string().into(), + state_key: Some(state_key.clone()), + // content doesn't matter for state events + content: MatrixEventContent::default(), + }; + + capabilities.read.iter().any(|filter| filter.matches(&filter_in)) + } + }; + + if allowed { + let request = + ReadMatrixStateEvent(ReadStateEventCommand { event_type, state_key }); + self.send_matrix_driver_request(request).then(|events, machine| { + machine + .send_from_widget_response(raw_request, ReadEventResponse { events }); + }); + } else { + self.send_from_widget_error_response(raw_request, "Not allowed"); + } + } } } @@ -240,6 +309,14 @@ impl WidgetMachine { } } + fn send_from_widget_error_response( + &self, + raw_request: Raw, + error: impl fmt::Display, + ) { + self.send_from_widget_response(raw_request, FromWidgetErrorResponse::new(error)) + } + #[instrument(skip_all, fields(action = T::ACTION))] fn send_to_widget_request( &mut self,