State-reset and security mitigations.

Upgrade Ruma to present.

The following are intentionally benign for activation in a later commit:

- Hydra backports not default.
- Room version 12 not default.
- Room version 12 not listed as stable.

Do not enable them manually or you can brick your database.

Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
Jason Volk
2025-06-29 03:33:29 +00:00
parent 2c6dd78502
commit 628597c318
134 changed files with 14961 additions and 4935 deletions

View File

@@ -0,0 +1,152 @@
//! Types to deserialize `m.room.create` events.
use std::{borrow::Cow, iter, ops::Deref};
use ruma::{
OwnedUserId, RoomVersionId, UserId, room_version_rules::AuthorizationRules,
serde::from_raw_json_value,
};
use serde::{Deserialize, de::IgnoredAny};
use crate::{Error, Result, err, matrix::Event};
/// A helper type for an [`Event`] of type `m.room.create`.
///
/// This is a type that deserializes each field lazily, when requested.
#[derive(Debug)]
pub struct RoomCreateEvent<E: Event>(E);
impl<E: Event> RoomCreateEvent<E> {
/// Construct a new `RoomCreateEvent` around the given event.
#[inline]
pub fn new(event: E) -> Self { Self(event) }
/// The version of the room.
#[allow(dead_code)]
pub fn room_version(&self) -> Result<RoomVersionId> {
#[derive(Deserialize)]
struct RoomCreateContentRoomVersion {
#[allow(dead_code)]
room_version: Option<RoomVersionId>,
}
let content: RoomCreateContentRoomVersion =
from_raw_json_value(self.content()).map_err(|err: Error| {
err!("invalid `room_version` field in `m.room.create` event: {err}")
})?;
Ok(content.room_version.unwrap_or(RoomVersionId::V1))
}
/// Whether the room is federated.
pub fn federate(&self) -> Result<bool> {
#[derive(Deserialize)]
struct RoomCreateContentFederate {
#[serde(rename = "m.federate")]
federate: Option<bool>,
}
let content: RoomCreateContentFederate =
from_raw_json_value(self.content()).map_err(|err: Error| {
err!("invalid `m.federate` field in `m.room.create` event: {err}")
})?;
Ok(content.federate.unwrap_or(true))
}
/// The creator of the room.
///
/// If the `use_room_create_sender` field of `AuthorizationRules` is set,
/// the creator is the sender of this `m.room.create` event, otherwise it
/// is deserialized from the `creator` field of this event's content.
pub fn creator(&self, rules: &AuthorizationRules) -> Result<Cow<'_, UserId>> {
#[derive(Deserialize)]
struct RoomCreateContentCreator {
creator: OwnedUserId,
}
if rules.use_room_create_sender {
Ok(Cow::Borrowed(self.sender()))
} else {
let content: RoomCreateContentCreator =
from_raw_json_value(self.content()).map_err(|err: Error| {
err!("missing or invalid `creator` field in `m.room.create` event: {err}")
})?;
Ok(Cow::Owned(content.creator))
}
}
/// The creators of the room.
///
/// If the `use_room_create_sender` field of `AuthorizationRules` is set,
/// the creator is the sender of this `m.room.create` event, otherwise it
/// is deserialized from the `creator` field of this event's content.
/// Additionally if the `explicitly_privilege_room_creators`
/// field of `AuthorizationRules` is set, any additional user IDs in
/// `additional_creators`, if present, will also be considered creators.
pub fn creators<'a>(
&'a self,
rules: &'a AuthorizationRules,
) -> Result<impl Iterator<Item = OwnedUserId> + Clone + use<'a, E>> {
let initial = self.creator(rules)?.into_owned();
let additional = self.additional_creators(rules)?;
Ok(iter::once(initial).chain(additional))
}
/// The additional creators of the room (if any).
///
/// If the `explicitly_privilege_room_creators`
/// field of `AuthorizationRules` is set, any additional user IDs in
/// `additional_creators`, if present, will also be considered creators.
///
/// This function ignores the primary room creator, and should only be used
/// in `check_room_member_join`. Otherwise, you should use `creators`
/// instead.
pub(super) fn additional_creators(
&self,
rules: &AuthorizationRules,
) -> Result<impl Iterator<Item = OwnedUserId> + Clone> {
#[derive(Deserialize)]
struct RoomCreateContentAdditionalCreators {
#[serde(default)]
additional_creators: Vec<OwnedUserId>,
}
Ok(if rules.additional_room_creators {
let mut content: RoomCreateContentAdditionalCreators =
from_raw_json_value(self.content()).map_err(|err: serde_json::Error| {
err!("invalid `additional_creators` field in `m.room.create` event: {err}")
})?;
content.additional_creators.sort();
content.additional_creators.dedup();
content.additional_creators.into_iter()
} else {
Vec::new().into_iter()
})
}
/// Whether the event has a `creator` field.
pub fn has_creator(&self) -> Result<bool> {
#[derive(Deserialize)]
struct RoomCreateContentCreator {
creator: Option<IgnoredAny>,
}
let content: RoomCreateContentCreator =
from_raw_json_value(self.content()).map_err(|err: Error| {
err!("invalid `creator` field in `m.room.create` event: {err}")
})?;
Ok(content.creator.is_some())
}
}
impl<E: Event> Deref for RoomCreateEvent<E> {
type Target = E;
#[inline]
fn deref(&self) -> &Self::Target { &self.0 }
}

