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