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!(inline.sm.unwrap(), StreamManagement { optional: false });
273 assert_eq!(inline.payloads.len(), 0);
274 }
275
276 #[test]
278 fn test_authenticate() {
279 let elem: Element = r#"<authenticate xmlns='urn:xmpp:sasl:2' mechanism='SCRAM-SHA-1-PLUS'>
280 <initial-response>cD10bHMtZXhwb3J0ZXIsLG49dXNlcixyPTEyQzRDRDVDLUUzOEUtNEE5OC04RjZELTE1QzM4RjUxQ0NDNg==</initial-response>
281 <user-agent id='d4565fa7-4d72-4749-b3d3-740edbf87770'>
282 <software>AwesomeXMPP</software>
283 <device>Kiva's Phone</device>
284 </user-agent>
285 </authenticate>"#
286 .parse()
287 .unwrap();
288
289 let auth = Authenticate::try_from(elem).unwrap();
290
291 assert_eq!(auth.mechanism, "SCRAM-SHA-1-PLUS");
292 assert_eq!(
293 auth.initial_response.unwrap(),
294 BASE64_STANDARD.decode("cD10bHMtZXhwb3J0ZXIsLG49dXNlcixyPTEyQzRDRDVDLUUzOEUtNEE5OC04RjZELTE1QzM4RjUxQ0NDNg==").unwrap()
295 );
296
297 assert_eq!(auth.user_agent.software.as_ref().unwrap(), "AwesomeXMPP");
298 assert_eq!(auth.user_agent.device.as_ref().unwrap(), "Kiva's Phone");
299 }
300
301 #[test]
303 fn test_authenticate_2() {
304 let elem: Element = r#"<authenticate xmlns='urn:xmpp:sasl:2' mechanism='BLURDYBLOOP'>
305 <initial-response>SSBzaG91bGQgbWFrZSB0aGlzIGEgY29tcGV0aXRpb24=</initial-response>
306 <user-agent id='d4565fa7-4d72-4749-b3d3-740edbf87770'>
307 <software>AwesomeXMPP</software>
308 <device>Kiva's Phone</device>
309 </user-agent>
310 <bind xmlns='urn:xmpp:bind:example'/>
311 </authenticate>"#
312 .parse()
313 .unwrap();
314
315 let auth = Authenticate::try_from(elem).unwrap();
316
317 assert_eq!(auth.mechanism, "BLURDYBLOOP");
318 assert_eq!(
319 auth.initial_response.unwrap(),
320 BASE64_STANDARD
321 .decode("SSBzaG91bGQgbWFrZSB0aGlzIGEgY29tcGV0aXRpb24=")
322 .unwrap()
323 );
324
325 assert_eq!(auth.user_agent.software.as_ref().unwrap(), "AwesomeXMPP");
326 assert_eq!(auth.user_agent.device.as_ref().unwrap(), "Kiva's Phone");
327
328 assert_eq!(auth.payloads.len(), 1);
329 let bind = auth.payloads.iter().next().unwrap();
330 assert!(bind.is("bind", "urn:xmpp:bind:example"));
331 }
332
333 #[test]
335 fn test_example_5() {
336 let elem: Element = "<challenge xmlns='urn:xmpp:sasl:2'>cj0xMkM0Q0Q1Qy1FMzhFLTRBOTgtOEY2RC0xNUMzOEY1MUNDQzZhMDkxMTdhNi1hYzUwLTRmMmYtOTNmMS05Mzc5OWMyYmRkZjYscz1RU1hDUitRNnNlazhiZjkyLGk9NDA5Ng==</challenge>"
337 .parse()
338 .unwrap();
339 let challenge = Challenge::try_from(elem).unwrap();
340 assert_eq!(
341 challenge.sasl_data,
342 b"r=12C4CD5C-E38E-4A98-8F6D-15C38F51CCC6a09117a6-ac50-4f2f-93f1-93799c2bddf6,s=QSXCR+Q6sek8bf92,i=4096"
343 );
344
345 let elem: Element = "<response xmlns='urn:xmpp:sasl:2'>Yz1jRDEwYkhNdFpYaHdiM0owWlhJc0xNY29Rdk9kQkRlUGQ0T3N3bG1BV1YzZGcxYTFXaDF0WVBUQndWaWQxMFZVLHI9MTJDNENENUMtRTM4RS00QTk4LThGNkQtMTVDMzhGNTFDQ0M2YTA5MTE3YTYtYWM1MC00ZjJmLTkzZjEtOTM3OTljMmJkZGY2LHA9VUFwbzd4bzZQYTlKK1ZhZWpmei9kRzdCb21VPQ==</response>"
346 .parse()
347 .unwrap();
348 let response = Response::try_from(elem).unwrap();
349 assert_eq!(
350 response.sasl_data,
351 b"c=cD10bHMtZXhwb3J0ZXIsLMcoQvOdBDePd4OswlmAWV3dg1a1Wh1tYPTBwVid10VU,r=12C4CD5C-E38E-4A98-8F6D-15C38F51CCC6a09117a6-ac50-4f2f-93f1-93799c2bddf6,p=UApo7xo6Pa9J+Vaejfz/dG7BomU="
352 );
353 }
354
355 #[test]
357 fn test_example_7_8() {
358 let elem: Element = r#"<success xmlns='urn:xmpp:sasl:2'>
359 <additional-data>dj1tc1ZIcy9CeklPSERxWGVWSDdFbW1EdTlpZDg9</additional-data>
360 <authorization-identifier>user@example.org</authorization-identifier>
361 </success>"#
362 .parse()
363 .unwrap();
364
365 let success = Success::try_from(elem).unwrap();
366
367 assert_eq!(
368 success.additional_data.unwrap(),
369 BASE64_STANDARD
370 .decode("dj1tc1ZIcy9CeklPSERxWGVWSDdFbW1EdTlpZDg9")
371 .unwrap()
372 );
373
374 assert_eq!(
375 success.authorization_identifier,
376 Jid::new("user@example.org").unwrap()
377 );
378
379 let elem: Element = r#"<success xmlns='urn:xmpp:sasl:2'>
380 <additional-data>ip/AeIOfZXKBV+fW2smE0GUB3I//nnrrLCYkt0Vj</additional-data>
381 <authorization-identifier>juliet@montague.example/Balcony/a987dsh9a87sdh</authorization-identifier>
382 </success>"#
383 .parse()
384 .unwrap();
385
386 let success = Success::try_from(elem).unwrap();
387
388 assert_eq!(
389 success.additional_data.unwrap(),
390 BASE64_STANDARD
391 .decode("ip/AeIOfZXKBV+fW2smE0GUB3I//nnrrLCYkt0Vj")
392 .unwrap()
393 );
394
395 assert_eq!(
396 success.authorization_identifier,
397 Jid::new("juliet@montague.example/Balcony/a987dsh9a87sdh").unwrap()
398 );
399 }
400
401 #[test]
403 fn example_success_stream_management() {
404 let elem: Element = r#"<success xmlns='urn:xmpp:sasl:2'>
405 <additional-data>SGFkIHlvdSBnb2luZywgdGhlcmUsIGRpZG4ndCBJPw==</additional-data>
406 <authorization-identifier>juliet@montague.example</authorization-identifier>
407 <resumed xmlns='urn:xmpp:sm:3' h='345' previd='124'/>
408 </success>"#
409 .parse()
410 .unwrap();
411
412 let success = Success::try_from(elem).unwrap();
413
414 assert_eq!(
415 success.additional_data.unwrap(),
416 BASE64_STANDARD
417 .decode("SGFkIHlvdSBnb2luZywgdGhlcmUsIGRpZG4ndCBJPw==")
418 .unwrap()
419 );
420
421 assert_eq!(
422 success.authorization_identifier,
423 Jid::new("juliet@montague.example").unwrap()
424 );
425
426 assert_eq!(success.payloads.len(), 1);
427 let resumed =
428 crate::sm::Resumed::try_from(success.payloads.into_iter().next().unwrap()).unwrap();
429 assert_eq!(resumed.h, 345);
430 assert_eq!(resumed.previd, crate::sm::StreamId(String::from("124")));
431 }
432
433 #[test]
435 fn example_failure() {
436 let elem: Element = r#"<failure xmlns='urn:xmpp:sasl:2'>
437 <aborted xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
438 <optional-application-specific xmlns='urn:something:else'/>
439 <text>This is a terrible example.</text>
440</failure>"#
441 .parse()
442 .unwrap();
443
444 let failure = Failure::try_from(elem).unwrap();
445
446 assert_eq!(failure.text.unwrap(), "This is a terrible example.");
447
448 assert_eq!(failure.payloads.len(), 2);
449
450 let mut payloads = failure.payloads.into_iter();
451
452 let condition = crate::sasl::DefinedCondition::try_from(payloads.next().unwrap()).unwrap();
453 assert_eq!(condition, crate::sasl::DefinedCondition::Aborted);
454
455 assert!(payloads
456 .next()
457 .unwrap()
458 .is("optional-application-specific", "urn:something:else"));
459 }
460
461 #[test]
462 fn example_failure_no_text() {
463 let elem: Element = r#"<failure xmlns='urn:xmpp:sasl:2'><aborted xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/></failure>"#
464 .parse()
465 .unwrap();
466
467 let failure = Failure::try_from(elem).unwrap();
468
469 assert_eq!(failure.text, None);
470
471 assert_eq!(failure.payloads.len(), 1);
472
473 let mut payloads = failure.payloads.into_iter();
474
475 let condition = crate::sasl::DefinedCondition::try_from(payloads.next().unwrap()).unwrap();
476 assert_eq!(condition, crate::sasl::DefinedCondition::Aborted);
477 }
478
479 #[test]
481 fn example_11() {
482 let elem: Element = r#"<continue xmlns='urn:xmpp:sasl:2'>
483 <additional-data>SSdtIGJvcmVkIG5vdy4=</additional-data>
484 <tasks>
485 <task>HOTP-EXAMPLE</task>
486 <task>TOTP-EXAMPLE</task>
487 </tasks>
488 <text>This account requires 2FA</text>
489</continue>"#
490 .parse()
491 .unwrap();
492
493 let cont = Continue::try_from(elem).unwrap();
494
495 assert_eq!(
496 cont.additional_data,
497 BASE64_STANDARD.decode("SSdtIGJvcmVkIG5vdy4=").unwrap()
498 );
499
500 assert_eq!(cont.text.as_deref(), Some("This account requires 2FA"));
501
502 assert_eq!(cont.tasks.len(), 2);
503 let mut tasks = cont.tasks.into_iter();
504
505 assert_eq!(tasks.next().unwrap(), "HOTP-EXAMPLE");
506
507 assert_eq!(tasks.next().unwrap(), "TOTP-EXAMPLE");
508 }
509
510 #[test]
512 fn test_fictional_totp() {
513 let elem: Element = r#"<next xmlns='urn:xmpp:sasl:2' task='TOTP-EXAMPLE'>
514 <totp xmlns="urn:totp:example">SSd2ZSBydW4gb3V0IG9mIGlkZWFzIGhlcmUu</totp>
515</next>"#
516 .parse()
517 .unwrap();
518
519 let next = Next::try_from(elem).unwrap();
520 assert_eq!(next.task, "TOTP-EXAMPLE");
521
522 let payload = next.payloads.into_iter().next().unwrap();
523 assert!(payload.is("totp", "urn:totp:example"));
524 assert_eq!(&payload.text(), "SSd2ZSBydW4gb3V0IG9mIGlkZWFzIGhlcmUu");
525
526 let elem: Element = r#"<task-data xmlns='urn:xmpp:sasl:2'>
527 <totp xmlns="urn:totp:example">94d27acffa2e99a42ba7786162a9e73e7ab17b9d</totp>
528</task-data>"#
529 .parse()
530 .unwrap();
531
532 let task_data = TaskData::try_from(elem).unwrap();
533 let payload = task_data.payloads.into_iter().next().unwrap();
534 assert!(payload.is("totp", "urn:totp:example"));
535 assert_eq!(&payload.text(), "94d27acffa2e99a42ba7786162a9e73e7ab17b9d");
536
537 let elem: Element = r#"<task-data xmlns='urn:xmpp:sasl:2'>
538 <totp xmlns="urn:totp:example">OTRkMjdhY2ZmYTJlOTlhNDJiYTc3ODYxNjJhOWU3M2U3YWIxN2I5ZAo=</totp>
539</task-data>"#
540 .parse()
541 .unwrap();
542
543 let task_data = TaskData::try_from(elem).unwrap();
544 let payload = task_data.payloads.into_iter().next().unwrap();
545 assert!(payload.is("totp", "urn:totp:example"));
546 assert_eq!(
547 &payload.text(),
548 "OTRkMjdhY2ZmYTJlOTlhNDJiYTc3ODYxNjJhOWU3M2U3YWIxN2I5ZAo="
549 );
550
551 let elem: Element = r#"<success xmlns='urn:xmpp:sasl:2'>
552 <totp xmlns="urn:totp:example">SGFkIHlvdSBnb2luZywgdGhlcmUsIGRpZG4ndCBJPw==</totp>
553 <authorization-identifier>juliet@montague.example</authorization-identifier>
554</success>"#
555 .parse()
556 .unwrap();
557
558 let success = Success::try_from(elem).unwrap();
559 assert_eq!(success.additional_data, None);
560
561 let payload = success.payloads.into_iter().next().unwrap();
562 assert!(payload.is("totp", "urn:totp:example"));
563 assert_eq!(
564 &payload.text(),
565 "SGFkIHlvdSBnb2luZywgdGhlcmUsIGRpZG4ndCBJPw=="
566 );
567
568 assert_eq!(
569 success.authorization_identifier,
570 Jid::new("juliet@montague.example").unwrap(),
571 )
572 }
573
574 #[test]
576 fn example_13() {
577 let elem: Element = r#"<authenticate xmlns='urn:xmpp:sasl:2' mechanism='PLAIN'>
578 <initial-response>AGFsaWNlQGV4YW1wbGUub3JnCjM0NQ==</initial-response>
579 <user-agent id='d4565fa7-4d72-4749-b3d3-740edbf87770'>
580 <software>AwesomeXMPP</software>
581 <device>Kiva's Phone</device>
582 </user-agent>
583</authenticate>"#
584 .parse()
585 .unwrap();
586
587 let auth = Authenticate::try_from(elem).unwrap();
588
589 assert_eq!(auth.mechanism, "PLAIN");
590 assert_eq!(
591 auth.initial_response.unwrap(),
592 BASE64_STANDARD
593 .decode("AGFsaWNlQGV4YW1wbGUub3JnCjM0NQ==")
594 .unwrap()
595 );
596
597 assert_eq!(auth.payloads.len(), 0);
598
599 let user_agent = auth.user_agent;
600 assert_eq!(
601 user_agent.id,
602 "d4565fa7-4d72-4749-b3d3-740edbf87770"
603 .parse::<Uuid>()
604 .unwrap()
605 );
606 assert_eq!(user_agent.software.as_deref(), Some("AwesomeXMPP"));
607 assert_eq!(user_agent.device.as_deref(), Some("Kiva's Phone"));
608
609 let elem: Element = r#"<success xmlns='urn:xmpp:sasl:2'>
610 <authorization-identifier>alice@example.org</authorization-identifier>
611</success>"#
612 .parse()
613 .unwrap();
614
615 let success = Success::try_from(elem).unwrap();
616 assert_eq!(
617 success.authorization_identifier,
618 Jid::new("alice@example.org").unwrap()
619 );
620 assert_eq!(success.additional_data, None);
621 assert_eq!(success.payloads.len(), 0);
622 }
623
624 #[test]
626 fn example_14() {
627 let elem: Element = r#"<authenticate xmlns='urn:xmpp:sasl:2' mechanism='CRAM-MD5'>
628 <user-agent id='d4565fa7-4d72-4749-b3d3-740edbf87770'>
629 <software>AwesomeXMPP</software>
630 <device>Kiva's Phone</device>
631 </user-agent>
632</authenticate>"#
633 .parse()
634 .unwrap();
635
636 let auth = Authenticate::try_from(elem).unwrap();
637
638 assert_eq!(auth.mechanism, "CRAM-MD5");
639 assert_eq!(auth.initial_response, None);
640 assert_eq!(auth.payloads.len(), 0);
641
642 let user_agent = auth.user_agent;
643 assert_eq!(
644 user_agent.id,
645 "d4565fa7-4d72-4749-b3d3-740edbf87770"
646 .parse::<Uuid>()
647 .unwrap()
648 );
649 assert_eq!(user_agent.software.as_deref(), Some("AwesomeXMPP"));
650 assert_eq!(user_agent.device.as_deref(), Some("Kiva's Phone"));
651
652 let elem: Element = r#"<challenge xmlns='urn:xmpp:sasl:2'>PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2UucmVzdG9uLm1jaS5uZXQ+</challenge>"#
653 .parse()
654 .unwrap();
655
656 let challenge = Challenge::try_from(elem).unwrap();
657 assert_eq!(
658 challenge.sasl_data,
659 BASE64_STANDARD
660 .decode("PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2UucmVzdG9uLm1jaS5uZXQ+")
661 .unwrap()
662 );
663
664 let elem: Element = r#"<response xmlns='urn:xmpp:sasl:2'>dGltIGI5MTNhNjAyYzdlZGE3YTQ5NWI0ZTZlNzMzNGQzODkw</response>"#
665 .parse()
666 .unwrap();
667
668 let response = Response::try_from(elem).unwrap();
669 assert_eq!(
670 response.sasl_data,
671 BASE64_STANDARD
672 .decode("dGltIGI5MTNhNjAyYzdlZGE3YTQ5NWI0ZTZlNzMzNGQzODkw")
673 .unwrap()
674 );
675
676 let elem: Element = r#"<success xmlns='urn:xmpp:sasl:2'>
677 <authorization-identifier>tim@example.org</authorization-identifier>
678</success>
679 "#
680 .parse()
681 .unwrap();
682
683 let success = Success::try_from(elem).unwrap();
684 assert_eq!(
685 success.authorization_identifier,
686 Jid::new("tim@example.org").unwrap()
687 );
688 assert_eq!(success.additional_data, None);
689 assert_eq!(success.payloads.len(), 0);
690 }
691
692 #[test]
694 fn example_15() {
695 let elem: Element = r#"<authenticate xmlns='urn:xmpp:sasl:2' mechanism='BLURDYBLOOP'>
696 <initial-response>SW5pdGlhbCBSZXNwb25zZQ==</initial-response>
697 <user-agent id='d4565fa7-4d72-4749-b3d3-740edbf87770'>
698 <software>AwesomeXMPP</software>
699 <device>Kiva's Phone</device>
700 </user-agent>
701 <megabind xmlns='urn:example:megabind'>
702 <resource>this-one-please</resource>
703 </megabind>
704</authenticate>"#
705 .parse()
706 .unwrap();
707
708 let auth = Authenticate::try_from(elem).unwrap();
709 assert_eq!(auth.mechanism, "BLURDYBLOOP");
710 assert_eq!(
711 auth.initial_response,
712 Some(BASE64_STANDARD.decode("SW5pdGlhbCBSZXNwb25zZQ==").unwrap())
713 );
714
715 assert_eq!(
716 auth.user_agent.id,
717 "d4565fa7-4d72-4749-b3d3-740edbf87770"
718 .parse::<Uuid>()
719 .unwrap()
720 );
721 assert_eq!(auth.user_agent.software.as_deref(), Some("AwesomeXMPP"));
722 assert_eq!(auth.user_agent.device.as_deref(), Some("Kiva's Phone"));
723
724 assert_eq!(auth.payloads.len(), 1);
725 let bind = auth.payloads.into_iter().next().unwrap();
726 assert!(bind.is("megabind", "urn:example:megabind"));
727
728 let mut bind_payloads = bind.children();
729 let resource = bind_payloads.next().unwrap();
730 assert_eq!(resource.name(), "resource");
731 assert_eq!(&resource.text(), "this-one-please");
732 assert_eq!(bind_payloads.next(), None);
733
734 let elem: Element = r#"<challenge xmlns='urn:xmpp:sasl:2'>PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2UucmVzdG9uLm1jaS5uZXQ+</challenge>"#
735 .parse()
736 .unwrap();
737 let challenge = Challenge::try_from(elem).unwrap();
738 assert_eq!(
739 challenge.sasl_data,
740 BASE64_STANDARD
741 .decode("PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2UucmVzdG9uLm1jaS5uZXQ+")
742 .unwrap()
743 );
744
745 let elem: Element = r#"<response xmlns='urn:xmpp:sasl:2'>dGltIGI5MTNhNjAyYzdlZGE3YTQ5NWI0ZTZlNzMzNGQzODkw</response>"#
746 .parse()
747 .unwrap();
748 let response = Response::try_from(elem).unwrap();
749 assert_eq!(response.sasl_data, b"tim b913a602c7eda7a495b4e6e7334d3890");
750
751 let elem: Element = r#"<continue xmlns='urn:xmpp:sasl:2'>
752 <additional-data>QWRkaXRpb25hbCBEYXRh</additional-data>
753 <tasks>
754 <task>UNREALISTIC-2FA</task>
755 </tasks>
756</continue>"#
757 .parse()
758 .unwrap();
759 let cont = Continue::try_from(elem).unwrap();
760 assert_eq!(
761 cont.additional_data,
762 BASE64_STANDARD.decode("QWRkaXRpb25hbCBEYXRh").unwrap()
763 );
764 assert_eq!(cont.tasks.len(), 1);
765 assert_eq!(cont.tasks.into_iter().next().unwrap(), "UNREALISTIC-2FA");
766
767 let elem: Element = r#"<next xmlns='urn:xmpp:sasl:2' task='UNREALISTIC-2FA'>
768 <parameters xmlns='urn:example:unrealistic2fa'>VW5yZWFsaXN0aWMgMkZBIElS</parameters>
769</next>"#
770 .parse()
771 .unwrap();
772 let next = Next::try_from(elem).unwrap();
773 assert_eq!(next.payloads.len(), 1);
774 let params = next.payloads.into_iter().next().unwrap();
775 assert!(params.is("parameters", "urn:example:unrealistic2fa"));
776 assert_eq!(¶ms.text(), "VW5yZWFsaXN0aWMgMkZBIElS");
777
778 let elem: Element = r#"<task-data xmlns='urn:xmpp:sasl:2'>
779 <question xmlns='urn:example:unrealistic2fa'>PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2UucmVzdG9uLm1jaS5uZXQ+</question>
780</task-data>"#
781 .parse()
782 .unwrap();
783 let task_data = TaskData::try_from(elem).unwrap();
784 assert_eq!(task_data.payloads.len(), 1);
785 let question = task_data.payloads.into_iter().next().unwrap();
786 assert!(question.is("question", "urn:example:unrealistic2fa"));
787 assert_eq!(
788 &question.text(),
789 "PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2UucmVzdG9uLm1jaS5uZXQ+"
790 );
791
792 let elem: Element = r#"<task-data xmlns='urn:xmpp:sasl:2'>
793 <response xmlns='urn:example:unrealistic2fa'>dGltIGI5MTNhNjAyYzdlZGE3YTQ5NWI0ZTZlNzMzNGQzODkw</response>
794</task-data>"#
795 .parse()
796 .unwrap();
797 let task_data = TaskData::try_from(elem).unwrap();
798 assert_eq!(task_data.payloads.len(), 1);
799 let response = task_data.payloads.into_iter().next().unwrap();
800 assert!(response.is("response", "urn:example:unrealistic2fa"));
801 assert_eq!(
802 &response.text(),
803 "dGltIGI5MTNhNjAyYzdlZGE3YTQ5NWI0ZTZlNzMzNGQzODkw"
804 );
805
806 let elem: Element = r#"<success xmlns='urn:xmpp:sasl:2'>
807 <result xmlns='urn:example:unrealistic2fa'>VW5yZWFsaXN0aWMgMkZBIG11dHVhbCBhdXRoIGRhdGE=</result>
808 <authorization-identifier>alice@example.org/this-one-please</authorization-identifier>
809</success>"#
810 .parse()
811 .unwrap();
812 let success = Success::try_from(elem).unwrap();
813 assert_eq!(
814 success.authorization_identifier,
815 Jid::new("alice@example.org/this-one-please").unwrap()
816 );
817
818 assert_eq!(success.payloads.len(), 1);
819 let res = success.payloads.into_iter().next().unwrap();
820 assert!(res.is("result", "urn:example:unrealistic2fa"));
821 assert_eq!(&res.text(), "VW5yZWFsaXN0aWMgMkZBIG11dHVhbCBhdXRoIGRhdGE=");
822 }
823}