Skip to main content

tuwunel_service/rooms/state_res/events/
member.rs

1//! Types to deserialize `m.room.member` events.
2
3use std::ops::Deref;
4
5use ruma::{
6	CanonicalJsonObject, OwnedUserId, events::room::member::MembershipState,
7	serde::from_raw_json_value, signatures::to_canonical_json_string_for_signing,
8};
9use serde::Deserialize;
10use serde_json::value::RawValue as RawJsonValue;
11use tuwunel_core::{Err, Error, Result, debug_error, err, matrix::Event};
12
13/// A helper type for an [`Event`] of type `m.room.member`.
14///
15/// This is a type that deserializes each field lazily, as requested.
16#[derive(Debug, Clone)]
17pub struct RoomMemberEvent<E: Event>(E);
18
19impl<E: Event> RoomMemberEvent<E> {
20	/// Construct a new `RoomMemberEvent` around the given event.
21	#[inline]
22	pub fn new(event: E) -> Self { Self(event) }
23
24	/// The membership of the user.
25	#[inline]
26	pub fn membership(&self) -> Result<MembershipState> {
27		RoomMemberEventContent(self.content()).membership()
28	}
29
30	/// If this is a `join` event, the ID of a user on the homeserver that
31	/// authorized it.
32	#[inline]
33	pub fn join_authorised_via_users_server(&self) -> Result<Option<OwnedUserId>> {
34		RoomMemberEventContent(self.content()).join_authorised_via_users_server()
35	}
36
37	/// If this is an `invite` event, details about the third-party invite that
38	/// resulted in this event.
39	#[inline]
40	pub fn third_party_invite(&self) -> Result<Option<ThirdPartyInvite>> {
41		RoomMemberEventContent(self.content()).third_party_invite()
42	}
43}
44
45impl<E: Event> Deref for RoomMemberEvent<E> {
46	type Target = E;
47
48	#[inline]
49	fn deref(&self) -> &Self::Target { &self.0 }
50}
51
52/// Helper trait for `Option<RoomMemberEvent<E>>`.
53pub(crate) trait RoomMemberEventResultExt {
54	/// The membership of the user.
55	///
56	/// Defaults to `leave` if there is no `m.room.member` event.
57	fn membership(&self) -> Result<MembershipState>;
58}
59
60impl<E: Event> RoomMemberEventResultExt for Result<RoomMemberEvent<E>> {
61	fn membership(&self) -> Result<MembershipState> {
62		match self {
63			| Ok(event) => event.membership(),
64			| Err(e) if e.is_not_found() => Ok(MembershipState::Leave),
65			| Err(e) if cfg!(test) => panic!("membership(): unexpected: {e}"),
66			| Err(e) => {
67				debug_error!("membership(): unexpected: {e}");
68				Ok(MembershipState::Leave)
69			},
70		}
71	}
72}
73
74/// A helper type for the raw JSON content of an event of type `m.room.member`.
75pub struct RoomMemberEventContent<'a>(&'a RawJsonValue);
76
77impl<'a> RoomMemberEventContent<'a> {
78	/// Construct a new `RoomMemberEventContent` around the given raw JSON
79	/// content.
80	#[inline]
81	#[must_use]
82	pub fn new(content: &'a RawJsonValue) -> Self { Self(content) }
83}
84
85impl RoomMemberEventContent<'_> {
86	/// The membership of the user.
87	pub fn membership(&self) -> Result<MembershipState> {
88		#[derive(Deserialize)]
89		struct RoomMemberContentMembership {
90			membership: MembershipState,
91		}
92
93		let content: RoomMemberContentMembership =
94			from_raw_json_value(self.0).map_err(|err: Error| {
95				err!(Request(InvalidParam(
96					"missing or invalid `membership` field in `m.room.member` event: {err}"
97				)))
98			})?;
99
100		Ok(content.membership)
101	}
102
103	/// If this is a `join` event, the ID of a user on the homeserver that
104	/// authorized it.
105	pub fn join_authorised_via_users_server(&self) -> Result<Option<OwnedUserId>> {
106		#[derive(Deserialize)]
107		struct RoomMemberContentJoinAuthorizedViaUsersServer {
108			join_authorised_via_users_server: Option<OwnedUserId>,
109		}
110
111		let content: RoomMemberContentJoinAuthorizedViaUsersServer = from_raw_json_value(self.0)
112			.map_err(|err: Error| {
113				err!(Request(InvalidParam(
114					"invalid `join_authorised_via_users_server` field in `m.room.member` event: \
115					 {err}"
116				)))
117			})?;
118
119		Ok(content.join_authorised_via_users_server)
120	}
121
122	/// If this is an `invite` event, details about the third-party invite that
123	/// resulted in this event.
124	pub fn third_party_invite(&self) -> Result<Option<ThirdPartyInvite>> {
125		#[derive(Deserialize)]
126		struct RoomMemberContentThirdPartyInvite {
127			third_party_invite: Option<ThirdPartyInvite>,
128		}
129
130		let content: RoomMemberContentThirdPartyInvite =
131			from_raw_json_value(self.0).map_err(|err: Error| {
132				err!(Request(InvalidParam(
133					"invalid `third_party_invite` field in `m.room.member` event: {err}"
134				)))
135			})?;
136
137		Ok(content.third_party_invite)
138	}
139}
140
141/// Details about a third-party invite.
142#[derive(Deserialize)]
143pub struct ThirdPartyInvite {
144	/// Signed details about the third-party invite.
145	signed: CanonicalJsonObject,
146}
147
148impl ThirdPartyInvite {
149	/// The unique identifier for the third-party invite.
150	pub fn token(&self) -> Result<&str> {
151		let Some(token_value) = self.signed.get("token") else {
152			return Err!(Request(InvalidParam(
153				"missing `token` field in `third_party_invite.signed` of `m.room.member` event"
154			)));
155		};
156
157		token_value.as_str().ok_or_else(|| {
158			err!(Request(InvalidParam(
159				"unexpected format of `token` field in `third_party_invite.signed` of \
160				 `m.room.member` event: expected string, got {token_value:?}"
161			)))
162		})
163	}
164
165	/// The Matrix ID of the user that was invited.
166	pub fn mxid(&self) -> Result<&str> {
167		let Some(mxid_value) = self.signed.get("mxid") else {
168			return Err!(Request(InvalidParam(
169				"missing `mxid` field in `third_party_invite.signed` of `m.room.member` event"
170			)));
171		};
172
173		mxid_value.as_str().ok_or_else(|| {
174			err!(Request(InvalidParam(
175				"unexpected format of `mxid` field in `third_party_invite.signed` of \
176				 `m.room.member` event: expected string, got {mxid_value:?}"
177			)))
178		})
179	}
180
181	/// The signatures of the event.
182	pub fn signatures(&self) -> Result<&CanonicalJsonObject> {
183		let Some(signatures_value) = self.signed.get("signatures") else {
184			return Err!(Request(InvalidParam(
185				"missing `signatures` field in `third_party_invite.signed` of `m.room.member` \
186				 event"
187			)));
188		};
189
190		signatures_value.as_object().ok_or_else(|| {
191			err!(Request(InvalidParam(
192				"unexpected format of `signatures` field in `third_party_invite.signed` of \
193				 `m.room.member` event: expected object, got {signatures_value:?}"
194			)))
195		})
196	}
197
198	/// The `signed` object as canonical JSON string to verify the signatures.
199	pub fn signed_canonical_json(&self) -> Result<String> {
200		to_canonical_json_string_for_signing(&self.signed).map_err(|error| {
201			err!(Request(InvalidParam(
202				"invalid `third_party_invite.signed` field in `m.room.member` event: {error}"
203			)))
204		})
205	}
206}