ruma_identity_service_api/invitation/
store_invitation.rs

1//! `POST /_matrix/identity/*/store-invite`
2//!
3//! Store pending invitations to a user's third-party ID.
4
5pub mod v2 {
6    //! `/v2/` ([spec])
7    //!
8    //! [spec]: https://spec.matrix.org/latest/identity-service-api/#post_matrixidentityv2store-invite
9
10    use ruma_common::{
11        api::{request, response, Metadata},
12        metadata,
13        room::RoomType,
14        third_party_invite::IdentityServerBase64PublicKey,
15        thirdparty::Medium,
16        OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId, OwnedUserId,
17    };
18    use ruma_events::room::third_party_invite::RoomThirdPartyInviteEventContent;
19    use serde::{ser::SerializeSeq, Deserialize, Serialize};
20
21    const METADATA: Metadata = metadata! {
22        method: POST,
23        rate_limited: false,
24        authentication: AccessToken,
25        history: {
26            1.0 => "/_matrix/identity/v2/store-invite",
27        }
28    };
29
30    /// Request type for the `store_invitation` endpoint.
31    #[request]
32    pub struct Request {
33        /// The type of the third party identifier for the invited user.
34        ///
35        /// Currently, only `Medium::Email` is supported.
36        pub medium: Medium,
37
38        /// The email address of the invited user.
39        pub address: String,
40
41        /// The Matrix room ID to which the user is invited.
42        pub room_id: OwnedRoomId,
43
44        /// The Matrix user ID of the inviting user.
45        pub sender: OwnedUserId,
46
47        /// The Matrix room alias for the room to which the user is invited.
48        ///
49        /// This should be retrieved from the `m.room.canonical` state event.
50        #[serde(skip_serializing_if = "Option::is_none")]
51        pub room_alias: Option<OwnedRoomAliasId>,
52
53        /// The Content URI for the room to which the user is invited.
54        ///
55        /// This should be retrieved from the `m.room.avatar` state event.
56        #[serde(skip_serializing_if = "Option::is_none")]
57        pub room_avatar_url: Option<OwnedMxcUri>,
58
59        /// The `join_rule` for the room to which the user is invited.
60        ///
61        /// This should be retrieved from the `m.room.join_rules` state event.
62        #[serde(skip_serializing_if = "Option::is_none")]
63        pub room_join_rules: Option<String>,
64
65        /// The name of the room to which the user is invited.
66        ///
67        /// This should be retrieved from the `m.room.name` state event.
68        #[serde(skip_serializing_if = "Option::is_none")]
69        pub room_name: Option<String>,
70
71        /// The type of the room to which the user is invited.
72        ///
73        /// This should be retrieved from the `m.room.create` state event.
74        #[serde(skip_serializing_if = "Option::is_none")]
75        pub room_type: Option<RoomType>,
76
77        /// The display name of the user ID initiating the invite.
78        #[serde(skip_serializing_if = "Option::is_none")]
79        pub sender_display_name: Option<String>,
80
81        /// The Content URI for the avater of the user ID initiating the invite.
82        #[serde(skip_serializing_if = "Option::is_none")]
83        pub sender_avatar_url: Option<OwnedMxcUri>,
84    }
85
86    /// Response type for the `store_invitation` endpoint.
87    #[response]
88    pub struct Response {
89        /// The generated token.
90        ///
91        /// Must be a string consisting of the characters `[0-9a-zA-Z.=_-]`. Its length must not
92        /// exceed 255 characters and it must not be empty.
93        pub token: String,
94
95        /// A list of [server's long-term public key, generated ephemeral public key].
96        pub public_keys: PublicKeys,
97
98        /// The generated (redacted) display_name.
99        ///
100        /// An example is `f...@b...`.
101        pub display_name: String,
102    }
103
104    impl Request {
105        /// Creates a new `Request with the given medium, email address, room ID and sender.
106        pub fn new(
107            medium: Medium,
108            address: String,
109            room_id: OwnedRoomId,
110            sender: OwnedUserId,
111        ) -> Self {
112            Self {
113                medium,
114                address,
115                room_id,
116                sender,
117                room_alias: None,
118                room_avatar_url: None,
119                room_join_rules: None,
120                room_name: None,
121                room_type: None,
122                sender_display_name: None,
123                sender_avatar_url: None,
124            }
125        }
126
127        /// Creates a new `Request` with the given email address, room ID and sender.
128        pub fn email(address: String, room_id: OwnedRoomId, sender: OwnedUserId) -> Self {
129            Self::new(Medium::Email, address, room_id, sender)
130        }
131    }
132
133    impl Response {
134        /// Creates a new `Response` with the given token, public keys and display name.
135        pub fn new(token: String, public_keys: PublicKeys, display_name: String) -> Self {
136            Self { token, public_keys, display_name }
137        }
138    }
139
140    /// The server's long-term public key and generated ephemeral public key.
141    #[derive(Debug, Clone)]
142    #[allow(clippy::exhaustive_structs)]
143    pub struct PublicKeys {
144        /// The server's long-term public key.
145        pub server_key: PublicKey,
146
147        /// The generated ephemeral public key.
148        pub ephemeral_key: PublicKey,
149    }
150
151    impl<'de> Deserialize<'de> for PublicKeys {
152        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
153        where
154            D: serde::Deserializer<'de>,
155        {
156            let [server_key, ephemeral_key] = <[PublicKey; 2]>::deserialize(deserializer)?;
157
158            Ok(Self { server_key, ephemeral_key })
159        }
160    }
161
162    impl Serialize for PublicKeys {
163        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
164        where
165            S: serde::Serializer,
166        {
167            let mut seq = serializer.serialize_seq(Some(2))?;
168
169            seq.serialize_element(&self.server_key)?;
170            seq.serialize_element(&self.ephemeral_key)?;
171
172            seq.end()
173        }
174    }
175
176    /// A server's long-term or ephemeral public key.
177    #[derive(Clone, Debug, Serialize, Deserialize)]
178    #[non_exhaustive]
179    pub struct PublicKey {
180        /// The public key, encoded using unpadded base64.
181        pub public_key: IdentityServerBase64PublicKey,
182
183        /// The URI of an endpoint where the validity of this key can be checked by passing it as a
184        /// `public_key` query parameter.
185        pub key_validity_url: String,
186    }
187
188    impl PublicKey {
189        /// Constructs a new `PublicKey` with the given encoded public key and key validity URL.
190        pub fn new(public_key: IdentityServerBase64PublicKey, key_validity_url: String) -> Self {
191            Self { public_key, key_validity_url }
192        }
193    }
194
195    impl From<PublicKey> for ruma_events::room::third_party_invite::PublicKey {
196        fn from(key: PublicKey) -> Self {
197            let mut new_key = Self::new(key.public_key);
198            new_key.key_validity_url = Some(key.key_validity_url);
199            new_key
200        }
201    }
202
203    impl From<Response> for RoomThirdPartyInviteEventContent {
204        fn from(response: Response) -> Self {
205            let mut content = RoomThirdPartyInviteEventContent::new(
206                response.display_name,
207                response.public_keys.server_key.key_validity_url.clone(),
208                response.public_keys.server_key.public_key.clone(),
209            );
210            content.public_keys = Some(vec![
211                response.public_keys.server_key.into(),
212                response.public_keys.ephemeral_key.into(),
213            ]);
214            content
215        }
216    }
217}