1use xso::{text::Base64, AsXml, FromXml};
8
9use crate::bind2;
10use crate::ns;
11use crate::sm::StreamManagement;
12use jid::Jid;
13use minidom::Element;
14
15#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
17#[xml(namespace = ns::SASL2, name = "authentication")]
18pub struct Authentication {
19 #[xml(extract(n = .., name = "mechanism", fields(text(type_ = String))))]
21 pub mechanisms: Vec<String>,
22
23 #[xml(child(default))]
25 pub inline: Option<InlineFeatures>,
26}
27
28#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
30#[xml(namespace = ns::SASL2, name = "inline")]
31pub struct InlineFeatures {
32 #[xml(child(default))]
34 pub bind2: Option<bind2::BindFeature>,
35
36 #[xml(child(default))]
38 pub sm: Option<StreamManagement>,
39
40 #[xml(element(n = ..))]
42 pub payloads: Vec<Element>,
43}
44
45#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
47#[xml(namespace = ns::SASL2, name = "abort")]
48pub struct Abort {
49 #[xml(extract(default, fields(text(type_ = String))))]
51 pub text: Option<String>,
52
53 #[xml(element(n = ..))]
55 pub payloads: Vec<Element>,
56}
57
58#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
60#[xml(namespace = ns::SASL2, name = "user-agent")]
61pub struct UserAgent {
62 #[xml(attribute)]
64 pub id: uuid::Uuid,
65
66 #[xml(extract(default, fields(text(type_ = String))))]
68 pub software: Option<String>,
69
70 #[xml(extract(default, fields(text(type_ = String))))]
72 pub device: Option<String>,
73}
74
75#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
77#[xml(namespace = ns::SASL2, name = "authenticate")]
78pub struct Authenticate {
79 #[xml(attribute)]
81 pub mechanism: String,
82
83 #[xml(extract(default, name = "initial-response", fields(text = Base64)))]
85 pub initial_response: Option<Vec<u8>>,
86
87 #[xml(child)]
89 pub user_agent: UserAgent,
90
91 #[xml(element(n = ..))]
93 pub payloads: Vec<Element>,
94}
95
96#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
98#[xml(namespace = ns::SASL2, name = "challenge")]
99pub struct Challenge {
100 #[xml(text = Base64)]
102 pub sasl_data: Vec<u8>,
103}
104
105#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
107#[xml(namespace = ns::SASL2, name = "response")]
108pub struct Response {
109 #[xml(text = Base64)]
111 pub sasl_data: Vec<u8>,
112}
113
114#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
116#[xml(namespace = ns::SASL2, name = "success")]
117pub struct Success {
118 #[xml(extract(default, name = "additional-data", fields(text = Base64)))]
120 pub additional_data: Option<Vec<u8>>,
121
122 #[xml(extract(name = "authorization-identifier", fields(text)))]
124 pub authorization_identifier: Jid,
125
126 #[xml(element(n = ..))]
128 pub payloads: Vec<Element>,
129}
130
131#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
133#[xml(namespace = ns::SASL2, name = "failure")]
134pub struct Failure {
135 #[xml(extract(default, fields(text(type_ = String))))]
137 pub text: Option<String>,
138
139 #[xml(element(n = ..))]
141 pub payloads: Vec<Element>,
142}
143
144#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
146#[xml(namespace = ns::SASL2, name = "continue")]
147pub struct Continue {
148 #[xml(extract(name = "additional-data", fields(text = Base64)))]
150 pub additional_data: Vec<u8>,
151
152 #[xml(extract(fields(extract(n = .., name = "task", fields(text(type_ = String))))))]
157 pub tasks: Vec<String>,
158
159 #[xml(extract(default, fields(text(type_ = String))))]
161 pub text: Option<String>,
162}
163
164#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
166#[xml(namespace = ns::SASL2, name = "next")]
167pub struct Next {
168 #[xml(attribute)]
170 pub task: String,
171
172 #[xml(element(n = ..))]
174 pub payloads: Vec<Element>,
175}
176
177#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
179#[xml(namespace = ns::SASL2, name = "task-data")]
180pub struct TaskData {
181 #[xml(element(n = ..))]
183 pub payloads: Vec<Element>,
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189 use base64::prelude::*;
190 use uuid::Uuid;
191
192 #[cfg(target_pointer_width = "32")]
193 #[test]
194 fn test_size() {
195 assert_size!(Authentication, 40);
196 assert_size!(InlineFeatures, 28);
197 assert_size!(Abort, 24);
198 assert_size!(UserAgent, 40);
199 assert_size!(Authenticate, 76);
200 assert_size!(Challenge, 12);
201 assert_size!(Response, 12);
202 assert_size!(Success, 40);
203 assert_size!(Failure, 24);
204 assert_size!(Continue, 36);
205 assert_size!(Next, 24);
206 assert_size!(TaskData, 12);
207 }
208
209 #[cfg(target_pointer_width = "64")]
210 #[test]
211 fn test_size() {
212 assert_size!(Authentication, 80);
213 assert_size!(InlineFeatures, 56);
214 assert_size!(Abort, 48);
215 assert_size!(UserAgent, 64);
216 assert_size!(Authenticate, 136);
217 assert_size!(Challenge, 24);
218 assert_size!(Response, 24);
219 assert_size!(Success, 80);
220 assert_size!(Failure, 48);
221 assert_size!(Continue, 72);
222 assert_size!(Next, 48);
223 assert_size!(TaskData, 24);
224 }
225
226 #[test]
227 fn test_simple() {
228 let elem: Element = "<authentication xmlns='urn:xmpp:sasl:2'><mechanism>SCRAM-SHA-1</mechanism></authentication>"
229 .parse()
230 .unwrap();
231 let auth = Authentication::try_from(elem).unwrap();
232 assert_eq!(auth.mechanisms.len(), 1);
233 assert_eq!(auth.inline, None);
234
235 let elem: Element = "<challenge xmlns='urn:xmpp:sasl:2'>AAAA</challenge>"
236 .parse()
237 .unwrap();
238 let challenge = Challenge::try_from(elem).unwrap();
239 assert_eq!(challenge.sasl_data, b"\0\0\0");
240
241 let elem: Element = "<response xmlns='urn:xmpp:sasl:2'>YWJj</response>"
242 .parse()
243 .unwrap();
244 let response = Response::try_from(elem).unwrap();
245 assert_eq!(response.sasl_data, b"abc");
246 }
247
248 #[test]
250 fn test_auth() {
251 let elem: Element = r#"<authentication xmlns='urn:xmpp:sasl:2'>
252 <mechanism>SCRAM-SHA-1</mechanism>
253 <mechanism>SCRAM-SHA-1-PLUS</mechanism>
254 <inline>
255 <sm xmlns='urn:xmpp:sm:3'/>
256 <bind xmlns='urn:xmpp:bind:0'/>
257 </inline>
258 </authentication>"#
259 .parse()
260 .unwrap();
261
262 let auth = Authentication::try_from(elem).unwrap();
263
264 assert_eq!(auth.mechanisms.len(), 2);
265 let mut mech = auth.mechanisms.iter();
266 assert_eq!(mech.next().unwrap(), "SCRAM-SHA-1");
267 assert_eq!(mech.next().unwrap(), "SCRAM-SHA-1-PLUS");
268 assert_eq!(mech.next(), None);
269
270 let inline = auth.inline.unwrap();
271 assert_eq!(inline.bind2.unwrap().inline_features.len(), 0);
272 assert_eq!(
273 inline.sm.unwrap(),
274 StreamManagement {
275 optional: false,
276 required: false
277 }
278 );
279 assert_eq!(inline.payloads.len(), 0);
280 }
281
282 #[test]
284 fn test_authenticate() {
285 let elem: Element = r#"<authenticate xmlns='urn:xmpp:sasl:2' mechanism='SCRAM-SHA-1-PLUS'>
286 <initial-response>cD10bHMtZXhwb3J0ZXIsLG49dXNlcixyPTEyQzRDRDVDLUUzOEUtNEE5OC04RjZELTE1QzM4RjUxQ0NDNg==</initial-response>
287 <user-agent id='d4565fa7-4d72-4749-b3d3-740edbf87770'>
288 <software>AwesomeXMPP</software>
289 <device>Kiva's Phone</device>
290 </user-agent>
291 </authenticate>"#
292 .parse()
293 .unwrap();
294
295 let auth = Authenticate::try_from(elem).unwrap();
296
297 assert_eq!(auth.mechanism, "SCRAM-SHA-1-PLUS");
298 assert_eq!(
299 auth.initial_response.unwrap(),
300 BASE64_STANDARD.decode("cD10bHMtZXhwb3J0ZXIsLG49dXNlcixyPTEyQzRDRDVDLUUzOEUtNEE5OC04RjZELTE1QzM4RjUxQ0NDNg==").unwrap()
301 );
302
303 assert_eq!(auth.user_agent.software.as_ref().unwrap(), "AwesomeXMPP");
304 assert_eq!(auth.user_agent.device.as_ref().unwrap(), "Kiva's Phone");
305 }
306
307 #[test]
309 fn test_authenticate_2() {
310 let elem: Element = r#"<authenticate xmlns='urn:xmpp:sasl:2' mechanism='BLURDYBLOOP'>
311 <initial-response>SSBzaG91bGQgbWFrZSB0aGlzIGEgY29tcGV0aXRpb24=</initial-response>
312 <user-agent id='d4565fa7-4d72-4749-b3d3-740edbf87770'>
313 <software>AwesomeXMPP</software>
314 <device>Kiva's Phone</device>
315 </user-agent>
316 <bind xmlns='urn:xmpp:bind:example'/>
317 </authenticate>"#
318 .parse()
319 .unwrap();
320
321 let auth = Authenticate::try_from(elem).unwrap();
322
323 assert_eq!(auth.mechanism, "BLURDYBLOOP");
324 assert_eq!(
325 auth.initial_response.unwrap(),
326 BASE64_STANDARD
327 .decode("SSBzaG91bGQgbWFrZSB0aGlzIGEgY29tcGV0aXRpb24=")
328 .unwrap()
329 );
330
331 assert_eq!(auth.user_agent.software.as_ref().unwrap(), "AwesomeXMPP");
332 assert_eq!(auth.user_agent.device.as_ref().unwrap(), "Kiva's Phone");
333
334 assert_eq!(auth.payloads.len(), 1);
335 let bind = auth.payloads.iter().next().unwrap();
336 assert!(bind.is("bind", "urn:xmpp:bind:example"));
337 }
338
339 #[test]
341 fn test_example_5() {
342 let elem: Element = "<challenge xmlns='urn:xmpp:sasl:2'>cj0xMkM0Q0Q1Qy1FMzhFLTRBOTgtOEY2RC0xNUMzOEY1MUNDQzZhMDkxMTdhNi1hYzUwLTRmMmYtOTNmMS05Mzc5OWMyYmRkZjYscz1RU1hDUitRNnNlazhiZjkyLGk9NDA5Ng==</challenge>"
343 .parse()
344 .unwrap();
345 let challenge = Challenge::try_from(elem).unwrap();
346 assert_eq!(
347 challenge.sasl_data,
348 b"r=12C4CD5C-E38E-4A98-8F6D-15C38F51CCC6a09117a6-ac50-4f2f-93f1-93799c2bddf6,s=QSXCR+Q6sek8bf92,i=4096"
349 );
350
351 let elem: Element = "<response xmlns='urn:xmpp:sasl:2'>Yz1jRDEwYkhNdFpYaHdiM0owWlhJc0xNY29Rdk9kQkRlUGQ0T3N3bG1BV1YzZGcxYTFXaDF0WVBUQndWaWQxMFZVLHI9MTJDNENENUMtRTM4RS00QTk4LThGNkQtMTVDMzhGNTFDQ0M2YTA5MTE3YTYtYWM1MC00ZjJmLTkzZjEtOTM3OTljMmJkZGY2LHA9VUFwbzd4bzZQYTlKK1ZhZWpmei9kRzdCb21VPQ==</response>"
352 .parse()
353 .unwrap();
354 let response = Response::try_from(elem).unwrap();
355 assert_eq!(
356 response.sasl_data,
357 b"c=cD10bHMtZXhwb3J0ZXIsLMcoQvOdBDePd4OswlmAWV3dg1a1Wh1tYPTBwVid10VU,r=12C4CD5C-E38E-4A98-8F6D-15C38F51CCC6a09117a6-ac50-4f2f-93f1-93799c2bddf6,p=UApo7xo6Pa9J+Vaejfz/dG7BomU="
358 );
359 }
360
361 #[test]
363 fn test_example_7_8() {
364 let elem: Element = r#"<success xmlns='urn:xmpp:sasl:2'>
365 <additional-data>dj1tc1ZIcy9CeklPSERxWGVWSDdFbW1EdTlpZDg9</additional-data>
366 <authorization-identifier>user@example.org</authorization-identifier>
367 </success>"#
368 .parse()
369 .unwrap();
370
371 let success = Success::try_from(elem).unwrap();
372
373 assert_eq!(
374 success.additional_data.unwrap(),
375 BASE64_STANDARD
376 .decode("dj1tc1ZIcy9CeklPSERxWGVWSDdFbW1EdTlpZDg9")
377 .unwrap()
378 );
379
380 assert_eq!(
381 success.authorization_identifier,
382 Jid::new("user@example.org").unwrap()
383 );
384
385 let elem: Element = r#"<success xmlns='urn:xmpp:sasl:2'>
386 <additional-data>ip/AeIOfZXKBV+fW2smE0GUB3I//nnrrLCYkt0Vj</additional-data>
387 <authorization-identifier>juliet@montague.example/Balcony/a987dsh9a87sdh</authorization-identifier>
388 </success>"#
389 .parse()
390 .unwrap();
391
392 let success = Success::try_from(elem).unwrap();
393
394 assert_eq!(
395 success.additional_data.unwrap(),
396 BASE64_STANDARD
397 .decode("ip/AeIOfZXKBV+fW2smE0GUB3I//nnrrLCYkt0Vj")
398 .unwrap()
399 );
400
401 assert_eq!(
402 success.authorization_identifier,
403 Jid::new("juliet@montague.example/Balcony/a987dsh9a87sdh").unwrap()
404 );
405 }
406
407 #[test]
409 fn example_success_stream_management() {
410 let elem: Element = r#"<success xmlns='urn:xmpp:sasl:2'>
411 <additional-data>SGFkIHlvdSBnb2luZywgdGhlcmUsIGRpZG4ndCBJPw==</additional-data>
412 <authorization-identifier>juliet@montague.example</authorization-identifier>
413 <resumed xmlns='urn:xmpp:sm:3' h='345' previd='124'/>
414 </success>"#
415 .parse()
416 .unwrap();
417
418 let success = Success::try_from(elem).unwrap();
419
420 assert_eq!(
421 success.additional_data.unwrap(),
422 BASE64_STANDARD
423 .decode("SGFkIHlvdSBnb2luZywgdGhlcmUsIGRpZG4ndCBJPw==")
424 .unwrap()
425 );
426
427 assert_eq!(
428 success.authorization_identifier,
429 Jid::new("juliet@montague.example").unwrap()
430 );
431
432 assert_eq!(success.payloads.len(), 1);
433 let resumed =
434 crate::sm::Resumed::try_from(success.payloads.into_iter().next().unwrap()).unwrap();
435 assert_eq!(resumed.h, 345);
436 assert_eq!(resumed.previd, crate::sm::StreamId(String::from("124")));
437 }
438
439 #[test]
441 fn example_failure() {
442 let elem: Element = r#"<failure xmlns='urn:xmpp:sasl:2'>
443 <aborted xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
444 <optional-application-specific xmlns='urn:something:else'/>
445 <text>This is a terrible example.</text>
446</failure>"#
447 .parse()
448 .unwrap();
449
450 let failure = Failure::try_from(elem).unwrap();
451
452 assert_eq!(failure.text.unwrap(), "This is a terrible example.");
453
454 assert_eq!(failure.payloads.len(), 2);
455
456 let mut payloads = failure.payloads.into_iter();
457
458 let condition = crate::sasl::DefinedCondition::try_from(payloads.next().unwrap()).unwrap();
459 assert_eq!(condition, crate::sasl::DefinedCondition::Aborted);
460
461 assert!(payloads
462 .next()
463 .unwrap()
464 .is("optional-application-specific", "urn:something:else"));
465 }
466
467 #[test]
468 fn example_failure_no_text() {
469 let elem: Element = r#"<failure xmlns='urn:xmpp:sasl:2'><aborted xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/></failure>"#
470 .parse()
471 .unwrap();
472
473 let failure = Failure::try_from(elem).unwrap();
474
475 assert_eq!(failure.text, None);
476
477 assert_eq!(failure.payloads.len(), 1);
478
479 let mut payloads = failure.payloads.into_iter();
480
481 let condition = crate::sasl::DefinedCondition::try_from(payloads.next().unwrap()).unwrap();
482 assert_eq!(condition, crate::sasl::DefinedCondition::Aborted);
483 }
484
485 #[test]
487 fn example_11() {
488 let elem: Element = r#"<continue xmlns='urn:xmpp:sasl:2'>
489 <additional-data>SSdtIGJvcmVkIG5vdy4=</additional-data>
490 <tasks>
491 <task>HOTP-EXAMPLE</task>
492 <task>TOTP-EXAMPLE</task>
493 </tasks>
494 <text>This account requires 2FA</text>
495</continue>"#
496 .parse()
497 .unwrap();
498
499 let cont = Continue::try_from(elem).unwrap();
500
501 assert_eq!(
502 cont.additional_data,
503 BASE64_STANDARD.decode("SSdtIGJvcmVkIG5vdy4=").unwrap()
504 );
505
506 assert_eq!(cont.text.as_deref(), Some("This account requires 2FA"));
507
508 assert_eq!(cont.tasks.len(), 2);
509 let mut tasks = cont.tasks.into_iter();
510
511 assert_eq!(tasks.next().unwrap(), "HOTP-EXAMPLE");
512
513 assert_eq!(tasks.next().unwrap(), "TOTP-EXAMPLE");
514 }
515
516 #[test]
518 fn test_fictional_totp() {
519 let elem: Element = r#"<next xmlns='urn:xmpp:sasl:2' task='TOTP-EXAMPLE'>
520 <totp xmlns="urn:totp:example">SSd2ZSBydW4gb3V0IG9mIGlkZWFzIGhlcmUu</totp>
521</next>"#
522 .parse()
523 .unwrap();
524
525 let next = Next::try_from(elem).unwrap();
526 assert_eq!(next.task, "TOTP-EXAMPLE");
527
528 let payload = next.payloads.into_iter().next().unwrap();
529 assert!(payload.is("totp", "urn:totp:example"));
530 assert_eq!(&payload.text(), "SSd2ZSBydW4gb3V0IG9mIGlkZWFzIGhlcmUu");
531
532 let elem: Element = r#"<task-data xmlns='urn:xmpp:sasl:2'>
533 <totp xmlns="urn:totp:example">94d27acffa2e99a42ba7786162a9e73e7ab17b9d</totp>
534</task-data>"#
535 .parse()
536 .unwrap();
537
538 let task_data = TaskData::try_from(elem).unwrap();
539 let payload = task_data.payloads.into_iter().next().unwrap();
540 assert!(payload.is("totp", "urn:totp:example"));
541 assert_eq!(&payload.text(), "94d27acffa2e99a42ba7786162a9e73e7ab17b9d");
542
543 let elem: Element = r#"<task-data xmlns='urn:xmpp:sasl:2'>
544 <totp xmlns="urn:totp:example">OTRkMjdhY2ZmYTJlOTlhNDJiYTc3ODYxNjJhOWU3M2U3YWIxN2I5ZAo=</totp>
545</task-data>"#
546 .parse()
547 .unwrap();
548
549 let task_data = TaskData::try_from(elem).unwrap();
550 let payload = task_data.payloads.into_iter().next().unwrap();
551 assert!(payload.is("totp", "urn:totp:example"));
552 assert_eq!(
553 &payload.text(),
554 "OTRkMjdhY2ZmYTJlOTlhNDJiYTc3ODYxNjJhOWU3M2U3YWIxN2I5ZAo="
555 );
556
557 let elem: Element = r#"<success xmlns='urn:xmpp:sasl:2'>
558 <totp xmlns="urn:totp:example">SGFkIHlvdSBnb2luZywgdGhlcmUsIGRpZG4ndCBJPw==</totp>
559 <authorization-identifier>juliet@montague.example</authorization-identifier>
560</success>"#
561 .parse()
562 .unwrap();
563
564 let success = Success::try_from(elem).unwrap();
565 assert_eq!(success.additional_data, None);
566
567 let payload = success.payloads.into_iter().next().unwrap();
568 assert!(payload.is("totp", "urn:totp:example"));
569 assert_eq!(
570 &payload.text(),
571 "SGFkIHlvdSBnb2luZywgdGhlcmUsIGRpZG4ndCBJPw=="
572 );
573
574 assert_eq!(
575 success.authorization_identifier,
576 Jid::new("juliet@montague.example").unwrap(),
577 )
578 }
579
580 #[test]
582 fn example_13() {
583 let elem: Element = r#"<authenticate xmlns='urn:xmpp:sasl:2' mechanism='PLAIN'>
584 <initial-response>AGFsaWNlQGV4YW1wbGUub3JnCjM0NQ==</initial-response>
585 <user-agent id='d4565fa7-4d72-4749-b3d3-740edbf87770'>
586 <software>AwesomeXMPP</software>
587 <device>Kiva's Phone</device>
588 </user-agent>
589</authenticate>"#
590 .parse()
591 .unwrap();
592
593 let auth = Authenticate::try_from(elem).unwrap();
594
595 assert_eq!(auth.mechanism, "PLAIN");
596 assert_eq!(
597 auth.initial_response.unwrap(),
598 BASE64_STANDARD
599 .decode("AGFsaWNlQGV4YW1wbGUub3JnCjM0NQ==")
600 .unwrap()
601 );
602
603 assert_eq!(auth.payloads.len(), 0);
604
605 let user_agent = auth.user_agent;
606 assert_eq!(
607 user_agent.id,
608 "d4565fa7-4d72-4749-b3d3-740edbf87770"
609 .parse::<Uuid>()
610 .unwrap()
611 );
612 assert_eq!(user_agent.software.as_deref(), Some("AwesomeXMPP"));
613 assert_eq!(user_agent.device.as_deref(), Some("Kiva's Phone"));
614
615 let elem: Element = r#"<success xmlns='urn:xmpp:sasl:2'>
616 <authorization-identifier>alice@example.org</authorization-identifier>
617</success>"#
618 .parse()
619 .unwrap();
620
621 let success = Success::try_from(elem).unwrap();
622 assert_eq!(
623 success.authorization_identifier,
624 Jid::new("alice@example.org").unwrap()
625 );
626 assert_eq!(success.additional_data, None);
627 assert_eq!(success.payloads.len(), 0);
628 }
629
630 #[test]
632 fn example_14() {
633 let elem: Element = r#"<authenticate xmlns='urn:xmpp:sasl:2' mechanism='CRAM-MD5'>
634 <user-agent id='d4565fa7-4d72-4749-b3d3-740edbf87770'>
635 <software>AwesomeXMPP</software>
636 <device>Kiva's Phone</device>
637 </user-agent>
638</authenticate>"#
639 .parse()
640 .unwrap();
641
642 let auth = Authenticate::try_from(elem).unwrap();
643
644 assert_eq!(auth.mechanism, "CRAM-MD5");
645 assert_eq!(auth.initial_response, None);
646 assert_eq!(auth.payloads.len(), 0);
647
648 let user_agent = auth.user_agent;
649 assert_eq!(
650 user_agent.id,
651 "d4565fa7-4d72-4749-b3d3-740edbf87770"
652 .parse::<Uuid>()
653 .unwrap()
654 );
655 assert_eq!(user_agent.software.as_deref(), Some("AwesomeXMPP"));
656 assert_eq!(user_agent.device.as_deref(), Some("Kiva's Phone"));
657
658 let elem: Element = r#"<challenge xmlns='urn:xmpp:sasl:2'>PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2UucmVzdG9uLm1jaS5uZXQ+</challenge>"#
659 .parse()
660 .unwrap();
661
662 let challenge = Challenge::try_from(elem).unwrap();
663 assert_eq!(
664 challenge.sasl_data,
665 BASE64_STANDARD
666 .decode("PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2UucmVzdG9uLm1jaS5uZXQ+")
667 .unwrap()
668 );
669
670 let elem: Element = r#"<response xmlns='urn:xmpp:sasl:2'>dGltIGI5MTNhNjAyYzdlZGE3YTQ5NWI0ZTZlNzMzNGQzODkw</response>"#
671 .parse()
672 .unwrap();
673
674 let response = Response::try_from(elem).unwrap();
675 assert_eq!(
676 response.sasl_data,
677 BASE64_STANDARD
678 .decode("dGltIGI5MTNhNjAyYzdlZGE3YTQ5NWI0ZTZlNzMzNGQzODkw")
679 .unwrap()
680 );
681
682 let elem: Element = r#"<success xmlns='urn:xmpp:sasl:2'>
683 <authorization-identifier>tim@example.org</authorization-identifier>
684</success>
685 "#
686 .parse()
687 .unwrap();
688
689 let success = Success::try_from(elem).unwrap();
690 assert_eq!(
691 success.authorization_identifier,
692 Jid::new("tim@example.org").unwrap()
693 );
694 assert_eq!(success.additional_data, None);
695 assert_eq!(success.payloads.len(), 0);
696 }
697
698 #[test]
700 fn example_15() {
701 let elem: Element = r#"<authenticate xmlns='urn:xmpp:sasl:2' mechanism='BLURDYBLOOP'>
702 <initial-response>SW5pdGlhbCBSZXNwb25zZQ==</initial-response>
703 <user-agent id='d4565fa7-4d72-4749-b3d3-740edbf87770'>
704 <software>AwesomeXMPP</software>
705 <device>Kiva's Phone</device>
706 </user-agent>
707 <megabind xmlns='urn:example:megabind'>
708 <resource>this-one-please</resource>
709 </megabind>
710</authenticate>"#
711 .parse()
712 .unwrap();
713
714 let auth = Authenticate::try_from(elem).unwrap();
715 assert_eq!(auth.mechanism, "BLURDYBLOOP");
716 assert_eq!(
717 auth.initial_response,
718 Some(BASE64_STANDARD.decode("SW5pdGlhbCBSZXNwb25zZQ==").unwrap())
719 );
720
721 assert_eq!(
722 auth.user_agent.id,
723 "d4565fa7-4d72-4749-b3d3-740edbf87770"
724 .parse::<Uuid>()
725 .unwrap()
726 );
727 assert_eq!(auth.user_agent.software.as_deref(), Some("AwesomeXMPP"));
728 assert_eq!(auth.user_agent.device.as_deref(), Some("Kiva's Phone"));
729
730 assert_eq!(auth.payloads.len(), 1);
731 let bind = auth.payloads.into_iter().next().unwrap();
732 assert!(bind.is("megabind", "urn:example:megabind"));
733
734 let mut bind_payloads = bind.children();
735 let resource = bind_payloads.next().unwrap();
736 assert_eq!(resource.name(), "resource");
737 assert_eq!(&resource.text(), "this-one-please");
738 assert_eq!(bind_payloads.next(), None);
739
740 let elem: Element = r#"<challenge xmlns='urn:xmpp:sasl:2'>PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2UucmVzdG9uLm1jaS5uZXQ+</challenge>"#
741 .parse()
742 .unwrap();
743 let challenge = Challenge::try_from(elem).unwrap();
744 assert_eq!(
745 challenge.sasl_data,
746 BASE64_STANDARD
747 .decode("PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2UucmVzdG9uLm1jaS5uZXQ+")
748 .unwrap()
749 );
750
751 let elem: Element = r#"<response xmlns='urn:xmpp:sasl:2'>dGltIGI5MTNhNjAyYzdlZGE3YTQ5NWI0ZTZlNzMzNGQzODkw</response>"#
752 .parse()
753 .unwrap();
754 let response = Response::try_from(elem).unwrap();
755 assert_eq!(response.sasl_data, b"tim b913a602c7eda7a495b4e6e7334d3890");
756
757 let elem: Element = r#"<continue xmlns='urn:xmpp:sasl:2'>
758 <additional-data>QWRkaXRpb25hbCBEYXRh</additional-data>
759 <tasks>
760 <task>UNREALISTIC-2FA</task>
761 </tasks>
762</continue>"#
763 .parse()
764 .unwrap();
765 let cont = Continue::try_from(elem).unwrap();
766 assert_eq!(
767 cont.additional_data,
768 BASE64_STANDARD.decode("QWRkaXRpb25hbCBEYXRh").unwrap()
769 );
770 assert_eq!(cont.tasks.len(), 1);
771 assert_eq!(cont.tasks.into_iter().next().unwrap(), "UNREALISTIC-2FA");
772
773 let elem: Element = r#"<next xmlns='urn:xmpp:sasl:2' task='UNREALISTIC-2FA'>
774 <parameters xmlns='urn:example:unrealistic2fa'>VW5yZWFsaXN0aWMgMkZBIElS</parameters>
775</next>"#
776 .parse()
777 .unwrap();
778 let next = Next::try_from(elem).unwrap();
779 assert_eq!(next.payloads.len(), 1);
780 let params = next.payloads.into_iter().next().unwrap();
781 assert!(params.is("parameters", "urn:example:unrealistic2fa"));
782 assert_eq!(¶ms.text(), "VW5yZWFsaXN0aWMgMkZBIElS");
783
784 let elem: Element = r#"<task-data xmlns='urn:xmpp:sasl:2'>
785 <question xmlns='urn:example:unrealistic2fa'>PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2UucmVzdG9uLm1jaS5uZXQ+</question>
786</task-data>"#
787 .parse()
788 .unwrap();
789 let task_data = TaskData::try_from(elem).unwrap();
790 assert_eq!(task_data.payloads.len(), 1);
791 let question = task_data.payloads.into_iter().next().unwrap();
792 assert!(question.is("question", "urn:example:unrealistic2fa"));
793 assert_eq!(
794 &question.text(),
795 "PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2UucmVzdG9uLm1jaS5uZXQ+"
796 );
797
798 let elem: Element = r#"<task-data xmlns='urn:xmpp:sasl:2'>
799 <response xmlns='urn:example:unrealistic2fa'>dGltIGI5MTNhNjAyYzdlZGE3YTQ5NWI0ZTZlNzMzNGQzODkw</response>
800</task-data>"#
801 .parse()
802 .unwrap();
803 let task_data = TaskData::try_from(elem).unwrap();
804 assert_eq!(task_data.payloads.len(), 1);
805 let response = task_data.payloads.into_iter().next().unwrap();
806 assert!(response.is("response", "urn:example:unrealistic2fa"));
807 assert_eq!(
808 &response.text(),
809 "dGltIGI5MTNhNjAyYzdlZGE3YTQ5NWI0ZTZlNzMzNGQzODkw"
810 );
811
812 let elem: Element = r#"<success xmlns='urn:xmpp:sasl:2'>
813 <result xmlns='urn:example:unrealistic2fa'>VW5yZWFsaXN0aWMgMkZBIG11dHVhbCBhdXRoIGRhdGE=</result>
814 <authorization-identifier>alice@example.org/this-one-please</authorization-identifier>
815</success>"#
816 .parse()
817 .unwrap();
818 let success = Success::try_from(elem).unwrap();
819 assert_eq!(
820 success.authorization_identifier,
821 Jid::new("alice@example.org/this-one-please").unwrap()
822 );
823
824 assert_eq!(success.payloads.len(), 1);
825 let res = success.payloads.into_iter().next().unwrap();
826 assert!(res.is("result", "urn:example:unrealistic2fa"));
827 assert_eq!(&res.text(), "VW5yZWFsaXN0aWMgMkZBIG11dHVhbCBhdXRoIGRhdGE=");
828 }
829}