Skip to main content

ruma_federation_api/
membership.rs

1//! Room membership endpoints.
2
3use ruma_common::serde::{Raw, from_raw_json_value};
4use ruma_events::AnyStrippedStateEvent;
5use serde::{Deserialize, Serialize, de};
6use serde_json::value::RawValue as RawJsonValue;
7
8pub mod create_invite;
9pub mod create_join_event;
10pub mod create_knock_event;
11pub mod create_leave_event;
12pub mod prepare_join_event;
13pub mod prepare_knock_event;
14pub mod prepare_leave_event;
15
16/// Possible event formats that may appear in stripped state.
17#[derive(Clone, Debug, Serialize)]
18#[serde(untagged)]
19#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
20pub enum RawStrippedState {
21    /// A stripped state event.
22    #[deprecated = "Since Matrix 1.16, stripped state events are required to be sent over federation as full PDUs.\
23                    It is still possible to receive this variant for backwards compatibility."]
24    Stripped(Raw<AnyStrippedStateEvent>),
25
26    /// A full federation PDU.
27    Pdu(Box<RawJsonValue>),
28}
29
30impl<'de> Deserialize<'de> for RawStrippedState {
31    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
32    where
33        D: ::serde::Deserializer<'de>,
34    {
35        #[derive(Deserialize)]
36        struct PotentialPduDeHelper {
37            auth_events: Option<de::IgnoredAny>,
38            prev_events: Option<de::IgnoredAny>,
39            signatures: Option<de::IgnoredAny>,
40            hashes: Option<de::IgnoredAny>,
41        }
42
43        let json = Box::<RawJsonValue>::deserialize(deserializer)?;
44
45        let PotentialPduDeHelper { auth_events, prev_events, signatures, hashes } =
46            from_raw_json_value(&json)?;
47
48        if auth_events.is_some()
49            && prev_events.is_some()
50            && signatures.is_some()
51            && hashes.is_some()
52        {
53            Ok(Self::Pdu(json))
54        } else {
55            #[allow(deprecated)]
56            Ok(Self::Stripped(Raw::from_json(json)))
57        }
58    }
59}
60
61impl From<Raw<AnyStrippedStateEvent>> for RawStrippedState {
62    fn from(value: Raw<AnyStrippedStateEvent>) -> Self {
63        #[allow(deprecated)]
64        Self::Stripped(value)
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use assert_matches::assert_matches;
71    use ruma_common::{serde::Raw, user_id};
72    use ruma_events::{AnyStrippedStateEvent, room::member::MembershipState};
73    use serde_json::{from_value as from_json_value, json};
74    use strass::assert_let;
75
76    use super::RawStrippedState;
77
78    #[test]
79    #[allow(deprecated)]
80    fn deserialize_stripped_state() {
81        let user_id = user_id!("@patrick:localhost");
82        let content = json!({
83            "membership": "join",
84        });
85
86        // Stripped format.
87        let stripped_event_json = json!({
88            "content": content,
89            "sender": user_id,
90            "state_key": user_id,
91            "type": "m.room.member",
92        });
93        assert_let!(
94            RawStrippedState::Stripped(raw_stripped_event) =
95                from_json_value::<RawStrippedState>(stripped_event_json).unwrap()
96        );
97        assert_let!(
98            AnyStrippedStateEvent::RoomMember(stripped_member_event) =
99                raw_stripped_event.deserialize().unwrap()
100        );
101        assert_eq!(stripped_member_event.sender, user_id);
102        assert_eq!(stripped_member_event.state_key, user_id);
103        assert_eq!(stripped_member_event.content.membership, MembershipState::Join);
104
105        // PDU format
106        let pdu_event_json = json!({
107            "auth_events": [
108                "$one",
109                "$two",
110                "$three"
111            ],
112            "content": content,
113            "depth": 10,
114            "hashes": {
115                "sha256": "thisisahash"
116            },
117            "origin_server_ts": 1_000_000,
118            "prev_events": [
119                "$one",
120                "$two",
121                "$three"
122            ],
123            "room_id": "!room:localhost",
124            "sender": user_id,
125            "signatures": {
126                "localhost": {
127                    "ed25519:1": "thisisakey"
128                }
129            },
130            "state_key": user_id,
131            "type": "m.room.member",
132        });
133        assert_matches!(
134            from_json_value::<RawStrippedState>(pdu_event_json).unwrap(),
135            RawStrippedState::Pdu(_)
136        );
137    }
138
139    #[test]
140    fn serialize_stripped_state() {
141        let user_id = user_id!("@patrick:localhost");
142        let content = json!({
143            "membership": "join",
144        });
145
146        // Stripped format.
147        let stripped_event_json = json!({
148            "content": content,
149            "sender": user_id,
150            "state_key": user_id,
151            "type": "m.room.member",
152        });
153        let raw_stripped_event =
154            Raw::new(&stripped_event_json).unwrap().cast_unchecked::<AnyStrippedStateEvent>();
155        let stripped_state = RawStrippedState::from(raw_stripped_event);
156
157        let stripped_event_json = serde_json::to_string(&stripped_state).unwrap();
158        assert_eq!(
159            stripped_event_json,
160            r#"{"content":{"membership":"join"},"sender":"@patrick:localhost","state_key":"@patrick:localhost","type":"m.room.member"}"#
161        );
162    }
163}