1pub use hessra_token_authz::{
44 add_multi_party_attestation,
46 add_multi_party_attestation_to_token,
47 add_service_node_attestation,
48 biscuit_key_from_string,
50 create_biscuit,
52 create_multi_party_biscuit,
53 create_multi_party_biscuit_with_time,
54 create_multi_party_token,
55 create_multi_party_token_with_time,
56 create_raw_multi_party_biscuit,
57 create_service_chain_biscuit,
58 create_service_chain_token,
59 create_service_chain_token_with_time,
60 create_token,
61 create_token_with_time,
62 verify_biscuit_local,
63 verify_service_chain_biscuit_local,
64 verify_service_chain_token_local,
65 verify_token_local,
66 ServiceNode,
67};
68
69pub use hessra_token_core::{
71 decode_token, encode_token, parse_token, public_key_from_pem_file, Biscuit, KeyPair, PublicKey,
72 TokenError, TokenTimeConfig,
73};
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78 use biscuit_auth::macros::biscuit;
79 use serde_json::Value;
80 use std::fs;
81
82 #[test]
83 fn test_verify_biscuit_local() {
84 let keypair = KeyPair::new();
86 let public_key = keypair.public();
87
88 let biscuit_builder = biscuit!(
90 r#"
91 right("alice", "resource1", "read");
92 "#
93 );
94 let biscuit = biscuit_builder.build(&keypair).unwrap();
95 let token_bytes = biscuit.to_vec().unwrap();
96
97 let result = verify_biscuit_local(
99 token_bytes,
100 public_key,
101 "alice".to_string(),
102 "resource1".to_string(),
103 "read".to_string(),
104 );
105 assert!(result.is_ok());
106 }
107
108 #[test]
109 fn test_verify_service_chain_biscuit() {
110 let root_keypair = KeyPair::new();
112 let service_keypair = KeyPair::new();
113 let service_public_key_hex = hex::encode(service_keypair.public().to_bytes());
114 let service_public_key_str = format!("ed25519/{}", service_public_key_hex);
115
116 let biscuit_builder = biscuit!(
118 r#"
119 right("alice", "resource1", "write");
120 node("resource1", "service1");
121 "#
122 );
123 let biscuit = biscuit_builder.build(&root_keypair).unwrap();
124 let token_bytes = biscuit.to_vec().unwrap();
125
126 let service_nodes = vec![ServiceNode {
128 component: "service1".to_string(),
129 public_key: service_public_key_str,
130 }];
131
132 let result = verify_service_chain_biscuit_local(
134 token_bytes,
135 root_keypair.public(),
136 "alice".to_string(),
137 "resource1".to_string(),
138 "write".to_string(),
139 service_nodes,
140 None,
141 );
142 assert!(result.is_ok());
143 }
144
145 #[test]
146 fn test_add_service_node_attestation() {
147 let root_keypair = KeyPair::new();
149 let service_keypair = KeyPair::new();
150
151 let biscuit_builder = biscuit!(
153 r#"
154 right("alice", "resource1", "read");
155 right("alice", "resource1", "write");
156 "#
157 );
158 let biscuit = biscuit_builder.build(&root_keypair).unwrap();
159 let token_bytes = biscuit.to_vec().unwrap();
160
161 let attested_token = add_service_node_attestation(
163 token_bytes,
164 root_keypair.public(),
165 "resource1",
166 &service_keypair,
167 );
168 assert!(attested_token.is_ok());
169
170 let result = verify_biscuit_local(
172 attested_token.unwrap(),
173 root_keypair.public(),
174 "alice".to_string(),
175 "resource1".to_string(),
176 "read".to_string(),
177 );
178 assert!(result.is_ok());
179 }
180
181 #[test]
182 fn test_base64_utils() {
183 let keypair = KeyPair::new();
185 let biscuit_builder = biscuit!(
186 r#"
187 right("alice", "resource1", "read");
188 "#
189 );
190 let biscuit = biscuit_builder.build(&keypair).unwrap();
191 let original_bytes = biscuit.to_vec().unwrap();
192
193 let encoded = encode_token(&original_bytes);
195 assert!(!encoded.is_empty());
196
197 let decoded = decode_token(&encoded).unwrap();
199 assert_eq!(original_bytes, decoded);
200
201 let result = decode_token("invalid-base64!");
203 assert!(result.is_err());
204 }
205
206 #[test]
207 fn test_verify_token_string() {
208 let keypair = KeyPair::new();
210 let biscuit_builder = biscuit!(
211 r#"
212 right("alice", "resource1", "read");
213 right("alice", "resource1", "write");
214 check if subject($sub), resource($res), operation($op), right($sub, $res, $op);
215 "#
216 );
217 let biscuit = biscuit_builder.build(&keypair).unwrap();
218 let token_bytes = biscuit.to_vec().unwrap();
219 let token_string = encode_token(&token_bytes);
220
221 let result = verify_token_local(
223 &token_string,
224 keypair.public(),
225 "alice",
226 "resource1",
227 "read",
228 );
229 assert!(result.is_ok());
230
231 let result =
233 verify_token_local(&token_string, keypair.public(), "bob", "resource1", "read");
234 assert!(result.is_err());
235 }
236
237 #[test]
238 fn test_token_verification_from_json() {
239 let json_data =
241 fs::read_to_string("tests/test_tokens.json").expect("Failed to read test_tokens.json");
242 let tokens: Value =
243 serde_json::from_str(&json_data).expect("Failed to parse test_tokens.json");
244
245 let public_key = public_key_from_pem_file("tests/hessra_key.pem")
247 .expect("Failed to load test public key");
248
249 for token_value in tokens["tokens"].as_array().unwrap() {
251 let name = token_value["name"].as_str().unwrap();
252 let token_string = token_value["token"].as_str().unwrap();
253 let metadata = &token_value["metadata"];
254
255 let subject = metadata["subject"].as_str().unwrap();
257 let resource = metadata["resource"].as_str().unwrap();
258 let expected_result = metadata["expected_result"].as_bool().unwrap();
259 let description = metadata["description"].as_str().unwrap_or("No description");
260
261 println!("Testing token '{}': {}", name, description);
262
263 let result = parse_token(token_string, public_key).and_then(|biscuit| {
265 println!("Token blocks: {}", biscuit.print());
267
268 if metadata["type"].as_str().unwrap() == "singleton" {
269 verify_token_local(token_string, public_key, subject, resource, "read")
270 } else {
271 let service_nodes = vec![
273 ServiceNode {
274 component: "auth_service".to_string(),
275 public_key: "ed25519/0123456789abcdef0123456789abcdef".to_string(),
276 },
277 ServiceNode {
278 component: "payment_service".to_string(),
279 public_key: "ed25519/fedcba9876543210fedcba9876543210".to_string(),
280 },
281 ];
282
283 verify_service_chain_token_local(
284 token_string,
285 public_key,
286 subject,
287 resource,
288 "read",
289 service_nodes,
290 None,
291 )
292 }
293 });
294
295 let verification_succeeded = result.is_ok();
297 assert_eq!(
298 verification_succeeded, expected_result,
299 "Token '{}' verification resulted in {}, expected: {} - {}",
300 name, verification_succeeded, expected_result, description
301 );
302
303 println!(
304 "✓ Token '{}' - Verification: {}",
305 name,
306 if verification_succeeded == expected_result {
307 "PASSED"
308 } else {
309 "FAILED"
310 }
311 );
312 }
313 }
314
315 #[test]
316 fn test_service_chain_tokens_from_json() {
317 let json_data =
319 fs::read_to_string("tests/test_tokens.json").expect("Failed to read test_tokens.json");
320 let tokens: Value =
321 serde_json::from_str(&json_data).expect("Failed to parse test_tokens.json");
322
323 let public_key = public_key_from_pem_file("tests/hessra_key.pem")
325 .expect("Failed to load test public key");
326
327 if let Some(tokens_array) = tokens["tokens"].as_array() {
329 if let Some(order_service_token) = tokens_array
330 .iter()
331 .find(|t| t["name"].as_str().unwrap() == "argo-cli1_access_order_service")
332 {
333 let token_string = order_service_token["token"].as_str().unwrap();
334 let subject = order_service_token["metadata"]["subject"].as_str().unwrap();
335 let resource = order_service_token["metadata"]["resource"]
336 .as_str()
337 .unwrap();
338 let expected_result = order_service_token["metadata"]["expected_result"]
339 .as_bool()
340 .unwrap();
341
342 let service_nodes = vec![
344 ServiceNode {
345 component: "auth_service".to_string(),
346 public_key: "ed25519/0123456789abcdef0123456789abcdef".to_string(),
347 },
348 ServiceNode {
349 component: "payment_service".to_string(),
350 public_key: "ed25519/fedcba9876543210fedcba9876543210".to_string(),
351 },
352 ];
353
354 let result = verify_service_chain_token_local(
356 token_string,
357 public_key,
358 subject,
359 resource,
360 "read",
361 service_nodes,
362 None,
363 );
364
365 assert_eq!(
367 result.is_ok(),
368 expected_result,
369 "Service chain verification for '{}' resulted in {}, expected: {}",
370 order_service_token["name"].as_str().unwrap(),
371 result.is_ok(),
372 expected_result
373 );
374 }
375 }
376 }
377
378 #[test]
379 fn test_service_chain_lifecycle() {
380 let json_data = fs::read_to_string("tests/service_chain_tokens.json")
382 .expect("Failed to read service_chain_tokens.json");
383 let tokens: Value =
384 serde_json::from_str(&json_data).expect("Failed to parse service_chain_tokens.json");
385
386 let initial_token = tokens["tokens"][0]["token"].as_str().unwrap();
388 let token_after_auth = tokens["tokens"][1]["token"].as_str().unwrap();
389 let token_after_payment = tokens["tokens"][2]["token"].as_str().unwrap();
390 let final_token = tokens["tokens"][3]["token"].as_str().unwrap();
391
392 let subject = "uri:urn:test:argo-cli1";
394 let resource = "order_service";
395
396 let root_public_key = public_key_from_pem_file("tests/hessra_key.pem")
398 .expect("Failed to load test public key");
399
400 let auth_service_pk_str = tokens["tokens"][1]["metadata"]["service_nodes"][0]["public_key"]
402 .as_str()
403 .unwrap();
404 let payment_service_pk_str = tokens["tokens"][2]["metadata"]["service_nodes"][1]
405 ["public_key"]
406 .as_str()
407 .unwrap();
408 let order_service_pk_str = tokens["tokens"][3]["metadata"]["service_nodes"][2]
409 ["public_key"]
410 .as_str()
411 .unwrap();
412
413 let result = verify_token_local(initial_token, root_public_key, subject, resource, "read");
418 assert!(result.is_ok(), "Initial token verification failed");
419
420 let service_nodes_for_payment = vec![ServiceNode {
422 component: "auth_service".to_string(),
423 public_key: auth_service_pk_str.to_string(),
424 }];
425
426 let result = verify_service_chain_token_local(
427 token_after_auth,
428 root_public_key,
429 subject,
430 resource,
431 "read",
432 service_nodes_for_payment.clone(),
433 None,
434 );
435 assert!(
436 result.is_ok(),
437 "Token with auth attestation verification failed"
438 );
439
440 let service_nodes_for_order = vec![
442 ServiceNode {
443 component: "auth_service".to_string(),
444 public_key: auth_service_pk_str.to_string(),
445 },
446 ServiceNode {
447 component: "payment_service".to_string(),
448 public_key: payment_service_pk_str.to_string(),
449 },
450 ];
451
452 let result = verify_service_chain_token_local(
453 token_after_payment,
454 root_public_key,
455 subject,
456 resource,
457 "read",
458 service_nodes_for_order.clone(),
459 None,
460 );
461 assert!(
462 result.is_ok(),
463 "Token with payment attestation verification failed"
464 );
465
466 let service_nodes_complete = vec![
468 ServiceNode {
469 component: "auth_service".to_string(),
470 public_key: auth_service_pk_str.to_string(),
471 },
472 ServiceNode {
473 component: "payment_service".to_string(),
474 public_key: payment_service_pk_str.to_string(),
475 },
476 ServiceNode {
477 component: "order_service".to_string(),
478 public_key: order_service_pk_str.to_string(),
479 },
480 ];
481
482 let result = verify_service_chain_token_local(
483 final_token,
484 root_public_key,
485 subject,
486 resource,
487 "read",
488 service_nodes_complete.clone(),
489 None,
490 );
491
492 if result.is_err() {
494 println!("Error details: {:?}", result);
495
496 let decoded_final = decode_token(final_token).unwrap();
498 if let Ok(biscuit) = Biscuit::from(&decoded_final, root_public_key) {
499 println!("Token blocks: {}", biscuit.print());
500 } else {
501 println!("Failed to parse token");
502 }
503 }
504
505 assert!(result.is_ok(), "Final token verification failed");
506
507 let result = verify_service_chain_token_local(
509 token_after_auth,
510 root_public_key,
511 subject,
512 resource,
513 "read",
514 service_nodes_complete,
515 None,
516 );
517 assert!(
519 result.is_err(),
520 "Incomplete service chain should be rejected"
521 );
522 }
523
524 #[test]
525 fn test_multi_party_token_verification_lifecycle() {
526 let subject = "test@test.com".to_owned();
527 let resource = "res1".to_string();
528 let operation = "read".to_string();
529 let root = KeyPair::new();
530 let public_key = root.public();
531
532 let approval_service_key = KeyPair::new();
534 let approval_service_public_key = hex::encode(approval_service_key.public().to_bytes());
535 let approval_service_public_key = format!("ed25519/{}", approval_service_public_key);
536 let approval_service_node = ServiceNode {
537 component: "approval_service".to_string(),
538 public_key: approval_service_public_key.clone(),
539 };
540 let nodes = vec![approval_service_node];
541
542 let token = create_multi_party_biscuit(
544 subject.clone(),
545 resource.clone(),
546 operation.clone(),
547 root,
548 &nodes,
549 );
550 assert!(token.is_ok(), "Failed to create multi-party token");
551 let token = token.unwrap();
552 let token_string = encode_token(&token);
553
554 println!("✓ Multi-party token created successfully");
555
556 let result = verify_token_local(&token_string, public_key, &subject, &resource, &operation);
558 assert!(
559 result.is_err(),
560 "Multi-party token should fail verification without attestation"
561 );
562 println!("✓ Unattested multi-party token correctly failed verification");
563
564 let attested_token = add_multi_party_attestation(
566 token,
567 public_key,
568 "approval_service".to_string(),
569 approval_service_key,
570 );
571 assert!(
572 attested_token.is_ok(),
573 "Failed to add multi-party attestation"
574 );
575 let attested_token = attested_token.unwrap();
576 let attested_token_string = encode_token(&attested_token);
577
578 println!("✓ Multi-party attestation added successfully");
579
580 let result = verify_token_local(
582 &attested_token_string,
583 public_key,
584 &subject,
585 &resource,
586 &operation,
587 );
588 assert!(
589 result.is_ok(),
590 "Attested multi-party token should pass verification"
591 );
592 println!("✓ Attested multi-party token correctly passed verification");
593
594 let wrong_service_key = KeyPair::new();
596 let wrong_attested_token = add_multi_party_attestation(
597 decode_token(&token_string).unwrap(),
598 public_key,
599 "wrong_service".to_string(),
600 wrong_service_key,
601 );
602 assert!(wrong_attested_token.is_ok(), "Attestation should succeed");
603 let wrong_attested_token_string = encode_token(&wrong_attested_token.unwrap());
604
605 let result = verify_token_local(
606 &wrong_attested_token_string,
607 public_key,
608 &subject,
609 &resource,
610 &operation,
611 );
612 assert!(
613 result.is_err(),
614 "Token attested by wrong namespace should fail verification"
615 );
616 println!("✓ Token attested by wrong namespace correctly failed verification");
617 }
618
619 #[test]
620 fn test_multi_party_token_with_multiple_parties() {
621 let subject = "test@test.com".to_owned();
622 let resource = "sensitive_resource".to_string();
623 let operation = "admin".to_string();
624 let root = KeyPair::new();
625 let public_key = root.public();
626
627 let legal_dept_key = KeyPair::new();
629 let legal_dept_public_key = hex::encode(legal_dept_key.public().to_bytes());
630 let legal_dept_public_key = format!("ed25519/{}", legal_dept_public_key);
631 let legal_dept_node = ServiceNode {
632 component: "legal_dept".to_string(),
633 public_key: legal_dept_public_key.clone(),
634 };
635
636 let security_team_key = KeyPair::new();
637 let security_team_public_key = hex::encode(security_team_key.public().to_bytes());
638 let security_team_public_key = format!("ed25519/{}", security_team_public_key);
639 let security_team_node = ServiceNode {
640 component: "security_team".to_string(),
641 public_key: security_team_public_key.clone(),
642 };
643
644 let nodes = vec![legal_dept_node, security_team_node];
645
646 let token = create_multi_party_biscuit(
648 subject.clone(),
649 resource.clone(),
650 operation.clone(),
651 root,
652 &nodes,
653 );
654 assert!(token.is_ok(), "Failed to create multi-party token");
655 let token = token.unwrap();
656 let token_string = encode_token(&token);
657
658 let result = verify_token_local(&token_string, public_key, &subject, &resource, &operation);
660 assert!(result.is_err(), "Token should fail without attestations");
661
662 let partially_attested_token = add_multi_party_attestation(
664 decode_token(&token_string).unwrap(),
665 public_key,
666 "legal_dept".to_string(),
667 legal_dept_key,
668 )
669 .unwrap();
670 let partially_attested_token_string = encode_token(&partially_attested_token);
671
672 let result = verify_token_local(
674 &partially_attested_token_string,
675 public_key,
676 &subject,
677 &resource,
678 &operation,
679 );
680 assert!(
681 result.is_err(),
682 "Token should fail with only one of two required attestations"
683 );
684
685 let fully_attested_token = add_multi_party_attestation(
687 partially_attested_token,
688 public_key,
689 "security_team".to_string(),
690 security_team_key,
691 )
692 .unwrap();
693 let fully_attested_token_string = encode_token(&fully_attested_token);
694
695 let result = verify_token_local(
697 &fully_attested_token_string,
698 public_key,
699 &subject,
700 &resource,
701 &operation,
702 );
703 assert!(
704 result.is_ok(),
705 "Token should pass with both required attestations"
706 );
707 println!("✓ Multi-party token with multiple parties verified successfully");
708 }
709}