1extern crate biscuit_auth as biscuit;
2
3use crate::verify::{biscuit_key_from_string, ServiceNode};
4
5use biscuit::macros::{biscuit, rule};
6use biscuit::{Biscuit, BiscuitBuilder, KeyPair};
7use chrono::Utc;
8use std::error::Error;
9use tracing::info;
10
11#[derive(Debug, Clone, Copy)]
16pub struct TokenTimeConfig {
17 pub start_time: Option<i64>,
19 pub duration: i64,
21}
22
23impl Default for TokenTimeConfig {
24 fn default() -> Self {
25 Self {
26 start_time: None,
27 duration: 300, }
29 }
30}
31
32fn _create_base_biscuit_builder(
33 subject: String,
34 resource: String,
35 operation: String,
36) -> Result<BiscuitBuilder, Box<dyn Error>> {
37 create_base_biscuit_builder_with_time(subject, resource, operation, TokenTimeConfig::default())
38}
39
40fn create_base_biscuit_builder_with_time(
41 subject: String,
42 resource: String,
43 operation: String,
44 time_config: TokenTimeConfig,
45) -> Result<BiscuitBuilder, Box<dyn Error>> {
46 let start_time = time_config
47 .start_time
48 .unwrap_or_else(|| Utc::now().timestamp());
49 let expiration = start_time + time_config.duration;
50
51 let biscuit_builder = biscuit!(
52 r#"
53 right({subject}, {resource}, {operation});
54 check if time($time), $time < {expiration};
55 "#
56 );
57
58 Ok(biscuit_builder)
59}
60
61pub fn create_raw_biscuit(
62 subject: String,
63 resource: String,
64 operation: String,
65 key: KeyPair,
66 time_config: TokenTimeConfig,
67) -> Result<Biscuit, Box<dyn Error>> {
68 let biscuit = create_base_biscuit_builder_with_time(subject, resource, operation, time_config)?
69 .build(&key)?;
70
71 info!("biscuit (authority): {}", biscuit);
72
73 Ok(biscuit)
74}
75
76pub fn create_biscuit(
94 subject: String,
95 resource: String,
96 operation: String,
97 key: KeyPair,
98 time_config: TokenTimeConfig,
99) -> Result<Vec<u8>, Box<dyn Error>> {
100 let biscuit = create_raw_biscuit(subject, resource, operation, key, time_config)?;
101 let token = biscuit.to_vec()?;
102 Ok(token)
103}
104
105fn create_base64_biscuit(
106 subject: String,
107 resource: String,
108 operation: String,
109 key: KeyPair,
110 time_config: TokenTimeConfig,
111) -> Result<String, Box<dyn Error>> {
112 let biscuit = create_raw_biscuit(subject, resource, operation, key, time_config)?;
113 let token = biscuit.to_base64()?;
114 Ok(token)
115}
116
117pub fn create_token(
118 subject: String,
119 resource: String,
120 operation: String,
121 key: KeyPair,
122) -> Result<String, Box<dyn Error>> {
123 create_base64_biscuit(
124 subject,
125 resource,
126 operation,
127 key,
128 TokenTimeConfig::default(),
129 )
130}
131
132pub fn create_token_with_time(
133 subject: String,
134 resource: String,
135 operation: String,
136 key: KeyPair,
137 time_config: TokenTimeConfig,
138) -> Result<String, Box<dyn Error>> {
139 create_base64_biscuit(subject, resource, operation, key, time_config)
140}
141
142pub fn create_service_chain_biscuit(
161 subject: String,
162 resource: String,
163 operation: String,
164 key: KeyPair,
165 nodes: &Vec<ServiceNode>,
166 time_config: TokenTimeConfig,
167) -> Result<Vec<u8>, Box<dyn Error>> {
168 let biscuit =
169 create_raw_service_chain_biscuit(subject, resource, operation, key, nodes, time_config)?;
170 let token = biscuit.to_vec()?;
171 Ok(token)
172}
173
174fn create_base64_service_chain_biscuit(
175 subject: String,
176 resource: String,
177 operation: String,
178 key: KeyPair,
179 nodes: &Vec<ServiceNode>,
180 time_config: TokenTimeConfig,
181) -> Result<String, Box<dyn Error>> {
182 let biscuit =
183 create_raw_service_chain_biscuit(subject, resource, operation, key, nodes, time_config)?;
184 let token = biscuit.to_base64()?;
185 Ok(token)
186}
187
188pub fn create_raw_service_chain_biscuit(
189 subject: String,
190 resource: String,
191 operation: String,
192 key: KeyPair,
193 nodes: &Vec<ServiceNode>,
194 time_config: TokenTimeConfig,
195) -> Result<Biscuit, Box<dyn Error>> {
196 create_service_chain_biscuit_with_time(subject, resource, operation, key, nodes, time_config)
197}
198
199pub fn create_service_chain_token(
200 subject: String,
201 resource: String,
202 operation: String,
203 key: KeyPair,
204 nodes: &Vec<ServiceNode>,
205) -> Result<String, Box<dyn Error>> {
206 create_base64_service_chain_biscuit(
207 subject,
208 resource,
209 operation,
210 key,
211 nodes,
212 TokenTimeConfig::default(),
213 )
214}
215
216pub fn create_service_chain_biscuit_with_time(
235 subject: String,
236 resource: String,
237 operation: String,
238 key: KeyPair,
239 nodes: &Vec<ServiceNode>,
240 time_config: TokenTimeConfig,
241) -> Result<Biscuit, Box<dyn Error>> {
242 let service = resource.clone();
243 let mut biscuit_builder =
244 create_base_biscuit_builder_with_time(subject, service, operation, time_config)?;
245
246 for node in nodes {
248 let component = node.component.clone();
249 let public_key = biscuit_key_from_string(node.public_key.clone())?;
250 biscuit_builder = biscuit_builder.rule(rule!(
251 r#"
252 node($s, {component}) <- service($s) trusting {public_key};
253 "#
254 ))?;
255 }
256
257 let biscuit = biscuit_builder.build(&key)?;
258
259 info!("biscuit (authority): {}", biscuit);
260
261 Ok(biscuit)
262}
263
264pub fn create_service_chain_token_with_time(
265 subject: String,
266 resource: String,
267 operation: String,
268 key: KeyPair,
269 nodes: &Vec<ServiceNode>,
270 time_config: TokenTimeConfig,
271) -> Result<String, Box<dyn Error>> {
272 let biscuit = create_service_chain_biscuit_with_time(
273 subject,
274 resource,
275 operation,
276 key,
277 nodes,
278 time_config,
279 )?;
280 let token = biscuit.to_base64()?;
281 Ok(token)
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287 use crate::verify::{verify_biscuit_local, verify_service_chain_biscuit_local};
288 use biscuit::macros::block;
289 use biscuit::Biscuit;
290 #[test]
291 fn test_create_biscuit() {
292 let subject = "test@test.com".to_owned();
293 let resource: String = "res1".to_string();
294 let operation = "read".to_string();
295 let root = KeyPair::new();
296 let public_key = root.public();
297 let token = create_biscuit(
298 subject.clone(),
299 resource.clone(),
300 operation.clone(),
301 root,
302 TokenTimeConfig::default(),
303 )
304 .unwrap();
305
306 let res = verify_biscuit_local(token, public_key, subject, resource, operation);
307 assert!(res.is_ok());
308 }
309
310 #[test]
311 fn test_biscuit_operations() {
312 let subject = "test@test.com".to_owned();
313 let resource = "res1".to_string();
314 let operation = "read".to_string();
315 let root = KeyPair::new();
316 let public_key = root.public();
317
318 let read_token = create_biscuit(
320 subject.clone(),
321 resource.clone(),
322 operation.clone(),
323 root,
324 TokenTimeConfig::default(),
325 )
326 .unwrap();
327
328 let res = verify_biscuit_local(
329 read_token.clone(),
330 public_key,
331 subject.clone(),
332 resource.clone(),
333 operation.clone(),
334 );
335 assert!(res.is_ok());
336
337 let root = KeyPair::new();
338 let public_key = root.public();
339
340 let write_token = create_biscuit(
342 subject.clone(),
343 resource.clone(),
344 "write".to_string(),
345 root,
346 TokenTimeConfig::default(),
347 )
348 .unwrap();
349
350 let res = verify_biscuit_local(
351 write_token.clone(),
352 public_key,
353 subject.clone(),
354 resource.clone(),
355 "write".to_string(),
356 );
357 assert!(res.is_ok());
358
359 let res = verify_biscuit_local(
361 read_token,
362 public_key,
363 subject.clone(),
364 resource.clone(),
365 "write".to_string(),
366 );
367 assert!(res.is_err());
368
369 let res = verify_biscuit_local(
371 write_token,
372 public_key,
373 subject.clone(),
374 resource.clone(),
375 "read".to_string(),
376 );
377 assert!(res.is_err());
378 }
379
380 #[test]
381 fn test_biscuit_expiration() {
382 let subject = "test@test.com".to_owned();
383 let resource = "res1".to_string();
384 let operation = "read".to_string();
385 let root = KeyPair::new();
386 let public_key = root.public();
387 let token = create_biscuit(
389 subject.clone(),
390 resource.clone(),
391 operation.clone(),
392 root,
393 TokenTimeConfig::default(),
394 )
395 .unwrap();
396
397 let res = verify_biscuit_local(
398 token.clone(),
399 public_key,
400 subject.clone(),
401 resource.clone(),
402 operation.clone(),
403 );
404 assert!(res.is_ok());
405
406 let root = KeyPair::new();
408 let token = create_biscuit(
409 subject.clone(),
410 resource.clone(),
411 operation.clone(),
412 root,
413 TokenTimeConfig {
414 start_time: Some(Utc::now().timestamp() - 301),
415 duration: 300,
416 },
417 )
418 .unwrap();
419 let res = verify_biscuit_local(token, public_key, subject, resource, operation);
420 assert!(res.is_err());
421 }
422
423 #[test]
424 fn test_custom_token_time_config() {
425 let subject = "test@test.com".to_owned();
426 let resource = "res1".to_string();
427 let operation = "read".to_string();
428 let root = KeyPair::new();
429 let public_key = root.public();
430
431 let past_time = Utc::now().timestamp() - 3600;
433 let time_config = TokenTimeConfig {
434 start_time: Some(past_time),
435 duration: 7200, };
437
438 let token = create_biscuit(
439 subject.clone(),
440 resource.clone(),
441 operation.clone(),
442 root,
443 time_config,
444 )
445 .unwrap();
446
447 let res = verify_biscuit_local(
449 token.clone(),
450 public_key,
451 subject.clone(),
452 resource.clone(),
453 operation.clone(),
454 );
455 assert!(res.is_ok());
456 }
457
458 #[test]
459 fn test_service_chain_biscuit() {
460 let subject = "test@test.com".to_owned();
461 let resource = "res1".to_string();
462 let operation = "read".to_string();
463 let root = KeyPair::new();
464 let public_key = root.public();
465 let chain_key = KeyPair::new();
466 let chain_public_key = hex::encode(chain_key.public().to_bytes());
467 let chain_public_key = format!("ed25519/{}", chain_public_key);
468 let chain_node = ServiceNode {
469 component: "edge_function".to_string(),
470 public_key: chain_public_key.clone(),
471 };
472 let nodes = vec![chain_node];
473 let token = create_service_chain_biscuit(
474 subject.clone(),
475 resource.clone(),
476 operation.clone(),
477 root,
478 &nodes,
479 TokenTimeConfig::default(),
480 );
481 if let Err(e) = &token {
482 println!("Error: {}", e);
483 }
484 assert!(token.is_ok());
485 let token = token.unwrap();
486 let res = verify_biscuit_local(
487 token.clone(),
488 public_key,
489 subject.clone(),
490 resource.clone(),
491 operation.clone(),
492 );
493 assert!(res.is_ok());
494 let biscuit = Biscuit::from(&token, public_key).unwrap();
495 let third_party_request = biscuit.third_party_request().unwrap();
496 let third_party_block = block!(
497 r#"
498 service("res1");
499 "#
500 );
501 let third_party_block = third_party_request
502 .create_block(&chain_key.private(), third_party_block)
503 .unwrap();
504 let attested_biscuit = biscuit
505 .append_third_party(chain_key.public(), third_party_block)
506 .unwrap();
507 let attested_token = attested_biscuit.to_vec().unwrap();
508 let res = verify_service_chain_biscuit_local(
509 attested_token,
510 public_key,
511 subject.clone(),
512 resource.clone(),
513 operation.clone(),
514 nodes,
515 None,
516 );
517 assert!(res.is_ok());
518 }
519
520 #[test]
521 fn test_service_chain_biscuit_with_component_name() {
522 let subject = "test@test.com".to_owned();
523 let resource = "res1".to_string();
524 let root = KeyPair::new();
525 let public_key = root.public();
526
527 let chain_key1 = KeyPair::new();
529 let chain_public_key1 = hex::encode(chain_key1.public().to_bytes());
530 let chain_public_key1 = format!("ed25519/{}", chain_public_key1);
531 let chain_node1 = ServiceNode {
532 component: "edge_function".to_string(),
533 public_key: chain_public_key1.clone(),
534 };
535
536 let chain_key2 = KeyPair::new();
537 let chain_public_key2 = hex::encode(chain_key2.public().to_bytes());
538 let chain_public_key2 = format!("ed25519/{}", chain_public_key2);
539 let chain_node2 = ServiceNode {
540 component: "middleware".to_string(),
541 public_key: chain_public_key2.clone(),
542 };
543
544 let nodes = vec![chain_node1.clone(), chain_node2.clone()];
545
546 let token = create_service_chain_biscuit(
548 subject.clone(),
549 resource.clone(),
550 "read".to_string(),
551 root,
552 &nodes,
553 TokenTimeConfig::default(),
554 );
555 assert!(token.is_ok());
556 let token = token.unwrap();
557
558 let biscuit = Biscuit::from(&token, public_key).unwrap();
560 let third_party_request = biscuit.third_party_request().unwrap();
561 let third_party_block = block!(
562 r#"
563 service("res1");
564 "#
565 );
566 let third_party_block = third_party_request
567 .create_block(&chain_key1.private(), third_party_block)
568 .unwrap();
569 let attested_biscuit = biscuit
570 .append_third_party(chain_key1.public(), third_party_block)
571 .unwrap();
572 let attested_token = attested_biscuit.to_vec().unwrap();
573
574 let res = verify_service_chain_biscuit_local(
578 attested_token.clone(),
579 public_key,
580 subject.clone(),
581 resource.clone(),
582 "read".to_string(),
583 nodes.clone(),
584 Some("edge_function".to_string()),
585 );
586 assert!(res.is_ok());
588
589 let nodes = vec![chain_node1.clone(), chain_node2.clone()];
591
592 let res = verify_service_chain_biscuit_local(
594 attested_token.clone(),
595 public_key,
596 subject.clone(),
597 resource.clone(),
598 "read".to_string(),
599 nodes.clone(),
600 Some("middleware".to_string()),
601 );
602 assert!(res.is_ok());
603 }
604
605 #[test]
606 fn test_service_chain_biscuit_with_nonexistent_component() {
607 let subject = "test@test.com".to_owned();
608 let resource = "res1".to_string();
609 let root = KeyPair::new();
610 let public_key = root.public();
611 let chain_key = KeyPair::new();
612 let chain_public_key = hex::encode(chain_key.public().to_bytes());
613 let chain_public_key = format!("ed25519/{}", chain_public_key);
614 let chain_node = ServiceNode {
615 component: "edge_function".to_string(),
616 public_key: chain_public_key.clone(),
617 };
618 let nodes = vec![chain_node];
619 let token = create_service_chain_biscuit(
620 subject.clone(),
621 resource.clone(),
622 "read".to_string(),
623 root,
624 &nodes,
625 TokenTimeConfig::default(),
626 );
627 assert!(token.is_ok());
628 let token = token.unwrap();
629
630 let biscuit = Biscuit::from(&token, public_key).unwrap();
631 let third_party_request = biscuit.third_party_request().unwrap();
632 let third_party_block = block!(
633 r#"
634 service("res1");
635 "#
636 );
637 let third_party_block = third_party_request
638 .create_block(&chain_key.private(), third_party_block)
639 .unwrap();
640 let attested_biscuit = biscuit
641 .append_third_party(chain_key.public(), third_party_block)
642 .unwrap();
643 let attested_token = attested_biscuit.to_vec().unwrap();
644
645 let res = verify_service_chain_biscuit_local(
647 attested_token,
648 public_key,
649 subject.clone(),
650 resource.clone(),
651 "read".to_string(),
652 nodes.clone(),
653 Some("nonexistent_component".to_string()),
654 );
655 assert!(res.is_err());
656
657 let err = res.unwrap_err().to_string();
659 assert!(err.contains("nonexistent_component"));
660 }
661
662 #[test]
663 fn test_service_chain_biscuit_with_multiple_nodes() {
664 let subject = "test@test.com".to_owned();
665 let resource = "res1".to_string();
666 let root = KeyPair::new();
667 let public_key = root.public();
668
669 let chain_key1 = KeyPair::new();
671 let chain_public_key1 = hex::encode(chain_key1.public().to_bytes());
672 let chain_public_key1 = format!("ed25519/{}", chain_public_key1);
673 let chain_node1 = ServiceNode {
674 component: "edge_function".to_string(),
675 public_key: chain_public_key1.clone(),
676 };
677
678 let chain_key2 = KeyPair::new();
679 let chain_public_key2 = hex::encode(chain_key2.public().to_bytes());
680 let chain_public_key2 = format!("ed25519/{}", chain_public_key2);
681 let chain_node2 = ServiceNode {
682 component: "middleware".to_string(),
683 public_key: chain_public_key2.clone(),
684 };
685
686 let chain_key3 = KeyPair::new();
687 let chain_public_key3 = hex::encode(chain_key3.public().to_bytes());
688 let chain_public_key3 = format!("ed25519/{}", chain_public_key3);
689 let chain_node3 = ServiceNode {
690 component: "backend".to_string(),
691 public_key: chain_public_key3.clone(),
692 };
693
694 let nodes = vec![
696 chain_node1.clone(),
697 chain_node2.clone(),
698 chain_node3.clone(),
699 ];
700 let token = create_service_chain_biscuit(
701 subject.clone(),
702 resource.clone(),
703 "read".to_string(),
704 root,
705 &nodes,
706 TokenTimeConfig::default(),
707 );
708 assert!(token.is_ok());
709 let token = token.unwrap();
710
711 println!("Created initial token");
712
713 let biscuit = Biscuit::from(&token, public_key).unwrap();
715 let third_party_request1 = biscuit.third_party_request().unwrap();
716 let third_party_block1 = block!(
717 r#"
718 service("res1");
719 "#
720 );
721 let third_party_block1 = third_party_request1
722 .create_block(&chain_key1.private(), third_party_block1)
723 .unwrap();
724 let attested_biscuit1 = biscuit
725 .append_third_party(chain_key1.public(), third_party_block1)
726 .unwrap();
727
728 let all_nodes = vec![
730 chain_node1.clone(),
731 chain_node2.clone(),
732 chain_node3.clone(),
733 ];
734 let attested_token1 = attested_biscuit1.to_vec().unwrap();
735
736 let res = verify_service_chain_biscuit_local(
739 attested_token1.clone(),
740 public_key,
741 subject.clone(),
742 resource.clone(),
743 "read".to_string(),
744 all_nodes.clone(),
745 Some("middleware".to_string()),
746 );
747 assert!(res.is_ok());
748
749 let res = verify_service_chain_biscuit_local(
753 attested_token1.clone(),
754 public_key,
755 subject.clone(),
756 resource.clone(),
757 "read".to_string(),
758 all_nodes.clone(),
759 Some("backend".to_string()),
760 );
761 assert!(res.is_err());
762 }
763
764 #[test]
765 fn test_service_chain_biscuit_with_custom_time() {
766 let subject = "test@test.com".to_owned();
767 let resource = "res1".to_string();
768 let root = KeyPair::new();
769 let public_key = root.public();
770 let chain_key = KeyPair::new();
771 let chain_public_key = hex::encode(chain_key.public().to_bytes());
772 let chain_public_key = format!("ed25519/{}", chain_public_key);
773 let chain_node = ServiceNode {
774 component: "edge_function".to_string(),
775 public_key: chain_public_key.clone(),
776 };
777 let nodes = vec![chain_node];
778
779 let valid_token = create_service_chain_biscuit_with_time(
781 subject.clone(),
782 resource.clone(),
783 "read".to_string(),
784 root,
785 &nodes,
786 TokenTimeConfig::default(),
787 );
788 assert!(valid_token.is_ok());
789 let valid_token = valid_token.unwrap().to_vec().unwrap();
790
791 let res = verify_biscuit_local(
793 valid_token.clone(),
794 public_key,
795 subject.clone(),
796 resource.clone(),
797 "read".to_string(),
798 );
799 assert!(res.is_ok());
800
801 let expired_time_config = TokenTimeConfig {
803 start_time: Some(Utc::now().timestamp() - 360), duration: 300, };
806
807 let root2 = KeyPair::new();
809 let public_key2 = root2.public();
810
811 let expired_token = create_service_chain_biscuit_with_time(
812 subject.clone(),
813 resource.clone(),
814 "read".to_string(),
815 root2,
816 &nodes,
817 expired_time_config,
818 );
819 assert!(expired_token.is_ok());
820 let expired_token = expired_token.unwrap().to_vec().unwrap();
821
822 let res = verify_biscuit_local(
824 expired_token,
825 public_key2,
826 subject,
827 resource,
828 "read".to_string(),
829 );
830 assert!(res.is_err());
831 }
832}