View File

@@ -0,0 +1,74 @@
//! Types to deserialize `m.room.join_rules` events.
use std::ops::Deref;
use ruma::serde::{PartialEqAsRefStr, StringEnum, from_raw_json_value};
use serde::Deserialize;
use crate::{Error, Result, err, matrix::Event};
/// A helper type for an [`Event`] of type `m.room.join_rules`.
///
/// This is a type that deserializes each field lazily, when requested.
#[derive(Debug, Clone)]
pub struct RoomJoinRulesEvent<E: Event>(E);
impl<E: Event> RoomJoinRulesEvent<E> {
/// Construct a new `RoomJoinRulesEvent` around the given event.
#[inline]
pub fn new(event: E) -> Self { Self(event) }
/// The join rule of the room.
pub fn join_rule(&self) -> Result<JoinRule> {
#[derive(Deserialize)]
struct RoomJoinRulesContentJoinRule {
join_rule: JoinRule,
}
let content: RoomJoinRulesContentJoinRule =
from_raw_json_value(self.content()).map_err(|err: Error| {
err!("missing or invalid `join_rule` field in `m.room.join_rules` event: {err}")
})?;
Ok(content.join_rule)
}
}
impl<E: Event> Deref for RoomJoinRulesEvent<E> {
type Target = E;
#[inline]
fn deref(&self) -> &Self::Target { &self.0 }
}
/// The possible values for the join rule of a room.
#[derive(Clone, StringEnum, PartialEqAsRefStr)]
#[ruma_enum(rename_all = "snake_case")]
#[non_exhaustive]
pub enum JoinRule {
/// `public`
Public,
/// `invite`
Invite,
/// `knock`
Knock,
/// `restricted`
Restricted,
/// `KnockRestricted`
KnockRestricted,
#[doc(hidden)]
_Custom(PrivOwnedStr),
}
impl Eq for JoinRule {}
// Wrapper around `Box<str>` that cannot be used in a meaningful way outside of
// this crate. Used for string enums because their `_Custom` variant can't be
// truly private (only `#[doc(hidden)]`).
#[derive(Debug, Clone)]
pub struct PrivOwnedStr(Box<str>);

View File

