ruma_client_api/uiaa/auth_data/
data_serde.rs1use std::borrow::Cow;
4
5use as_variant::as_variant;
6use ruma_common::{
7 serde::{JsonObject, from_raw_json_value},
8 thirdparty::Medium,
9};
10use serde::{Deserialize, Deserializer, Serialize, de};
11use serde_json::{Value as JsonValue, value::RawValue as RawJsonValue};
12
13use super::{
14 AuthData, CustomThirdPartyUserIdentifier, EmailUserIdentifier, MsisdnUserIdentifier,
15 UserIdentifier,
16};
17use crate::uiaa::{CustomAuthData, CustomUserIdentifier};
18
19impl<'de> Deserialize<'de> for AuthData {
20 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
21 where
22 D: Deserializer<'de>,
23 {
24 let json = Box::<RawJsonValue>::deserialize(deserializer)?;
25
26 #[derive(Deserialize)]
27 struct ExtractType<'a> {
28 #[serde(borrow, rename = "type")]
29 auth_type: Option<Cow<'a, str>>,
30 }
31
32 let ExtractType { auth_type } = from_raw_json_value(&json)?;
33
34 match auth_type.as_deref() {
35 Some("m.login.password") => from_raw_json_value(&json).map(Self::Password),
36 Some("m.login.recaptcha") => from_raw_json_value(&json).map(Self::ReCaptcha),
37 Some("m.login.email.identity") => from_raw_json_value(&json).map(Self::EmailIdentity),
38 Some("m.login.msisdn") => from_raw_json_value(&json).map(Self::Msisdn),
39 Some("m.login.dummy") => from_raw_json_value(&json).map(Self::Dummy),
40 Some("m.login.registration_token") => {
41 from_raw_json_value(&json).map(Self::RegistrationToken)
42 }
43 Some("m.login.terms") => from_raw_json_value(&json).map(Self::Terms),
44 Some("m.oauth" | "org.matrix.cross_signing_reset") => {
45 from_raw_json_value(&json).map(Self::OAuth)
46 }
47 None => from_raw_json_value(&json).map(Self::FallbackAcknowledgement),
48 Some(_) => {
49 let mut data = from_raw_json_value::<JsonObject, _>(&json)?;
50 let auth_type = as_variant!(
51 data.remove("type").expect("we already checked that the type field is present"),
52 JsonValue::String
53 )
54 .expect("we already checked that the type is a string");
55 let session = data
56 .remove("session")
57 .and_then(|session| as_variant!(session, JsonValue::String));
58
59 Ok(Self::_Custom(CustomAuthData { auth_type, session, data }))
60 }
61 }
62 }
63}
64
65impl<'de> Deserialize<'de> for UserIdentifier {
66 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
67 where
68 D: Deserializer<'de>,
69 {
70 #[derive(Deserialize)]
71 struct ExtractType<'a> {
72 #[serde(borrow, rename = "type")]
73 identifier_type: Cow<'a, str>,
74 }
75
76 let json = Box::<RawJsonValue>::deserialize(deserializer)?;
77 let ExtractType { identifier_type } =
78 serde_json::from_str(json.get()).map_err(de::Error::custom)?;
79
80 match identifier_type.as_ref() {
81 "m.id.user" => from_raw_json_value(&json).map(Self::Matrix),
82 "m.id.phone" => from_raw_json_value(&json).map(Self::PhoneNumber),
83 "m.id.thirdparty" => {
84 let id: CustomThirdPartyUserIdentifier = from_raw_json_value(&json)?;
85 match &id.medium {
86 Medium::Email => Ok(Self::Email(EmailUserIdentifier { address: id.address })),
87 Medium::Msisdn => Ok(Self::Msisdn(MsisdnUserIdentifier { number: id.address })),
88 _ => Ok(Self::_CustomThirdParty(id)),
89 }
90 }
91 _ => {
92 let mut data = from_raw_json_value::<JsonObject, _>(&json)?;
93 let identifier_type = as_variant!(
94 data.remove("type").expect("we already checked that the type field is present"),
95 JsonValue::String
96 )
97 .expect("we already checked that the type is a string");
98
99 Ok(Self::_Custom(CustomUserIdentifier { identifier_type, data }))
100 }
101 }
102 }
103}
104
105impl Serialize for EmailUserIdentifier {
106 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
107 where
108 S: serde::Serializer,
109 {
110 let Self { address } = self;
111
112 CustomThirdPartyUserIdentifier { medium: Medium::Email, address: address.clone() }
113 .serialize(serializer)
114 }
115}
116
117impl<'de> Deserialize<'de> for EmailUserIdentifier {
118 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
119 where
120 D: Deserializer<'de>,
121 {
122 let CustomThirdPartyUserIdentifier { medium, address } =
123 CustomThirdPartyUserIdentifier::deserialize(deserializer)?;
124
125 if medium != Medium::Email {
126 return Err(de::Error::invalid_value(
127 de::Unexpected::Str(medium.as_str()),
128 &Medium::Email.as_str(),
129 ));
130 }
131
132 Ok(Self { address })
133 }
134}
135
136impl Serialize for MsisdnUserIdentifier {
137 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
138 where
139 S: serde::Serializer,
140 {
141 let Self { number } = self;
142
143 CustomThirdPartyUserIdentifier { medium: Medium::Msisdn, address: number.clone() }
144 .serialize(serializer)
145 }
146}
147
148impl<'de> Deserialize<'de> for MsisdnUserIdentifier {
149 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
150 where
151 D: Deserializer<'de>,
152 {
153 let CustomThirdPartyUserIdentifier { medium, address } =
154 CustomThirdPartyUserIdentifier::deserialize(deserializer)?;
155
156 if medium != Medium::Msisdn {
157 return Err(de::Error::invalid_value(
158 de::Unexpected::Str(medium.as_str()),
159 &Medium::Msisdn.as_str(),
160 ));
161 }
162
163 Ok(Self { number: address })
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use assert_matches2::assert_let;
170 use ruma_common::canonical_json::assert_to_canonical_json_eq;
171 use serde_json::{Value as JsonValue, from_value as from_json_value, json};
172
173 use crate::uiaa::{
174 AuthData, EmailUserIdentifier, MatrixUserIdentifier, MsisdnUserIdentifier,
175 PhoneNumberUserIdentifier, UserIdentifier,
176 };
177
178 #[test]
179 fn serialize_user_identifier() {
180 assert_to_canonical_json_eq!(
181 UserIdentifier::Matrix(MatrixUserIdentifier::new("@user:notareal.hs".to_owned())),
182 json!({
183 "type": "m.id.user",
184 "user": "@user:notareal.hs",
185 })
186 );
187
188 assert_to_canonical_json_eq!(
189 UserIdentifier::PhoneNumber(PhoneNumberUserIdentifier::new(
190 "33".to_owned(),
191 "0102030405".to_owned()
192 )),
193 json!({
194 "type": "m.id.phone",
195 "country": "33",
196 "phone": "0102030405",
197 })
198 );
199
200 assert_to_canonical_json_eq!(
201 UserIdentifier::Email(EmailUserIdentifier::new("me@myprovider.net".to_owned())),
202 json!({
203 "type": "m.id.thirdparty",
204 "medium": "email",
205 "address": "me@myprovider.net",
206 })
207 );
208
209 assert_to_canonical_json_eq!(
210 UserIdentifier::Msisdn(MsisdnUserIdentifier::new("330102030405".to_owned())),
211 json!({
212 "type": "m.id.thirdparty",
213 "medium": "msisdn",
214 "address": "330102030405",
215 })
216 );
217
218 assert_to_canonical_json_eq!(
219 UserIdentifier::third_party_id("robot".into(), "01001110".to_owned()),
220 json!({
221 "type": "m.id.thirdparty",
222 "medium": "robot",
223 "address": "01001110",
224 })
225 );
226 }
227
228 #[test]
229 fn deserialize_user_identifier() {
230 let json = json!({
231 "type": "m.id.user",
232 "user": "@user:notareal.hs",
233 });
234 assert_let!(Ok(UserIdentifier::Matrix(id)) = from_json_value(json));
235 assert_eq!(id.user, "@user:notareal.hs");
236
237 let json = json!({
238 "type": "m.id.phone",
239 "country": "33",
240 "phone": "0102030405",
241 });
242 assert_let!(
243 Ok(UserIdentifier::PhoneNumber(PhoneNumberUserIdentifier { country, phone })) =
244 from_json_value(json)
245 );
246 assert_eq!(country, "33");
247 assert_eq!(phone, "0102030405");
248
249 let json = json!({
250 "type": "m.id.thirdparty",
251 "medium": "email",
252 "address": "me@myprovider.net",
253 });
254 assert_let!(Ok(UserIdentifier::Email(id)) = from_json_value(json));
255 assert_eq!(id.address, "me@myprovider.net");
256
257 let json = json!({
258 "type": "m.id.thirdparty",
259 "medium": "msisdn",
260 "address": "330102030405",
261 });
262 assert_let!(Ok(UserIdentifier::Msisdn(id)) = from_json_value(json));
263 assert_eq!(id.number, "330102030405");
264
265 let json = json!({
266 "type": "m.id.thirdparty",
267 "medium": "robot",
268 "address": "01110010",
269 });
270 let id = from_json_value::<UserIdentifier>(json).unwrap();
271 let (medium, address) = id.as_third_party_id().unwrap();
272 assert_eq!(medium.as_str(), "robot");
273 assert_eq!(address, "01110010");
274 }
275
276 #[test]
277 fn custom_user_identifier_roundtrip() {
278 let json = json!({
279 "type": "local.dev.identifier",
280 "foo": "bar",
281 });
282
283 let id = from_json_value::<UserIdentifier>(json.clone()).unwrap();
284 assert_eq!(id.identifier_type(), "local.dev.identifier");
285 let data = &*id.data();
286 assert_let!(Some(JsonValue::String(foo)) = data.get("foo"));
287 assert_eq!(foo, "bar");
288
289 assert_to_canonical_json_eq!(id, json);
290 }
291
292 #[test]
293 fn custom_auth_data_roundtrip() {
294 let json = json!({
295 "type": "local.dev.auth",
296 "session": "abcdef",
297 "foo": "bar",
298 });
299
300 let auth_data = from_json_value::<AuthData>(json.clone()).unwrap();
301 assert_eq!(auth_data.auth_type().unwrap().as_str(), "local.dev.auth");
302 assert_eq!(auth_data.session(), Some("abcdef"));
303 let data = auth_data.data();
304 assert_eq!(data.len(), 1);
305 assert_let!(Some(JsonValue::String(foo)) = data.get("foo"));
306 assert_eq!(foo, "bar");
307
308 assert_to_canonical_json_eq!(auth_data, json);
309 }
310}