widget: add serialize/deserialize for permissions

Co-authored-by: Jonas Platte <jplatte@matrix.org>
This commit is contained in:
Daniel Abramov
2023-10-10 21:17:05 +02:00
committed by Jonas Platte
parent 77cb1b7f11
commit f0521f72d5
+153 -2
View File
@@ -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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
enum Permission {
RequiresClient,
Read(EventFilter),
Send(EventFilter),
Unknown,
}
impl<'de> Deserialize<'de> for Permission {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
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::<Permission>::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)
}
}