From f0521f72d5255ffe2f08825dfeba08f70db07cf9 Mon Sep 17 00:00:00 2001 From: Daniel Abramov Date: Tue, 10 Oct 2023 21:17:05 +0200 Subject: [PATCH] widget: add serialize/deserialize for permissions Co-authored-by: Jonas Platte --- crates/matrix-sdk/src/widget/permissions.rs | 155 +++++++++++++++++++- 1 file changed, 153 insertions(+), 2 deletions(-) diff --git a/crates/matrix-sdk/src/widget/permissions.rs b/crates/matrix-sdk/src/widget/permissions.rs index b962bcbc4..cc80f82b0 100644 --- a/crates/matrix-sdk/src/widget/permissions.rs +++ b/crates/matrix-sdk/src/widget/permissions.rs @@ -15,9 +15,12 @@ //! Types and traits related to the permissions that a widget can request from a //! client. -use async_trait::async_trait; +use std::fmt; -use super::EventFilter; +use async_trait::async_trait; +use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer}; + +use super::{EventFilter, MessageLikeEventFilter, StateEventFilter}; /// Must be implemented by a component that provides functionality of deciding /// whether a widget is allowed to use certain capabilities (typically by @@ -44,3 +47,151 @@ pub struct Permissions { /// browser/tab/webview that is not connected to the postmessage widget-api. pub requires_client: bool, } + +const SEND_EVENT: &str = "org.matrix.msc2762.send.event"; +const READ_EVENT: &str = "org.matrix.msc2762.receive.event"; +const SEND_STATE: &str = "org.matrix.msc2762.send.state_event"; +const READ_STATE: &str = "org.matrix.msc2762.receive.state_event"; +const REQUIRES_CLIENT: &str = "io.element.requires_client"; + +impl Serialize for Permissions { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + struct PrintEventFilter<'a>(&'a EventFilter); + impl fmt::Display for PrintEventFilter<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + EventFilter::MessageLike(filter) => PrintMessageLikeEventFilter(filter).fmt(f), + EventFilter::State(filter) => PrintStateEventFilter(filter).fmt(f), + } + } + } + + struct PrintMessageLikeEventFilter<'a>(&'a MessageLikeEventFilter); + impl fmt::Display for PrintMessageLikeEventFilter<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + MessageLikeEventFilter::WithType(event_type) => { + // TODO: escape `#` as `\#` and `\` as `\\` in event_type + write!(f, "{event_type}") + } + MessageLikeEventFilter::RoomMessageWithMsgtype(msgtype) => { + write!(f, "m.room.message#{msgtype}") + } + } + } + } + + struct PrintStateEventFilter<'a>(&'a StateEventFilter); + impl fmt::Display for PrintStateEventFilter<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // TODO: escape `#` as `\#` and `\` as `\\` in event_type + match self.0 { + StateEventFilter::WithType(event_type) => write!(f, "{event_type}"), + StateEventFilter::WithTypeAndStateKey(event_type, state_key) => { + write!(f, "{event_type}#{state_key}") + } + } + } + } + + let seq_len = self.requires_client as usize + self.read.len() + self.send.len(); + let mut seq = serializer.serialize_seq(Some(seq_len))?; + + if self.requires_client { + seq.serialize_element(REQUIRES_CLIENT)?; + } + for filter in &self.read { + let name = match filter { + EventFilter::MessageLike(_) => READ_EVENT, + EventFilter::State(_) => READ_STATE, + }; + seq.serialize_element(&format!("{name}:{}", PrintEventFilter(filter)))?; + } + for filter in &self.send { + let name = match filter { + EventFilter::MessageLike(_) => SEND_EVENT, + EventFilter::State(_) => SEND_STATE, + }; + seq.serialize_element(&format!("{name}:{}", PrintEventFilter(filter)))?; + } + + seq.end() + } +} + +impl<'de> Deserialize<'de> for Permissions { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + enum Permission { + RequiresClient, + Read(EventFilter), + Send(EventFilter), + Unknown, + } + + impl<'de> Deserialize<'de> for Permission { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = ruma::serde::deserialize_cow_str(deserializer)?; + if s == REQUIRES_CLIENT { + return Ok(Self::RequiresClient); + } + + match s.split_once(':') { + Some((READ_EVENT, filter_s)) => Ok(Permission::Read(EventFilter::MessageLike( + parse_message_event_filter(filter_s), + ))), + Some((SEND_EVENT, filter_s)) => Ok(Permission::Send(EventFilter::MessageLike( + parse_message_event_filter(filter_s), + ))), + Some((READ_STATE, filter_s)) => { + Ok(Permission::Read(EventFilter::State(parse_state_event_filter(filter_s)))) + } + Some((SEND_STATE, filter_s)) => { + Ok(Permission::Send(EventFilter::State(parse_state_event_filter(filter_s)))) + } + _ => Ok(Self::Unknown), + } + } + } + + fn parse_message_event_filter(s: &str) -> MessageLikeEventFilter { + match s.strip_prefix("m.room.message#") { + Some(msgtype) => MessageLikeEventFilter::RoomMessageWithMsgtype(msgtype.to_owned()), + // TODO: Replace `\\` by `\` and `\#` by `#`, enforce no unescaped `#` + None => MessageLikeEventFilter::WithType(s.into()), + } + } + + fn parse_state_event_filter(s: &str) -> StateEventFilter { + // TODO: Search for un-escaped `#` only, replace `\\` by `\` and `\#` by `#` + match s.split_once('#') { + Some((event_type, state_key)) => { + StateEventFilter::WithTypeAndStateKey(event_type.into(), state_key.to_owned()) + } + None => StateEventFilter::WithType(s.into()), + } + } + + let mut permissions = + Permissions { read: Vec::new(), send: Vec::new(), requires_client: false }; + for permission in Vec::::deserialize(deserializer)? { + match permission { + Permission::RequiresClient => permissions.requires_client = true, + Permission::Read(filter) => permissions.read.push(filter), + Permission::Send(filter) => permissions.send.push(filter), + // ignore unknown permissions + Permission::Unknown => {} + } + } + + Ok(permissions) + } +}