@@ -0,0 +1,207 @@
//! Types to deserialize `m.room.member` events.
use std::ops::Deref;
use ruma::{
CanonicalJsonObject, OwnedUserId, events::room::member::MembershipState,
serde::from_raw_json_value, signatures::canonical_json,
};
use serde::Deserialize;
use serde_json::value::RawValue as RawJsonValue;
use crate::{Err, Error, Result, debug_error, err, matrix::Event};
/// A helper type for an [`Event`] of type `m.room.member`.
///
/// This is a type that deserializes each field lazily, as requested.
#[derive(Debug, Clone)]
pub struct RoomMemberEvent<E: Event>(E);
impl<E: Event> RoomMemberEvent<E> {
/// Construct a new `RoomMemberEvent` around the given event.
#[inline]
pub fn new(event: E) -> Self { Self(event) }
/// The membership of the user.
#[inline]
pub fn membership(&self) -> Result<MembershipState> {
RoomMemberEventContent(self.content()).membership()
}
/// If this is a `join` event, the ID of a user on the homeserver that
/// authorized it.
#[inline]
pub fn join_authorised_via_users_server(&self) -> Result<Option<OwnedUserId>> {
RoomMemberEventContent(self.content()).join_authorised_via_users_server()
}
/// If this is an `invite` event, details about the third-party invite that
/// resulted in this event.
#[inline]
pub fn third_party_invite(&self) -> Result<Option<ThirdPartyInvite>> {
RoomMemberEventContent(self.content()).third_party_invite()
}
}
impl<E: Event> Deref for RoomMemberEvent<E> {
type Target = E;
#[inline]
fn deref(&self) -> &Self::Target { &self.0 }
}
/// Helper trait for `Option<RoomMemberEvent<E>>`.
pub(crate) trait RoomMemberEventResultExt {
/// The membership of the user.
///
/// Defaults to `leave` if there is no `m.room.member` event.
fn membership(&self) -> Result<MembershipState>;
}
impl<E: Event> RoomMemberEventResultExt for Result<RoomMemberEvent<E>> {
fn membership(&self) -> Result<MembershipState> {
match self {
| Ok(event) => event.membership(),
| Err(e) if e.is_not_found() => Ok(MembershipState::Leave),
| Err(e) if cfg!(test) => panic!("membership(): unexpected: {e}"),
| Err(e) => {
debug_error!("membership(): unexpected: {e}");
Ok(MembershipState::Leave)
},
}
}
}
/// A helper type for the raw JSON content of an event of type `m.room.member`.
pub struct RoomMemberEventContent<'a>(&'a RawJsonValue);
impl<'a> RoomMemberEventContent<'a> {
/// Construct a new `RoomMemberEventContent` around the given raw JSON
/// content.
#[inline]
#[must_use]
pub fn new(content: &'a RawJsonValue) -> Self { Self(content) }
}
impl RoomMemberEventContent<'_> {
/// The membership of the user.
pub fn membership(&self) -> Result<MembershipState> {
#[derive(Deserialize)]
struct RoomMemberContentMembership {
membership: MembershipState,
}
let content: RoomMemberContentMembership =
from_raw_json_value(self.0).map_err(|err: Error| {
err!(Request(InvalidParam(
"missing or invalid `membership` field in `m.room.member` event: {err}"
)))
})?;
Ok(content.membership)
}
/// If this is a `join` event, the ID of a user on the homeserver that
/// authorized it.
pub fn join_authorised_via_users_server(&self) -> Result<Option<OwnedUserId>> {
#[derive(Deserialize)]
struct RoomMemberContentJoinAuthorizedViaUsersServer {
join_authorised_via_users_server: Option<OwnedUserId>,
}
let content: RoomMemberContentJoinAuthorizedViaUsersServer = from_raw_json_value(self.0)
.map_err(|err: Error| {
err!(Request(InvalidParam(
"invalid `join_authorised_via_users_server` field in `m.room.member` event: \
{err}"
)))
})?;
Ok(content.join_authorised_via_users_server)
}
/// If this is an `invite` event, details about the third-party invite that
/// resulted in this event.
pub fn third_party_invite(&self) -> Result<Option<ThirdPartyInvite>> {
#[derive(Deserialize)]
struct RoomMemberContentThirdPartyInvite {
third_party_invite: Option<ThirdPartyInvite>,
}
let content: RoomMemberContentThirdPartyInvite =
from_raw_json_value(self.0).map_err(|err: Error| {
err!(Request(InvalidParam(
"invalid `third_party_invite` field in `m.room.member` event: {err}"
)))
})?;
Ok(content.third_party_invite)
}
}
/// Details about a third-party invite.
#[derive(Deserialize)]
pub struct ThirdPartyInvite {
/// Signed details about the third-party invite.
signed: CanonicalJsonObject,
}
impl ThirdPartyInvite {
/// The unique identifier for the third-party invite.
pub fn token(&self) -> Result<&str> {
let Some(token_value) = self.signed.get("token") else {
return Err!(Request(InvalidParam(
"missing `token` field in `third_party_invite.signed` of `m.room.member` event"
)));
};
token_value.as_str().ok_or_else(|| {
err!(Request(InvalidParam(
"unexpected format of `token` field in `third_party_invite.signed` of \
`m.room.member` event: expected string, got {token_value:?}"
)))
})
}
/// The Matrix ID of the user that was invited.
pub fn mxid(&self) -> Result<&str> {
let Some(mxid_value) = self.signed.get("mxid") else {
return Err!(Request(InvalidParam(
"missing `mxid` field in `third_party_invite.signed` of `m.room.member` event"
)));
};
mxid_value.as_str().ok_or_else(|| {
err!(Request(InvalidParam(
"unexpected format of `mxid` field in `third_party_invite.signed` of \
`m.room.member` event: expected string, got {mxid_value:?}"
)))
})
}
/// The signatures of the event.
pub fn signatures(&self) -> Result<&CanonicalJsonObject> {
let Some(signatures_value) = self.signed.get("signatures") else {
return Err!(Request(InvalidParam(
"missing `signatures` field in `third_party_invite.signed` of `m.room.member` \
event"
)));
};
signatures_value.as_object().ok_or_else(|| {
err!(Request(InvalidParam(
"unexpected format of `signatures` field in `third_party_invite.signed` of \
`m.room.member` event: expected object, got {signatures_value:?}"
)))
})
}
/// The `signed` object as canonical JSON string to verify the signatures.
pub fn signed_canonical_json(&self) -> Result<String> {
canonical_json(&self.signed).map_err(|error| {
err!(Request(InvalidParam(
"invalid `third_party_invite.signed` field in `m.room.member` event: {error}"
)))
})
}
}

