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:
152
src/core/matrix/state_res/events/create.rs
Normal file
152
src/core/matrix/state_res/events/create.rs
Normal 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 }
|
||||
}
|
||||
74
src/core/matrix/state_res/events/join_rules.rs
Normal file
74
src/core/matrix/state_res/events/join_rules.rs
Normal 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>);
|
||||
207
src/core/matrix/state_res/events/member.rs
Normal file
207
src/core/matrix/state_res/events/member.rs
Normal 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}"
|
||||
)))
|
||||
})
|
||||
}
|
||||
}
|
||||
401
src/core/matrix/state_res/events/power_levels.rs
Normal file
401
src/core/matrix/state_res/events/power_levels.rs
Normal 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",
|
||||
}
|
||||
}
|
||||
}
|
||||
61
src/core/matrix/state_res/events/third_party_invite.rs
Normal file
61
src/core/matrix/state_res/events/third_party_invite.rs
Normal 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 }
|
||||
}
|
||||
Reference in New Issue
Block a user