ruma_state_res/
event_auth.rs

1use std::{
2    borrow::Borrow,
3    collections::{BTreeMap, BTreeSet, HashSet},
4};
5
6use js_int::Int;
7use ruma_common::{room::JoinRuleKind, room_version_rules::AuthorizationRules, EventId, UserId};
8use ruma_events::{room::member::MembershipState, StateEventType, TimelineEventType};
9use serde_json::value::RawValue as RawJsonValue;
10use tracing::{debug, info, instrument, warn};
11
12mod room_member;
13#[cfg(test)]
14mod tests;
15
16use self::room_member::check_room_member;
17use crate::{
18    events::{
19        member::{RoomMemberEventContent, RoomMemberEventOptionExt},
20        power_levels::{RoomPowerLevelsEventOptionExt, RoomPowerLevelsIntField},
21        RoomCreateEvent, RoomJoinRulesEvent, RoomMemberEvent, RoomPowerLevelsEvent,
22        RoomThirdPartyInviteEvent,
23    },
24    Event,
25};
26
27/// Get the list of [relevant auth events] required to authorize the event of the given type.
28///
29/// Returns a list of `(event_type, state_key)` tuples.
30///
31/// # Errors
32///
33/// Returns an `Err(_)` if a field could not be deserialized because `content` does not respect the
34/// expected format for the `event_type`.
35///
36/// [relevant auth events]: https://spec.matrix.org/latest/server-server-api/#auth-events-selection
37pub fn auth_types_for_event(
38    event_type: &TimelineEventType,
39    sender: &UserId,
40    state_key: Option<&str>,
41    content: &RawJsonValue,
42    rules: &AuthorizationRules,
43) -> Result<Vec<(StateEventType, String)>, String> {
44    // The `auth_events` for the `m.room.create` event in a room is empty.
45    if event_type == &TimelineEventType::RoomCreate {
46        return Ok(vec![]);
47    }
48
49    // For other events, it should be the following subset of the room state:
50    //
51    // - The `m.room.create` event.
52    // - The current `m.room.power_levels` event, if any.
53    // - The sender’s current `m.room.member` event, if any.
54    let mut auth_types = vec![
55        (StateEventType::RoomPowerLevels, "".to_owned()),
56        (StateEventType::RoomMember, sender.to_string()),
57        (StateEventType::RoomCreate, "".to_owned()),
58    ];
59
60    // If type is `m.room.member`:
61    if event_type == &TimelineEventType::RoomMember {
62        // The target’s current `m.room.member` event, if any.
63        let Some(state_key) = state_key else {
64            return Err("missing `state_key` field for `m.room.member` event".to_owned());
65        };
66        let key = (StateEventType::RoomMember, state_key.to_owned());
67        if !auth_types.contains(&key) {
68            auth_types.push(key);
69        }
70
71        let content = RoomMemberEventContent::new(content);
72        let membership = content.membership()?;
73
74        // If `membership` is `join`, `invite` or `knock`, the current `m.room.join_rules` event, if
75        // any.
76        if matches!(
77            membership,
78            MembershipState::Join | MembershipState::Invite | MembershipState::Knock
79        ) {
80            let key = (StateEventType::RoomJoinRules, "".to_owned());
81            if !auth_types.contains(&key) {
82                auth_types.push(key);
83            }
84        }
85
86        // If `membership` is `invite` and `content` contains a `third_party_invite` property, the
87        // current `m.room.third_party_invite` event with `state_key` matching
88        // `content.third_party_invite.signed.token`, if any.
89        if membership == MembershipState::Invite {
90            let third_party_invite = content.third_party_invite()?;
91
92            if let Some(third_party_invite) = third_party_invite {
93                let token = third_party_invite.token()?.to_owned();
94                let key = (StateEventType::RoomThirdPartyInvite, token);
95                if !auth_types.contains(&key) {
96                    auth_types.push(key);
97                }
98            }
99        }
100
101        // If `content.join_authorised_via_users_server` is present, and the room version supports
102        // restricted rooms, then the `m.room.member` event with `state_key` matching
103        // `content.join_authorised_via_users_server`.
104        //
105        // Note: And the membership is join (https://github.com/matrix-org/matrix-spec/pull/2100)
106        if membership == MembershipState::Join && rules.restricted_join_rule {
107            let join_authorised_via_users_server = content.join_authorised_via_users_server()?;
108            if let Some(user_id) = join_authorised_via_users_server {
109                let key = (StateEventType::RoomMember, user_id.to_string());
110                if !auth_types.contains(&key) {
111                    auth_types.push(key);
112                }
113            }
114        }
115    }
116
117    Ok(auth_types)
118}
119
120/// Check whether the incoming event passes the state-independent [authorization rules] for the
121/// given room version rules.
122///
123/// The state-independent rules are the first few authorization rules that check an incoming
124/// `m.room.create` event (which cannot have `auth_events`), and the list of `auth_events` of other
125/// events.
126///
127/// This method only needs to be called once, when the event is received.
128///
129/// # Errors
130///
131/// If the check fails, this returns an `Err(_)` with a description of the check that failed.
132///
133/// [authorization rules]: https://spec.matrix.org/latest/server-server-api/#authorization-rules
134#[instrument(skip_all, fields(event_id = incoming_event.event_id().borrow().as_str()))]
135pub fn check_state_independent_auth_rules<E: Event>(
136    rules: &AuthorizationRules,
137    incoming_event: impl Event,
138    fetch_event: impl Fn(&EventId) -> Option<E>,
139) -> Result<(), String> {
140    debug!("starting state-independent auth check");
141
142    // Since v1, if type is m.room.create:
143    if *incoming_event.event_type() == TimelineEventType::RoomCreate {
144        let room_create_event = RoomCreateEvent::new(incoming_event);
145
146        return check_room_create(room_create_event, rules);
147    }
148
149    let expected_auth_types = auth_types_for_event(
150        incoming_event.event_type(),
151        incoming_event.sender(),
152        incoming_event.state_key(),
153        incoming_event.content(),
154        rules,
155    )?
156    .into_iter()
157    .map(|(event_type, state_key)| (TimelineEventType::from(event_type), state_key))
158    .collect::<HashSet<_>>();
159
160    let room_id = incoming_event.room_id();
161    let mut seen_auth_types: HashSet<(TimelineEventType, String)> =
162        HashSet::with_capacity(expected_auth_types.len());
163
164    // Since v1, considering auth_events:
165    for auth_event_id in incoming_event.auth_events() {
166        let event_id = auth_event_id.borrow();
167
168        let Some(auth_event) = fetch_event(event_id) else {
169            return Err(format!("failed to find auth event {event_id}"));
170        };
171
172        // The auth event must be in the same room as the incoming event.
173        if auth_event.room_id() != room_id {
174            return Err(format!("auth event {event_id} not in the same room"));
175        }
176
177        let event_type = auth_event.event_type();
178        let state_key = auth_event
179            .state_key()
180            .ok_or_else(|| format!("auth event {event_id} has no `state_key`"))?;
181        let key = (event_type.clone(), state_key.to_owned());
182
183        // Since v1, if there are duplicate entries for a given type and state_key pair, reject.
184        if seen_auth_types.contains(&key) {
185            return Err(format!(
186                "duplicate auth event {event_id} for ({event_type}, {state_key}) pair"
187            ));
188        }
189
190        // Since v1, if there are entries whose type and state_key don’t match those specified by
191        // the auth events selection algorithm described in the server specification, reject.
192        if !expected_auth_types.contains(&key) {
193            return Err(format!(
194                "unexpected auth event {event_id} with ({event_type}, {state_key}) pair"
195            ));
196        }
197
198        // Since v1, if there are entries which were themselves rejected under the checks performed
199        // on receipt of a PDU, reject.
200        if auth_event.rejected() {
201            return Err(format!("rejected auth event {event_id}"));
202        }
203
204        seen_auth_types.insert(key);
205    }
206
207    // Since v1, if there is no m.room.create event among the entries, reject.
208    if !seen_auth_types.iter().any(|(event_type, _)| *event_type == TimelineEventType::RoomCreate) {
209        return Err("no `m.room.create` event in auth events".to_owned());
210    }
211
212    Ok(())
213}
214
215/// Check whether the incoming event passes the state-dependent [authorization rules] for the given
216/// room version rules.
217///
218/// The state-dependent rules are all the remaining rules not checked by
219/// [`check_state_independent_auth_rules()`].
220///
221/// This method should be called several times for an event, to perform the [checks on receipt of a
222/// PDU].
223///
224/// The `fetch_state` closure should gather state from a state snapshot. We need to know if the
225/// event passes auth against some state not a recursive collection of auth_events fields.
226///
227/// This assumes that `ruma_signatures::verify_event()` was called previously, as some authorization
228/// rules depend on the signatures being valid on the event.
229///
230/// # Errors
231///
232/// If the check fails, this returns an `Err(_)` with a description of the check that failed.
233///
234/// [authorization rules]: https://spec.matrix.org/latest/server-server-api/#authorization-rules
235/// [checks on receipt of a PDU]: https://spec.matrix.org/latest/server-server-api/#checks-performed-on-receipt-of-a-pdu
236#[instrument(skip_all, fields(event_id = incoming_event.event_id().borrow().as_str()))]
237pub fn check_state_dependent_auth_rules<E: Event>(
238    rules: &AuthorizationRules,
239    incoming_event: impl Event,
240    fetch_state: impl Fn(&StateEventType, &str) -> Option<E>,
241) -> Result<(), String> {
242    debug!("starting state-dependent auth check");
243
244    // There are no state-dependent auth rules for create events.
245    if *incoming_event.event_type() == TimelineEventType::RoomCreate {
246        debug!("allowing `m.room.create` event");
247        return Ok(());
248    }
249
250    let room_create_event = fetch_state.room_create_event()?;
251
252    // Since v1, if the create event content has the field m.federate set to false and the sender
253    // domain of the event does not match the sender domain of the create event, reject.
254    let federate = room_create_event.federate()?;
255    if !federate
256        && room_create_event.sender().server_name() != incoming_event.sender().server_name()
257    {
258        return Err("\
259            room is not federated and event's sender domain \
260            does not match `m.room.create` event's sender domain"
261            .to_owned());
262    }
263
264    let sender = incoming_event.sender();
265
266    // v1-v5, if type is m.room.aliases:
267    if rules.special_case_room_aliases
268        && *incoming_event.event_type() == TimelineEventType::RoomAliases
269    {
270        debug!("starting m.room.aliases check");
271        // v1-v5, if event has no state_key, reject.
272        //
273        // v1-v5, if sender's domain doesn't match state_key, reject.
274        if incoming_event.state_key() != Some(sender.server_name().as_str()) {
275            return Err("\
276                server name of the `state_key` of `m.room.aliases` event \
277                does not match the server name of the sender"
278                .to_owned());
279        }
280
281        // Otherwise, allow.
282        info!("`m.room.aliases` event was allowed");
283        return Ok(());
284    }
285
286    // Since v1, if type is m.room.member:
287    if *incoming_event.event_type() == TimelineEventType::RoomMember {
288        let room_member_event = RoomMemberEvent::new(incoming_event);
289        return check_room_member(room_member_event, rules, room_create_event, fetch_state);
290    }
291
292    // Since v1, if the sender's current membership state is not join, reject.
293    let sender_membership = fetch_state.user_membership(sender)?;
294
295    if sender_membership != MembershipState::Join {
296        return Err("sender's membership is not `join`".to_owned());
297    }
298
299    let creator = room_create_event.creator(rules)?;
300    let current_room_power_levels_event = fetch_state.room_power_levels_event();
301
302    let sender_power_level =
303        current_room_power_levels_event.user_power_level(sender, &creator, rules)?;
304
305    // Since v1, if type is m.room.third_party_invite:
306    if *incoming_event.event_type() == TimelineEventType::RoomThirdPartyInvite {
307        // Since v1, allow if and only if sender's current power level is greater than
308        // or equal to the invite level.
309        let invite_power_level = current_room_power_levels_event
310            .get_as_int_or_default(RoomPowerLevelsIntField::Invite, rules)?;
311
312        if sender_power_level < invite_power_level {
313            return Err("sender does not have enough power to send invites in this room".to_owned());
314        }
315
316        info!("`m.room.third_party_invite` event was allowed");
317        return Ok(());
318    }
319
320    // Since v1, if the event type's required power level is greater than the sender's power level,
321    // reject.
322    let event_type_power_level = current_room_power_levels_event.event_power_level(
323        incoming_event.event_type(),
324        incoming_event.state_key(),
325        rules,
326    )?;
327    if sender_power_level < event_type_power_level {
328        return Err(format!(
329            "sender does not have enough power to send event of type `{}`",
330            incoming_event.event_type()
331        ));
332    }
333
334    // Since v1, if the event has a state_key that starts with an @ and does not match the sender,
335    // reject.
336    if incoming_event.state_key().is_some_and(|k| k.starts_with('@'))
337        && incoming_event.state_key() != Some(incoming_event.sender().as_str())
338    {
339        return Err(
340            "sender cannot send event with `state_key` matching another user's ID".to_owned()
341        );
342    }
343
344    // If type is m.room.power_levels
345    if *incoming_event.event_type() == TimelineEventType::RoomPowerLevels {
346        let room_power_levels_event = RoomPowerLevelsEvent::new(incoming_event);
347        return check_room_power_levels(
348            room_power_levels_event,
349            current_room_power_levels_event,
350            rules,
351            sender_power_level,
352        );
353    }
354
355    // v1-v2, if type is m.room.redaction:
356    if rules.special_case_room_redaction
357        && *incoming_event.event_type() == TimelineEventType::RoomRedaction
358    {
359        return check_room_redaction(
360            incoming_event,
361            current_room_power_levels_event,
362            rules,
363            sender_power_level,
364        );
365    }
366
367    // Otherwise, allow.
368    info!("allowing event passed all checks");
369    Ok(())
370}
371
372/// Check whether the given event passes the `m.room.create` authorization rules.
373fn check_room_create(
374    room_create_event: RoomCreateEvent<impl Event>,
375    rules: &AuthorizationRules,
376) -> Result<(), String> {
377    debug!("start `m.room.create` check");
378
379    // Since v1, if it has any previous events, reject.
380    if room_create_event.prev_events().next().is_some() {
381        return Err("`m.room.create` event cannot have previous events".into());
382    }
383
384    // Since v1, if the domain of the room_id does not match the domain of the sender, reject.
385    let Some(room_id_server_name) = room_create_event.room_id().server_name() else {
386        return Err(
387            "invalid `room_id` field in `m.room.create` event: could not parse server name".into(),
388        );
389    };
390
391    if room_id_server_name != room_create_event.sender().server_name() {
392        return Err("invalid `room_id` field in `m.room.create` event: server name does not match sender's server name".into());
393    }
394
395    // Since v1, if `content.room_version` is present and is not a recognized version, reject.
396    //
397    // This check is assumed to be done before calling auth_check because we have an
398    // AuthorizationRules, which means that we recognized the version.
399
400    // v1-v10, if content has no creator field, reject.
401    if !rules.use_room_create_sender && !room_create_event.has_creator()? {
402        return Err("missing `creator` field in `m.room.create` event".into());
403    }
404
405    // Otherwise, allow.
406    info!("`m.room.create` event was allowed");
407    Ok(())
408}
409
410/// Check whether the given event passes the `m.room.power_levels` authorization rules.
411fn check_room_power_levels(
412    room_power_levels_event: RoomPowerLevelsEvent<impl Event>,
413    current_room_power_levels_event: Option<RoomPowerLevelsEvent<impl Event>>,
414    rules: &AuthorizationRules,
415    sender_power_level: Int,
416) -> Result<(), String> {
417    debug!("starting m.room.power_levels check");
418
419    // Since v10, if any of the properties users_default, events_default, state_default, ban,
420    // redact, kick, or invite in content are present and not an integer, reject.
421    let new_int_fields = room_power_levels_event.int_fields_map(rules)?;
422
423    // Since v10, if either of the properties events or notifications in content are present and not
424    // a dictionary with values that are integers, reject.
425    let new_events = room_power_levels_event.events(rules)?;
426    let new_notifications = room_power_levels_event.notifications(rules)?;
427
428    // v1-v9, If the users property in content is not an object with keys that are valid user IDs
429    // with values that are integers (or a string that is an integer), reject.
430    // Since v10, if the users property in content is not an object with keys that are valid user
431    // IDs with values that are integers, reject.
432    let new_users = room_power_levels_event.users(rules)?;
433
434    debug!("validation of power event finished");
435
436    // Since v1, if there is no previous m.room.power_levels event in the room, allow.
437    let Some(current_room_power_levels_event) = current_room_power_levels_event else {
438        info!("initial m.room.power_levels event allowed");
439        return Ok(());
440    };
441
442    // Since v1, for the properties users_default, events_default, state_default, ban, redact, kick,
443    // invite check if they were added, changed or removed. For each found alteration:
444    for field in RoomPowerLevelsIntField::ALL {
445        let current_power_level = current_room_power_levels_event.get_as_int(*field, rules)?;
446        let new_power_level = new_int_fields.get(field).copied();
447
448        if current_power_level == new_power_level {
449            continue;
450        }
451
452        // Since v1, if the current value is higher than the sender’s current power level,
453        // reject.
454        let current_power_level_too_big =
455            current_power_level.unwrap_or_else(|| field.default_value()) > sender_power_level;
456        // Since v1, if the new value is higher than the sender’s current power level, reject.
457        let new_power_level_too_big =
458            new_power_level.unwrap_or_else(|| field.default_value()) > sender_power_level;
459
460        if current_power_level_too_big || new_power_level_too_big {
461            return Err(format!(
462                "sender does not have enough power to change the power level of `{field}`"
463            ));
464        }
465    }
466
467    // Since v1, for each entry being added to, or changed in, the events property:
468    // - Since v1, if the new value is higher than the sender's current power level, reject.
469    let current_events = current_room_power_levels_event.events(rules)?;
470    check_power_level_maps(
471        current_events.as_ref(),
472        new_events.as_ref(),
473        &sender_power_level,
474        |_, current_power_level| {
475            // Since v1, for each entry being changed in, or removed from, the events property:
476            // - Since v1, if the current value is higher than the sender's current power level,
477            //   reject.
478            current_power_level > sender_power_level
479        },
480        |ev_type| {
481            format!(
482                "sender does not have enough power to change the `{ev_type}` event type power level"
483            )
484        },
485    )?;
486
487    // Since v6, for each entry being added to, or changed in, the notifications property:
488    // - Since v6, if the new value is higher than the sender's current power level, reject.
489    if rules.limit_notifications_power_levels {
490        let current_notifications = current_room_power_levels_event.notifications(rules)?;
491        check_power_level_maps(
492            current_notifications.as_ref(),
493            new_notifications.as_ref(),
494            &sender_power_level,
495            |_, current_power_level| {
496                // Since v6, for each entry being changed in, or removed from, the notifications
497                // property:
498                // - Since v6, if the current value is higher than the sender's current power level,
499                //   reject.
500                current_power_level > sender_power_level
501            },
502            |key| {
503                format!(
504                    "sender does not have enough power to change the `{key}` notification power level"
505                )
506            },
507        )?;
508    }
509
510    // Since v1, for each entry being added to, or changed in, the users property:
511    // - Since v1, if the new value is greater than the sender’s current power level, reject.
512    let current_users = current_room_power_levels_event.users(rules)?;
513    check_power_level_maps(
514        current_users,
515        new_users,
516        &sender_power_level,
517        |user_id, current_power_level| {
518            // Since v1, for each entry being changed in, or removed from, the users property, other
519            // than the sender’s own entry:
520            // - Since v1, if the current value is greater than or equal to the sender’s current
521            //   power level, reject.
522            user_id != room_power_levels_event.sender() && current_power_level >= sender_power_level
523        },
524        |user_id| format!("sender does not have enough power to change `{user_id}`'s  power level"),
525    )?;
526
527    // Otherwise, allow.
528    info!("m.room.power_levels event allowed");
529    Ok(())
530}
531
532/// Check the power levels changes between the current and the new maps.
533///
534/// # Arguments
535///
536/// * `current`: the map with the current power levels.
537/// * `new`: the map with the new power levels.
538/// * `sender_power_level`: the power level of the sender of the new map.
539/// * `reject_current_power_level_change_fn`: the function to check if a power level change or
540///   removal must be rejected given its current value.
541///
542///   The arguments to the method are the key of the power level and the current value of the power
543///   level. It must return `true` if the change or removal is rejected.
544///
545///   Note that another check is done after this one to check if the change is allowed given the new
546///   value of the power level.
547/// * `error_fn`: the function to generate an error when the change for the given key is not
548///   allowed.
549fn check_power_level_maps<K: Ord>(
550    current: Option<&BTreeMap<K, Int>>,
551    new: Option<&BTreeMap<K, Int>>,
552    sender_power_level: &Int,
553    reject_current_power_level_change_fn: impl FnOnce(&K, Int) -> bool + Copy,
554    error_fn: impl FnOnce(&K) -> String,
555) -> Result<(), String> {
556    let keys_to_check = current
557        .iter()
558        .flat_map(|m| m.keys())
559        .chain(new.iter().flat_map(|m| m.keys()))
560        .collect::<BTreeSet<_>>();
561
562    for key in keys_to_check {
563        let current_power_level = current.as_ref().and_then(|m| m.get(key));
564        let new_power_level = new.as_ref().and_then(|m| m.get(key));
565
566        if current_power_level == new_power_level {
567            continue;
568        }
569
570        // For each entry being changed in, or removed from, the property.
571        let current_power_level_change_rejected = current_power_level
572            .is_some_and(|power_level| reject_current_power_level_change_fn(key, *power_level));
573
574        // For each entry being added to, or changed in, the property:
575        // - If the new value is higher than the sender's current power level, reject.
576        let new_power_level_too_big = new_power_level > Some(sender_power_level);
577
578        if current_power_level_change_rejected || new_power_level_too_big {
579            return Err(error_fn(key));
580        }
581    }
582
583    Ok(())
584}
585
586/// Check whether the given event passes the `m.room.redaction` authorization rules.
587fn check_room_redaction(
588    room_redaction_event: impl Event,
589    current_room_power_levels_event: Option<RoomPowerLevelsEvent<impl Event>>,
590    rules: &AuthorizationRules,
591    sender_level: Int,
592) -> Result<(), String> {
593    let redact_level = current_room_power_levels_event
594        .get_as_int_or_default(RoomPowerLevelsIntField::Redact, rules)?;
595
596    // v1-v2, if the sender’s power level is greater than or equal to the redact level, allow.
597    if sender_level >= redact_level {
598        info!("`m.room.redaction` event allowed via power levels");
599        return Ok(());
600    }
601
602    // v1-v2, if the domain of the event_id of the event being redacted is the same as the
603    // domain of the event_id of the m.room.redaction, allow.
604    if room_redaction_event.event_id().borrow().server_name()
605        == room_redaction_event.redacts().as_ref().and_then(|&id| id.borrow().server_name())
606    {
607        info!("`m.room.redaction` event allowed via room version 1 rules");
608        return Ok(());
609    }
610
611    // Otherwise, reject.
612    Err("`m.room.redaction` event did not pass any of the allow rules".to_owned())
613}
614
615trait FetchStateExt<E: Event> {
616    fn room_create_event(&self) -> Result<RoomCreateEvent<E>, String>;
617
618    fn user_membership(&self, user_id: &UserId) -> Result<MembershipState, String>;
619
620    fn room_power_levels_event(&self) -> Option<RoomPowerLevelsEvent<E>>;
621
622    fn join_rule(&self) -> Result<JoinRuleKind, String>;
623
624    fn room_third_party_invite_event(&self, token: &str) -> Option<RoomThirdPartyInviteEvent<E>>;
625}
626
627impl<E, F> FetchStateExt<E> for F
628where
629    F: Fn(&StateEventType, &str) -> Option<E>,
630    E: Event,
631{
632    fn room_create_event(&self) -> Result<RoomCreateEvent<E>, String> {
633        self(&StateEventType::RoomCreate, "")
634            .map(RoomCreateEvent::new)
635            .ok_or_else(|| "no `m.room.create` event in current state".to_owned())
636    }
637
638    fn user_membership(&self, user_id: &UserId) -> Result<MembershipState, String> {
639        self(&StateEventType::RoomMember, user_id.as_str()).map(RoomMemberEvent::new).membership()
640    }
641
642    fn room_power_levels_event(&self) -> Option<RoomPowerLevelsEvent<E>> {
643        self(&StateEventType::RoomPowerLevels, "").map(RoomPowerLevelsEvent::new)
644    }
645
646    fn join_rule(&self) -> Result<JoinRuleKind, String> {
647        self(&StateEventType::RoomJoinRules, "")
648            .map(RoomJoinRulesEvent::new)
649            .ok_or_else(|| "no `m.room.join_rules` event in current state".to_owned())?
650            .join_rule()
651    }
652
653    fn room_third_party_invite_event(&self, token: &str) -> Option<RoomThirdPartyInviteEvent<E>> {
654        self(&StateEventType::RoomThirdPartyInvite, token).map(RoomThirdPartyInviteEvent::new)
655    }
656}