View File

@@ -0,0 +1,401 @@
//! Types to deserialize `m.room.power_levels` events.
use std::ops::Deref;
use ruma::{
Int, OwnedUserId, UserId,
events::{TimelineEventType, room::power_levels::UserPowerLevel},
int,
room_version_rules::AuthorizationRules,
serde::{
DebugAsRefStr, DisplayAsRefStr, JsonObject, OrdAsRefStr, PartialEqAsRefStr,
PartialOrdAsRefStr, deserialize_v1_powerlevel, from_raw_json_value,
vec_deserialize_int_powerlevel_values, vec_deserialize_v1_powerlevel_values,
},
};
use serde::de::DeserializeOwned;
use serde_json::{Error, from_value as from_json_value};
use crate::{Result, err, is_equal_to, matrix::Event, ref_at};
/// The default value of the creator's power level.
const DEFAULT_CREATOR_POWER_LEVEL: i32 = 100;
/// A helper type for an [`Event`] of type `m.room.power_levels`.
#[derive(Clone, Debug)]
pub struct RoomPowerLevelsEvent<E: Event>(E);
impl<E: Event> RoomPowerLevelsEvent<E> {
/// Construct a new `RoomPowerLevelsEvent` around the given event.
#[inline]
pub fn new(event: E) -> Self { Self(event) }
/// The deserialized content of the event.
fn deserialized_content(&self) -> Result<JsonObject> {
from_raw_json_value(self.content()).map_err(|error: Error| {
err!(Request(InvalidParam("malformed `m.room.power_levels` content: {error}")))
})
}
/// Get the value of a field that should contain an integer, if any.
///
/// The deserialization of this field is cached in memory.
pub(crate) fn get_as_int(
&self,
field: RoomPowerLevelsIntField,
rules: &AuthorizationRules,
) -> Result<Option<Int>> {
let content = self.deserialized_content()?;
let Some(value) = content.get(field.as_str()) else {
return Ok(None);
};
let res = if rules.integer_power_levels {
from_json_value(value.clone())
} else {
deserialize_v1_powerlevel(value)
};
let power_level = res.map(Some).map_err(|error| {
err!(Request(InvalidParam(
"unexpected format of `{field}` field in `content` of `m.room.power_levels` \
event: {error}"
)))
})?;
Ok(power_level)
}
/// Get the value of a field that should contain an integer, or its default
/// value if it is absent.
#[inline]
pub(crate) fn get_as_int_or_default(
&self,
field: RoomPowerLevelsIntField,
rules: &AuthorizationRules,
) -> Result<Int> {
Ok(self
.get_as_int(field, rules)?
.unwrap_or_else(|| field.default_value()))
}
/// Get the value of a field that should contain a map of any value to
/// integer, if any.
fn get_as_int_map<T: Ord + DeserializeOwned>(
&self,
field: &str,
rules: &AuthorizationRules,
) -> Result<Option<Vec<(T, Int)>>> {
let content = self.deserialized_content()?;
let Some(value) = content.get(field) else {
return Ok(None);
};
let res = if rules.integer_power_levels {
vec_deserialize_int_powerlevel_values(value)
} else {
vec_deserialize_v1_powerlevel_values(value)
};
res.map(Some).map_err(|error| {
err!(Request(InvalidParam(
"unexpected format of `{field}` field in `content` of `m.room.power_levels` \
event: {error}"
)))
})
}
/// Get the power levels required to send events, if any.
#[inline]
pub(crate) fn events(
&self,
rules: &AuthorizationRules,
) -> Result<Option<Vec<(TimelineEventType, Int)>>> {
self.get_as_int_map("events", rules)
}
/// Get the power levels required to trigger notifications, if any.
#[inline]
pub(crate) fn notifications(
&self,
rules: &AuthorizationRules,
) -> Result<Option<Vec<(String, Int)>>> {
self.get_as_int_map("notifications", rules)
}
/// Get the power levels of the users, if any.
///
/// The deserialization of this field is cached in memory.
#[inline]
pub(crate) fn users(
&self,
rules: &AuthorizationRules,
) -> Result<Option<Vec<(OwnedUserId, Int)>>> {
self.get_as_int_map("users", rules)
}
/// Get the power level of the user with the given ID.
///
/// Calling this method several times should be cheap because the necessary
/// deserialization results are cached.
pub(crate) fn user_power_level(
&self,
user_id: &UserId,
rules: &AuthorizationRules,
) -> Result<UserPowerLevel> {
let power_level = if let Some(power_level) = self
.users(rules)?
.as_ref()
.and_then(|users| get_value(users, user_id))
{
Ok(*power_level)
} else {
self.get_as_int_or_default(RoomPowerLevelsIntField::UsersDefault, rules)
};
power_level.map(Into::into)
}
/// Get the power level required to send an event of the given type.
pub(crate) fn event_power_level(
&self,
event_type: &TimelineEventType,
state_key: Option<&str>,
rules: &AuthorizationRules,
) -> Result<Int> {
let events = self.events(rules)?;
if let Some(power_level) = events
.as_ref()
.and_then(|events| get_value(events, event_type))
{
return Ok(*power_level);
}
let default_field = if state_key.is_some() {
RoomPowerLevelsIntField::StateDefault
} else {
RoomPowerLevelsIntField::EventsDefault
};
self.get_as_int_or_default(default_field, rules)
}
/// Get a map of all the fields with an integer value in the `content` of an
/// `m.room.power_levels` event.
pub(crate) fn int_fields_map(
&self,
rules: &AuthorizationRules,
) -> Result<Vec<(RoomPowerLevelsIntField, Int)>> {
RoomPowerLevelsIntField::ALL
.iter()
.copied()
.filter_map(|field| match self.get_as_int(field, rules) {
| Ok(value) => value.map(|value| Ok((field, value))),
| Err(error) => Some(Err(error)),
})
.collect()
}
}
impl<E: Event> Deref for RoomPowerLevelsEvent<E> {
type Target = E;
#[inline]
fn deref(&self) -> &Self::Target { &self.0 }
}
/// Helper trait for `Option<RoomPowerLevelsEvent<E>>`.
pub(crate) trait RoomPowerLevelsEventOptionExt {
/// Get the power level of the user with the given ID.
fn user_power_level(
&self,
user_id: &UserId,
creators: impl Iterator<Item = OwnedUserId>,
rules: &AuthorizationRules,
) -> Result<UserPowerLevel>;
/// Get the value of a field that should contain an integer, or its default
/// value if it is absent.
fn get_as_int_or_default(
&self,
field: RoomPowerLevelsIntField,
rules: &AuthorizationRules,
) -> Result<Int>;
/// Get the power level required to send an event of the given type.
fn event_power_level(
&self,
event_type: &TimelineEventType,
state_key: Option<&str>,
rules: &AuthorizationRules,
) -> Result<Int>;
}
impl<E> RoomPowerLevelsEventOptionExt for Option<RoomPowerLevelsEvent<E>>
where
E: Event,
{
fn user_power_level(
&self,
user_id: &UserId,
mut creators: impl Iterator<Item = OwnedUserId>,
rules: &AuthorizationRules,
) -> Result<UserPowerLevel> {
if rules.explicitly_privilege_room_creators && creators.any(is_equal_to!(user_id)) {
Ok(UserPowerLevel::Infinite)
} else if let Some(room_power_levels_event) = self {
room_power_levels_event.user_power_level(user_id, rules)
} else {
let power_level = if creators.any(is_equal_to!(user_id)) {
DEFAULT_CREATOR_POWER_LEVEL.into()
} else {
RoomPowerLevelsIntField::UsersDefault.default_value()
};
Ok(power_level.into())
}
}
fn get_as_int_or_default(
&self,
field: RoomPowerLevelsIntField,
rules: &AuthorizationRules,
) -> Result<Int> {
if let Some(room_power_levels_event) = self {
room_power_levels_event.get_as_int_or_default(field, rules)
} else {
Ok(field.default_value())
}
}
fn event_power_level(
&self,
event_type: &TimelineEventType,
state_key: Option<&str>,
rules: &AuthorizationRules,
) -> Result<Int> {
if let Some(room_power_levels_event) = self {
room_power_levels_event.event_power_level(event_type, state_key, rules)
} else {
let default_field = if state_key.is_some() {
RoomPowerLevelsIntField::StateDefault
} else {
RoomPowerLevelsIntField::EventsDefault
};
Ok(default_field.default_value())
}
}
}
#[inline]
pub(crate) fn get_value<'a, K, V, B>(vec: &'a [(K, V)], key: &'a B) -> Option<&'a V>
where
&'a K: PartialEq<&'a B>,
B: ?Sized,
{
position(vec, key)
.and_then(|i| vec.get(i))
.map(ref_at!(1))
}
#[inline]
pub(crate) fn contains_key<'a, K, V, B>(vec: &'a [(K, V)], key: &'a B) -> bool
where
&'a K: PartialEq<&'a B>,
B: ?Sized,
{
position(vec, key).is_some()
}
fn position<'a, K, V, B>(vec: &'a [(K, V)], key: &'a B) -> Option<usize>
where
&'a K: PartialEq<&'a B>,
B: ?Sized,
{
vec.iter()
.map(ref_at!(0))
.position(is_equal_to!(key))
}
/// Fields in the `content` of an `m.room.power_levels` event with an integer
/// value.
#[derive(
DebugAsRefStr,
Clone,
Copy,
DisplayAsRefStr,
PartialEqAsRefStr,
Eq,
PartialOrdAsRefStr,
OrdAsRefStr,
)]
#[non_exhaustive]
pub enum RoomPowerLevelsIntField {
/// `users_default`
UsersDefault,
/// `events_default`
EventsDefault,
/// `state_default`
StateDefault,
/// `ban`
Ban,
/// `redact`
Redact,
/// `kick`
Kick,
/// `invite`
Invite,
}
impl RoomPowerLevelsIntField {
/// A slice containing all the variants.
pub const ALL: &[Self] = &[
Self::UsersDefault,
Self::EventsDefault,
Self::StateDefault,
Self::Ban,
Self::Redact,
Self::Kick,
Self::Invite,
];
/// The string representation of this field.
#[inline]
#[must_use]
pub fn as_str(&self) -> &str { self.as_ref() }
/// The default value for this field if it is absent.
#[inline]
#[must_use]
pub fn default_value(self) -> Int {
match self {
| Self::UsersDefault | Self::EventsDefault | Self::Invite => int!(0),
| Self::StateDefault | Self::Kick | Self::Ban | Self::Redact => int!(50),
}
}
}
impl AsRef<str> for RoomPowerLevelsIntField {
#[inline]
fn as_ref(&self) -> &'static str {
match self {
| Self::UsersDefault => "users_default",
| Self::EventsDefault => "events_default",
| Self::StateDefault => "state_default",
| Self::Ban => "ban",
| Self::Redact => "redact",
| Self::Kick => "kick",
| Self::Invite => "invite",
}
}
}

