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) -> Result<BiscuitBuilder, Box<dyn Error>> {
36 create_base_biscuit_builder_with_time(subject, resource, TokenTimeConfig::default())
37}
38
39fn create_base_biscuit_builder_with_time(
40 subject: String,
41 resource: String,
42 time_config: TokenTimeConfig,
43) -> Result<BiscuitBuilder, Box<dyn Error>> {
44 let start_time = time_config
45 .start_time
46 .unwrap_or_else(|| Utc::now().timestamp());
47 let expiration = start_time + time_config.duration;
48
49 let biscuit_builder = biscuit!(
50 r#"
51 right({subject}, {resource}, "read");
52 right({subject}, {resource}, "write");
53 check if time($time), $time < {expiration};
54 "#
55 );
56
57 Ok(biscuit_builder)
58}
59
60pub fn create_raw_biscuit(
61 subject: String,
62 resource: String,
63 key: KeyPair,
64 time_config: TokenTimeConfig,
65) -> Result<Biscuit, Box<dyn Error>> {
66 let biscuit =
67 create_base_biscuit_builder_with_time(subject, resource, time_config)?.build(&key)?;
68
69 info!("biscuit (authority): {}", biscuit);
70
71 Ok(biscuit)
72}
73
74pub fn create_biscuit(
91 subject: String,
92 resource: String,
93 key: KeyPair,
94 time_config: TokenTimeConfig,
95) -> Result<Vec<u8>, Box<dyn Error>> {
96 let biscuit = create_raw_biscuit(subject, resource, key, time_config)?;
97 let token = biscuit.to_vec()?;
98 Ok(token)
99}
100
101fn create_base64_biscuit(
102 subject: String,
103 resource: String,
104 key: KeyPair,
105 time_config: TokenTimeConfig,
106) -> Result<String, Box<dyn Error>> {
107 let biscuit = create_raw_biscuit(subject, resource, key, time_config)?;
108 let token = biscuit.to_base64()?;
109 Ok(token)
110}
111
112pub fn create_token(
113 subject: String,
114 resource: String,
115 key: KeyPair,
116) -> Result<String, Box<dyn Error>> {
117 create_base64_biscuit(subject, resource, key, TokenTimeConfig::default())
118}
119
120pub fn create_token_with_time(
121 subject: String,
122 resource: String,
123 key: KeyPair,
124 time_config: TokenTimeConfig,
125) -> Result<String, Box<dyn Error>> {
126 create_base64_biscuit(subject, resource, key, time_config)
127}
128
129pub fn create_service_chain_biscuit(
147 subject: String,
148 resource: String,
149 key: KeyPair,
150 nodes: &Vec<ServiceNode>,
151 time_config: TokenTimeConfig,
152) -> Result<Vec<u8>, Box<dyn Error>> {
153 let biscuit = create_raw_service_chain_biscuit(subject, resource, key, nodes, time_config)?;
154 let token = biscuit.to_vec()?;
155 Ok(token)
156}
157
158fn create_base64_service_chain_biscuit(
159 subject: String,
160 resource: String,
161 key: KeyPair,
162 nodes: &Vec<ServiceNode>,
163 time_config: TokenTimeConfig,
164) -> Result<String, Box<dyn Error>> {
165 let biscuit = create_raw_service_chain_biscuit(subject, resource, key, nodes, time_config)?;
166 let token = biscuit.to_base64()?;
167 Ok(token)
168}
169
170pub fn create_raw_service_chain_biscuit(
171 subject: String,
172 resource: String,
173 key: KeyPair,
174 nodes: &Vec<ServiceNode>,
175 time_config: TokenTimeConfig,
176) -> Result<Biscuit, Box<dyn Error>> {
177 create_service_chain_biscuit_with_time(subject, resource, key, nodes, time_config)
178}
179
180pub fn create_service_chain_token(
181 subject: String,
182 resource: String,
183 key: KeyPair,
184 nodes: &Vec<ServiceNode>,
185) -> Result<String, Box<dyn Error>> {
186 create_base64_service_chain_biscuit(subject, resource, key, nodes, TokenTimeConfig::default())
187}
188
189pub fn create_service_chain_biscuit_with_time(
207 subject: String,
208 resource: String,
209 key: KeyPair,
210 nodes: &Vec<ServiceNode>,
211 time_config: TokenTimeConfig,
212) -> Result<Biscuit, Box<dyn Error>> {
213 let service = resource.clone();
214 let mut biscuit_builder = create_base_biscuit_builder_with_time(subject, service, time_config)?;
215
216 for node in nodes {
218 let component = node.component.clone();
219 let public_key = biscuit_key_from_string(node.public_key.clone())?;
220 biscuit_builder = biscuit_builder.rule(rule!(
221 r#"
222 node($s, {component}) <- service($s) trusting {public_key};
223 "#
224 ))?;
225 }
226
227 let biscuit = biscuit_builder.build(&key)?;
228
229 info!("biscuit (authority): {}", biscuit);
230
231 Ok(biscuit)
232}
233
234pub fn create_service_chain_token_with_time(
235 subject: String,
236 resource: String,
237 key: KeyPair,
238 nodes: &Vec<ServiceNode>,
239 time_config: TokenTimeConfig,
240) -> Result<String, Box<dyn Error>> {
241 let biscuit =
242 create_service_chain_biscuit_with_time(subject, resource, key, nodes, time_config)?;
243 let token = biscuit.to_base64()?;
244 Ok(token)
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250 use crate::verify::{verify_biscuit_local, verify_service_chain_biscuit_local};
251 use biscuit::macros::block;
252 use biscuit::Biscuit;
253 #[test]
254 fn test_create_biscuit() {
255 let subject = "test@test.com".to_owned();
256 let resource: String = "res1".to_string();
257 let root = KeyPair::new();
258 let public_key = root.public();
259 let token = create_biscuit(
260 subject.clone(),
261 resource.clone(),
262 root,
263 TokenTimeConfig::default(),
264 )
265 .unwrap();
266
267 let res = verify_biscuit_local(token, public_key, subject, resource);
268 assert!(res.is_ok());
269 }
270
271 #[test]
272 fn test_biscuit_expiration() {
273 let subject = "test@test.com".to_owned();
274 let resource = "res1".to_string();
275 let root = KeyPair::new();
276 let public_key = root.public();
277 let token = create_biscuit(
279 subject.clone(),
280 resource.clone(),
281 root,
282 TokenTimeConfig::default(),
283 )
284 .unwrap();
285
286 let res =
287 verify_biscuit_local(token.clone(), public_key, subject.clone(), resource.clone());
288 assert!(res.is_ok());
289
290 let root = KeyPair::new();
292 let token = create_biscuit(
293 subject.clone(),
294 resource.clone(),
295 root,
296 TokenTimeConfig {
297 start_time: Some(Utc::now().timestamp() - 301),
298 duration: 300,
299 },
300 )
301 .unwrap();
302 let res = verify_biscuit_local(token, public_key, subject, resource);
303 assert!(res.is_err());
304 }
305
306 #[test]
307 fn test_custom_token_time_config() {
308 let subject = "test@test.com".to_owned();
309 let resource = "res1".to_string();
310 let root = KeyPair::new();
311 let public_key = root.public();
312
313 let past_time = Utc::now().timestamp() - 3600;
315 let time_config = TokenTimeConfig {
316 start_time: Some(past_time),
317 duration: 7200, };
319
320 let token = create_biscuit(subject.clone(), resource.clone(), root, time_config).unwrap();
321
322 let res =
324 verify_biscuit_local(token.clone(), public_key, subject.clone(), resource.clone());
325 assert!(res.is_ok());
326 }
327
328 #[test]
329 fn test_service_chain_biscuit() {
330 let subject = "test@test.com".to_owned();
331 let resource = "res1".to_string();
332 let root = KeyPair::new();
333 let public_key = root.public();
334 let chain_key = KeyPair::new();
335 let chain_public_key = hex::encode(chain_key.public().to_bytes());
336 let chain_public_key = format!("ed25519/{}", chain_public_key);
337 let chain_node = ServiceNode {
338 component: "edge_function".to_string(),
339 public_key: chain_public_key.clone(),
340 };
341 let nodes = vec![chain_node];
342 let token = create_service_chain_biscuit(
343 subject.clone(),
344 resource.clone(),
345 root,
346 &nodes,
347 TokenTimeConfig::default(),
348 );
349 if let Err(e) = &token {
350 println!("Error: {}", e);
351 }
352 assert!(token.is_ok());
353 let token = token.unwrap();
354 let res =
355 verify_biscuit_local(token.clone(), public_key, subject.clone(), resource.clone());
356 assert!(res.is_ok());
357 let biscuit = Biscuit::from(&token, public_key).unwrap();
358 let third_party_request = biscuit.third_party_request().unwrap();
359 let third_party_block = block!(
360 r#"
361 service("res1");
362 "#
363 );
364 let third_party_block = third_party_request
365 .create_block(&chain_key.private(), third_party_block)
366 .unwrap();
367 let attenuated_biscuit = biscuit
368 .append_third_party(chain_key.public(), third_party_block)
369 .unwrap();
370 let attenuated_token = attenuated_biscuit.to_vec().unwrap();
371 let res = verify_service_chain_biscuit_local(
372 attenuated_token,
373 public_key,
374 subject.clone(),
375 resource.clone(),
376 nodes,
377 None,
378 );
379 assert!(res.is_ok());
380 }
381
382 #[test]
383 fn test_service_chain_biscuit_with_component_name() {
384 let subject = "test@test.com".to_owned();
385 let resource = "res1".to_string();
386 let root = KeyPair::new();
387 let public_key = root.public();
388
389 let chain_key1 = KeyPair::new();
391 let chain_public_key1 = hex::encode(chain_key1.public().to_bytes());
392 let chain_public_key1 = format!("ed25519/{}", chain_public_key1);
393 let chain_node1 = ServiceNode {
394 component: "edge_function".to_string(),
395 public_key: chain_public_key1.clone(),
396 };
397
398 let chain_key2 = KeyPair::new();
399 let chain_public_key2 = hex::encode(chain_key2.public().to_bytes());
400 let chain_public_key2 = format!("ed25519/{}", chain_public_key2);
401 let chain_node2 = ServiceNode {
402 component: "middleware".to_string(),
403 public_key: chain_public_key2.clone(),
404 };
405
406 let nodes = vec![chain_node1.clone(), chain_node2.clone()];
407
408 let token = create_service_chain_biscuit(
410 subject.clone(),
411 resource.clone(),
412 root,
413 &nodes,
414 TokenTimeConfig::default(),
415 );
416 assert!(token.is_ok());
417 let token = token.unwrap();
418
419 let biscuit = Biscuit::from(&token, public_key).unwrap();
421 let third_party_request = biscuit.third_party_request().unwrap();
422 let third_party_block = block!(
423 r#"
424 service("res1");
425 "#
426 );
427 let third_party_block = third_party_request
428 .create_block(&chain_key1.private(), third_party_block)
429 .unwrap();
430 let attenuated_biscuit = biscuit
431 .append_third_party(chain_key1.public(), third_party_block)
432 .unwrap();
433 let attenuated_token = attenuated_biscuit.to_vec().unwrap();
434
435 let res = verify_service_chain_biscuit_local(
439 attenuated_token.clone(),
440 public_key,
441 subject.clone(),
442 resource.clone(),
443 nodes.clone(),
444 Some("edge_function".to_string()),
445 );
446 assert!(res.is_ok());
448
449 let nodes = vec![chain_node1.clone(), chain_node2.clone()];
451
452 let res = verify_service_chain_biscuit_local(
454 attenuated_token.clone(),
455 public_key,
456 subject.clone(),
457 resource.clone(),
458 nodes.clone(),
459 Some("middleware".to_string()),
460 );
461 assert!(res.is_ok());
462 }
463
464 #[test]
465 fn test_service_chain_biscuit_with_nonexistent_component() {
466 let subject = "test@test.com".to_owned();
467 let resource = "res1".to_string();
468 let root = KeyPair::new();
469 let public_key = root.public();
470 let chain_key = KeyPair::new();
471 let chain_public_key = hex::encode(chain_key.public().to_bytes());
472 let chain_public_key = format!("ed25519/{}", chain_public_key);
473 let chain_node = ServiceNode {
474 component: "edge_function".to_string(),
475 public_key: chain_public_key.clone(),
476 };
477 let nodes = vec![chain_node];
478 let token = create_service_chain_biscuit(
479 subject.clone(),
480 resource.clone(),
481 root,
482 &nodes,
483 TokenTimeConfig::default(),
484 );
485 assert!(token.is_ok());
486 let token = token.unwrap();
487
488 let biscuit = Biscuit::from(&token, public_key).unwrap();
489 let third_party_request = biscuit.third_party_request().unwrap();
490 let third_party_block = block!(
491 r#"
492 service("res1");
493 "#
494 );
495 let third_party_block = third_party_request
496 .create_block(&chain_key.private(), third_party_block)
497 .unwrap();
498 let attenuated_biscuit = biscuit
499 .append_third_party(chain_key.public(), third_party_block)
500 .unwrap();
501 let attenuated_token = attenuated_biscuit.to_vec().unwrap();
502
503 let res = verify_service_chain_biscuit_local(
505 attenuated_token,
506 public_key,
507 subject.clone(),
508 resource.clone(),
509 nodes.clone(),
510 Some("nonexistent_component".to_string()),
511 );
512 assert!(res.is_err());
513
514 let err = res.unwrap_err().to_string();
516 assert!(err.contains("nonexistent_component"));
517 }
518
519 #[test]
520 fn test_service_chain_biscuit_with_multiple_nodes() {
521 let subject = "test@test.com".to_owned();
522 let resource = "res1".to_string();
523 let root = KeyPair::new();
524 let public_key = root.public();
525
526 let chain_key1 = KeyPair::new();
528 let chain_public_key1 = hex::encode(chain_key1.public().to_bytes());
529 let chain_public_key1 = format!("ed25519/{}", chain_public_key1);
530 let chain_node1 = ServiceNode {
531 component: "edge_function".to_string(),
532 public_key: chain_public_key1.clone(),
533 };
534
535 let chain_key2 = KeyPair::new();
536 let chain_public_key2 = hex::encode(chain_key2.public().to_bytes());
537 let chain_public_key2 = format!("ed25519/{}", chain_public_key2);
538 let chain_node2 = ServiceNode {
539 component: "middleware".to_string(),
540 public_key: chain_public_key2.clone(),
541 };
542
543 let chain_key3 = KeyPair::new();
544 let chain_public_key3 = hex::encode(chain_key3.public().to_bytes());
545 let chain_public_key3 = format!("ed25519/{}", chain_public_key3);
546 let chain_node3 = ServiceNode {
547 component: "backend".to_string(),
548 public_key: chain_public_key3.clone(),
549 };
550
551 let nodes = vec![
553 chain_node1.clone(),
554 chain_node2.clone(),
555 chain_node3.clone(),
556 ];
557 let token = create_service_chain_biscuit(
558 subject.clone(),
559 resource.clone(),
560 root,
561 &nodes,
562 TokenTimeConfig::default(),
563 );
564 assert!(token.is_ok());
565 let token = token.unwrap();
566
567 println!("Created initial token");
568
569 let biscuit = Biscuit::from(&token, public_key).unwrap();
571 let third_party_request1 = biscuit.third_party_request().unwrap();
572 let third_party_block1 = block!(
573 r#"
574 service("res1");
575 "#
576 );
577 let third_party_block1 = third_party_request1
578 .create_block(&chain_key1.private(), third_party_block1)
579 .unwrap();
580 let attenuated_biscuit1 = biscuit
581 .append_third_party(chain_key1.public(), third_party_block1)
582 .unwrap();
583
584 let all_nodes = vec![
586 chain_node1.clone(),
587 chain_node2.clone(),
588 chain_node3.clone(),
589 ];
590 let attenuated_token1 = attenuated_biscuit1.to_vec().unwrap();
591
592 let res = verify_service_chain_biscuit_local(
595 attenuated_token1.clone(),
596 public_key,
597 subject.clone(),
598 resource.clone(),
599 all_nodes.clone(),
600 Some("middleware".to_string()),
601 );
602 assert!(res.is_ok());
603
604 let res = verify_service_chain_biscuit_local(
608 attenuated_token1.clone(),
609 public_key,
610 subject.clone(),
611 resource.clone(),
612 all_nodes.clone(),
613 Some("backend".to_string()),
614 );
615 assert!(res.is_err());
616 }
617
618 #[test]
619 fn test_service_chain_biscuit_with_custom_time() {
620 let subject = "test@test.com".to_owned();
621 let resource = "res1".to_string();
622 let root = KeyPair::new();
623 let public_key = root.public();
624 let chain_key = KeyPair::new();
625 let chain_public_key = hex::encode(chain_key.public().to_bytes());
626 let chain_public_key = format!("ed25519/{}", chain_public_key);
627 let chain_node = ServiceNode {
628 component: "edge_function".to_string(),
629 public_key: chain_public_key.clone(),
630 };
631 let nodes = vec![chain_node];
632
633 let valid_token = create_service_chain_biscuit_with_time(
635 subject.clone(),
636 resource.clone(),
637 root,
638 &nodes,
639 TokenTimeConfig::default(),
640 );
641 assert!(valid_token.is_ok());
642 let valid_token = valid_token.unwrap().to_vec().unwrap();
643
644 let res = verify_biscuit_local(
646 valid_token.clone(),
647 public_key,
648 subject.clone(),
649 resource.clone(),
650 );
651 assert!(res.is_ok());
652
653 let expired_time_config = TokenTimeConfig {
655 start_time: Some(Utc::now().timestamp() - 360), duration: 300, };
658
659 let root2 = KeyPair::new();
661 let public_key2 = root2.public();
662
663 let expired_token = create_service_chain_biscuit_with_time(
664 subject.clone(),
665 resource.clone(),
666 root2,
667 &nodes,
668 expired_time_config,
669 );
670 assert!(expired_token.is_ok());
671 let expired_token = expired_token.unwrap().to_vec().unwrap();
672
673 let res = verify_biscuit_local(expired_token, public_key2, subject, resource);
675 assert!(res.is_err());
676 }
677}