events: Update m.room.member for room versions 7 - 9

* Add join_authorized_via_users_server key for roomver 8+
* Add custom redaction impl for RoomMemberEventContent
This commit is contained in:
Devin Ragotzy 2021-11-26 17:11:28 -05:00 committed by GitHub
parent bba7d62442
commit d15c877ad1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 137 additions and 5 deletions

View File

@ -21,7 +21,7 @@ markdown = ["pulldown-cmark"]
unstable-exhaustive-types = []
unstable-pdu = []
unstable-pre-spec = []
unstable-pre-spec = ["ruma-identifiers/unstable-pre-spec"]
[dependencies]
criterion = { version = "0.3.3", optional = true }

View File

@ -3,11 +3,15 @@
use std::collections::BTreeMap;
use ruma_events_macros::EventContent;
use ruma_identifiers::{MxcUri, ServerName, ServerSigningKeyId, UserId};
use ruma_identifiers::{MxcUri, RoomVersionId, ServerName, ServerSigningKeyId, UserId};
use ruma_serde::StringEnum;
use serde::{Deserialize, Serialize};
use serde_json::value::RawValue as RawJsonValue;
use crate::{StrippedStateEvent, SyncStateEvent};
use crate::{
EventContent, HasDeserializeFields, RedactContent, RedactedEventContent,
RedactedStateEventContent, StrippedStateEvent, SyncStateEvent,
};
/// The content of an `m.room.member` event.
///
@ -38,7 +42,7 @@ use crate::{StrippedStateEvent, SyncStateEvent};
/// must be assumed as leave.
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "m.room.member", kind = State)]
#[ruma_event(type = "m.room.member", kind = State, custom_redacted)]
pub struct RoomMemberEventContent {
/// The avatar URL for this user, if any.
///
@ -63,7 +67,6 @@ pub struct RoomMemberEventContent {
pub is_direct: Option<bool>,
/// The membership state of this user.
#[ruma_event(skip_redaction)]
pub membership: MembershipState,
/// If this member event is the successor to a third party invitation, this field will
@ -92,6 +95,12 @@ pub struct RoomMemberEventContent {
#[cfg(feature = "unstable-pre-spec")]
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
/// Arbitrarily chosen `UserId` (MxID) of a local user who can send an invite.
#[cfg(feature = "unstable-pre-spec")]
#[serde(rename = "join_authorised_via_users_server")]
#[serde(skip_serializing_if = "Option::is_none")]
pub join_authorized_via_users_server: Option<Box<UserId>>,
}
impl RoomMemberEventContent {
@ -107,10 +116,86 @@ impl RoomMemberEventContent {
blurhash: None,
#[cfg(feature = "unstable-pre-spec")]
reason: None,
#[cfg(feature = "unstable-pre-spec")]
join_authorized_via_users_server: None,
}
}
}
impl RedactContent for RoomMemberEventContent {
type Redacted = RedactedRoomMemberEventContent;
fn redact(self, _version: &RoomVersionId) -> RedactedRoomMemberEventContent {
RedactedRoomMemberEventContent {
membership: self.membership,
#[cfg(feature = "unstable-pre-spec")]
join_authorized_via_users_server: match _version {
#[cfg(feature = "unstable-pre-spec")]
RoomVersionId::V9 => self.join_authorized_via_users_server,
_ => None,
},
}
}
}
/// A member event that has been redacted.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct RedactedRoomMemberEventContent {
/// The membership state of this user.
pub membership: MembershipState,
/// An arbitrary user who has the power to issue invites.
///
/// This is redacted in room versions 8 and below. It is used for validating
/// joins when the join rule is restricted.
#[cfg(feature = "unstable-pre-spec")]
#[serde(rename = "join_authorised_via_users_server")]
pub join_authorized_via_users_server: Option<Box<UserId>>,
}
impl RedactedRoomMemberEventContent {
/// Create a `RedactedRoomMemberEventContent` with the given membership.
pub fn new(membership: MembershipState) -> Self {
Self {
membership,
#[cfg(feature = "unstable-pre-spec")]
join_authorized_via_users_server: None,
}
}
}
impl EventContent for RedactedRoomMemberEventContent {
fn event_type(&self) -> &str {
"m.room.member"
}
fn from_parts(event_type: &str, content: &RawJsonValue) -> serde_json::Result<Self> {
if event_type != "m.room.member" {
return Err(::serde::de::Error::custom(format!(
"expected event type `m.room.member`, found `{}`",
event_type
)));
}
serde_json::from_str(content.get())
}
}
// Since this redacted event has fields we leave the default `empty` method
// that will error if called.
impl RedactedEventContent for RedactedRoomMemberEventContent {
fn has_serialize_fields(&self) -> bool {
true
}
fn has_deserialize_fields() -> HasDeserializeFields {
HasDeserializeFields::Optional
}
}
impl RedactedStateEventContent for RedactedRoomMemberEventContent {}
/// The membership state of a user.
///
/// This type can hold an arbitrary string. To check for formats that are not available as a
@ -269,6 +354,8 @@ fn membership_change(
blurhash: None,
#[cfg(feature = "unstable-pre-spec")]
reason: None,
#[cfg(feature = "unstable-pre-spec")]
join_authorized_via_users_server: None,
}
};
@ -660,4 +747,49 @@ mod tests {
&& token == "abc123"
);
}
#[cfg(feature = "unstable-pre-spec")]
#[test]
fn serde_with_join_authorized() {
let json = json!({
"type": "m.room.member",
"content": {
"membership": "join",
"join_authorised_via_users_server": "@notcarl:example.com"
},
"event_id": "$h29iv0s8:example.com",
"origin_server_ts": 1,
"room_id": "!n8f893n9:example.com",
"sender": "@carl:example.com",
"state_key": "example.com"
});
assert_matches!(
from_json_value::<StateEvent<RoomMemberEventContent>>(json).unwrap(),
StateEvent {
content: RoomMemberEventContent {
avatar_url: None,
displayname: None,
is_direct: None,
membership: MembershipState::Join,
third_party_invite: None,
join_authorized_via_users_server: Some(authed),
..
},
event_id,
origin_server_ts,
room_id,
sender,
state_key,
unsigned,
prev_content: None,
} if event_id == "$h29iv0s8:example.com"
&& origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(1))
&& room_id == "!n8f893n9:example.com"
&& sender == "@carl:example.com"
&& authed == "@notcarl:example.com"
&& state_key == "example.com"
&& unsigned.is_empty()
);
}
}