View File

@@ -0,0 +1,61 @@
//! Types to deserialize `m.room.third_party_invite` events.
use std::{collections::BTreeSet, ops::Deref};
use ruma::{serde::from_raw_json_value, third_party_invite::IdentityServerBase64PublicKey};
use serde::Deserialize;
use crate::{Error, Result, err, matrix::Event};
/// A helper type for an [`Event`] of type `m.room.third_party_invite`.
///
/// This is a type that deserializes each field lazily, when requested.
#[derive(Debug, Clone)]
pub struct RoomThirdPartyInviteEvent<E: Event>(E);
impl<E: Event> RoomThirdPartyInviteEvent<E> {
/// Construct a new `RoomThirdPartyInviteEvent` around the given event.
pub fn new(event: E) -> Self { Self(event) }
/// The public keys of the identity server that might be used to sign the
/// third-party invite.
pub(crate) fn public_keys(&self) -> Result<BTreeSet<IdentityServerBase64PublicKey>> {
#[derive(Deserialize)]
struct RoomThirdPartyInviteContentPublicKeys {
public_key: Option<IdentityServerBase64PublicKey>,
#[serde(default)]
public_keys: Vec<PublicKey>,
}
#[derive(Deserialize)]
struct PublicKey {
public_key: IdentityServerBase64PublicKey,
}
let content: RoomThirdPartyInviteContentPublicKeys = from_raw_json_value(self.content())
.map_err(|err: Error| {
err!(
"invalid `public_key` or `public_keys` in `m.room.third_party_invite` \
event: {err}"
)
})?;
let public_keys = content
.public_keys
.into_iter()
.map(|k| k.public_key);
Ok(content
.public_key
.into_iter()
.chain(public_keys)
.collect())
}
}
impl<E: Event> Deref for RoomThirdPartyInviteEvent<E> {
type Target = E;
#[inline]
fn deref(&self) -> &Self::Target { &self.0 }
}