1mod attest;
43mod error;
44mod mint;
45mod utils;
46mod verify;
47
48pub use attest::add_service_node_attestation;
49pub use error::TokenError;
50pub use mint::{
51 create_biscuit, create_service_chain_biscuit, create_service_chain_token,
52 create_service_chain_token_with_time, create_token, create_token_with_time, TokenTimeConfig,
53};
54pub use utils::{decode_token, encode_token, parse_token, public_key_from_pem_file};
55pub use verify::{
56 biscuit_key_from_string, verify_biscuit_local, verify_service_chain_biscuit_local, ServiceNode,
57};
58pub use verify::{verify_service_chain_token_local, verify_token_local};
59
60pub use biscuit_auth::{Biscuit, KeyPair, PublicKey};
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use biscuit_auth::macros::biscuit;
67 use serde_json::Value;
68 use std::fs;
69
70 #[test]
71 fn test_verify_biscuit_local() {
72 let keypair = KeyPair::new();
74 let public_key = keypair.public();
75
76 let biscuit_builder = biscuit!(
78 r#"
79 right("alice", "resource1", "read");
80 "#
81 );
82 let biscuit = biscuit_builder.build(&keypair).unwrap();
83 let token_bytes = biscuit.to_vec().unwrap();
84
85 let result = verify_biscuit_local(
87 token_bytes,
88 public_key,
89 "alice".to_string(),
90 "resource1".to_string(),
91 "read".to_string(),
92 );
93 assert!(result.is_ok());
94 }
95
96 #[test]
97 fn test_verify_service_chain_biscuit() {
98 let root_keypair = KeyPair::new();
100 let service_keypair = KeyPair::new();
101 let service_public_key_hex = hex::encode(service_keypair.public().to_bytes());
102 let service_public_key_str = format!("ed25519/{}", service_public_key_hex);
103
104 let biscuit_builder = biscuit!(
106 r#"
107 right("alice", "resource1", "write");
108 node("resource1", "service1");
109 "#
110 );
111 let biscuit = biscuit_builder.build(&root_keypair).unwrap();
112 let token_bytes = biscuit.to_vec().unwrap();
113
114 let service_nodes = vec![ServiceNode {
116 component: "service1".to_string(),
117 public_key: service_public_key_str,
118 }];
119
120 let result = verify_service_chain_biscuit_local(
122 token_bytes,
123 root_keypair.public(),
124 "alice".to_string(),
125 "resource1".to_string(),
126 "write".to_string(),
127 service_nodes,
128 None,
129 );
130 assert!(result.is_ok());
131 }
132
133 #[test]
134 fn test_add_service_node_attestation() {
135 let root_keypair = KeyPair::new();
137 let service_keypair = KeyPair::new();
138
139 let biscuit_builder = biscuit!(
141 r#"
142 right("alice", "resource1", "read");
143 right("alice", "resource1", "write");
144 "#
145 );
146 let biscuit = biscuit_builder.build(&root_keypair).unwrap();
147 let token_bytes = biscuit.to_vec().unwrap();
148
149 let attested_token = add_service_node_attestation(
151 token_bytes,
152 root_keypair.public(),
153 "resource1",
154 &service_keypair,
155 );
156 assert!(attested_token.is_ok());
157
158 let result = verify_biscuit_local(
160 attested_token.unwrap(),
161 root_keypair.public(),
162 "alice".to_string(),
163 "resource1".to_string(),
164 "read".to_string(),
165 );
166 assert!(result.is_ok());
167 }
168
169 #[test]
170 fn test_base64_utils() {
171 let keypair = KeyPair::new();
173 let biscuit_builder = biscuit!(
174 r#"
175 right("alice", "resource1", "read");
176 "#
177 );
178 let biscuit = biscuit_builder.build(&keypair).unwrap();
179 let original_bytes = biscuit.to_vec().unwrap();
180
181 let encoded = encode_token(&original_bytes);
183 assert!(!encoded.is_empty());
184
185 let decoded = decode_token(&encoded).unwrap();
187 assert_eq!(original_bytes, decoded);
188
189 let result = decode_token("invalid-base64!");
191 assert!(result.is_err());
192 }
193
194 #[test]
195 fn test_verify_token_string() {
196 let keypair = KeyPair::new();
198 let biscuit_builder = biscuit!(
199 r#"
200 right("alice", "resource1", "read");
201 right("alice", "resource1", "write");
202 "#
203 );
204 let biscuit = biscuit_builder.build(&keypair).unwrap();
205 let token_bytes = biscuit.to_vec().unwrap();
206 let token_string = encode_token(&token_bytes);
207
208 let result = verify_token_local(
210 &token_string,
211 keypair.public(),
212 "alice",
213 "resource1",
214 "read",
215 );
216 assert!(result.is_ok());
217
218 let result =
220 verify_token_local(&token_string, keypair.public(), "bob", "resource1", "read");
221 assert!(result.is_err());
222 }
223
224 #[test]
225 fn test_token_verification_from_json() {
226 let json_data =
228 fs::read_to_string("tests/test_tokens.json").expect("Failed to read test_tokens.json");
229 let tokens: Value =
230 serde_json::from_str(&json_data).expect("Failed to parse test_tokens.json");
231
232 let public_key = public_key_from_pem_file("tests/hessra_key.pem")
234 .expect("Failed to load test public key");
235
236 for token_value in tokens["tokens"].as_array().unwrap() {
238 let name = token_value["name"].as_str().unwrap();
239 let token_string = token_value["token"].as_str().unwrap();
240 let metadata = &token_value["metadata"];
241
242 let subject = metadata["subject"].as_str().unwrap();
244 let resource = metadata["resource"].as_str().unwrap();
245 let expected_result = metadata["expected_result"].as_bool().unwrap();
246 let description = metadata["description"].as_str().unwrap_or("No description");
247
248 println!("Testing token '{}': {}", name, description);
249
250 let result = parse_token(token_string, public_key).and_then(|biscuit| {
252 println!("Token blocks: {}", biscuit.print());
254
255 if metadata["type"].as_str().unwrap() == "singleton" {
256 verify_token_local(token_string, public_key, subject, resource, "read")
257 } else {
258 let service_nodes = vec![
260 ServiceNode {
261 component: "auth_service".to_string(),
262 public_key: "ed25519/0123456789abcdef0123456789abcdef".to_string(),
263 },
264 ServiceNode {
265 component: "payment_service".to_string(),
266 public_key: "ed25519/fedcba9876543210fedcba9876543210".to_string(),
267 },
268 ];
269
270 verify_service_chain_token_local(
271 token_string,
272 public_key,
273 subject,
274 resource,
275 "read",
276 service_nodes,
277 None,
278 )
279 }
280 });
281
282 let verification_succeeded = result.is_ok();
284 assert_eq!(
285 verification_succeeded, expected_result,
286 "Token '{}' verification resulted in {}, expected: {} - {}",
287 name, verification_succeeded, expected_result, description
288 );
289
290 println!(
291 "✓ Token '{}' - Verification: {}",
292 name,
293 if verification_succeeded == expected_result {
294 "PASSED"
295 } else {
296 "FAILED"
297 }
298 );
299 }
300 }
301
302 #[test]
303 fn test_service_chain_tokens_from_json() {
304 let json_data =
306 fs::read_to_string("tests/test_tokens.json").expect("Failed to read test_tokens.json");
307 let tokens: Value =
308 serde_json::from_str(&json_data).expect("Failed to parse test_tokens.json");
309
310 let public_key = public_key_from_pem_file("tests/hessra_key.pem")
312 .expect("Failed to load test public key");
313
314 if let Some(tokens_array) = tokens["tokens"].as_array() {
316 if let Some(order_service_token) = tokens_array
317 .iter()
318 .find(|t| t["name"].as_str().unwrap() == "argo-cli1_access_order_service")
319 {
320 let token_string = order_service_token["token"].as_str().unwrap();
321 let subject = order_service_token["metadata"]["subject"].as_str().unwrap();
322 let resource = order_service_token["metadata"]["resource"]
323 .as_str()
324 .unwrap();
325 let expected_result = order_service_token["metadata"]["expected_result"]
326 .as_bool()
327 .unwrap();
328
329 let service_nodes = vec![
331 ServiceNode {
332 component: "auth_service".to_string(),
333 public_key: "ed25519/0123456789abcdef0123456789abcdef".to_string(),
334 },
335 ServiceNode {
336 component: "payment_service".to_string(),
337 public_key: "ed25519/fedcba9876543210fedcba9876543210".to_string(),
338 },
339 ];
340
341 let result = verify_service_chain_token_local(
343 token_string,
344 public_key,
345 subject,
346 resource,
347 "read",
348 service_nodes,
349 None,
350 );
351
352 assert_eq!(
354 result.is_ok(),
355 expected_result,
356 "Service chain verification for '{}' resulted in {}, expected: {}",
357 order_service_token["name"].as_str().unwrap(),
358 result.is_ok(),
359 expected_result
360 );
361 }
362 }
363 }
364
365 #[test]
366 fn test_service_chain_lifecycle() {
367 let json_data = fs::read_to_string("tests/service_chain_tokens.json")
369 .expect("Failed to read service_chain_tokens.json");
370 let tokens: Value =
371 serde_json::from_str(&json_data).expect("Failed to parse service_chain_tokens.json");
372
373 let initial_token = tokens["tokens"][0]["token"].as_str().unwrap();
375 let token_after_auth = tokens["tokens"][1]["token"].as_str().unwrap();
376 let token_after_payment = tokens["tokens"][2]["token"].as_str().unwrap();
377 let final_token = tokens["tokens"][3]["token"].as_str().unwrap();
378
379 let subject = "uri:urn:test:argo-cli1";
381 let resource = "order_service";
382
383 let root_public_key = public_key_from_pem_file("tests/hessra_key.pem")
385 .expect("Failed to load test public key");
386
387 let auth_service_pk_str = tokens["tokens"][1]["metadata"]["service_nodes"][0]["public_key"]
389 .as_str()
390 .unwrap();
391 let payment_service_pk_str = tokens["tokens"][2]["metadata"]["service_nodes"][1]
392 ["public_key"]
393 .as_str()
394 .unwrap();
395 let order_service_pk_str = tokens["tokens"][3]["metadata"]["service_nodes"][2]
396 ["public_key"]
397 .as_str()
398 .unwrap();
399
400 let result = verify_token_local(initial_token, root_public_key, subject, resource, "read");
405 assert!(result.is_ok(), "Initial token verification failed");
406
407 let service_nodes_for_payment = vec![ServiceNode {
409 component: "auth_service".to_string(),
410 public_key: auth_service_pk_str.to_string(),
411 }];
412
413 let result = verify_service_chain_token_local(
414 token_after_auth,
415 root_public_key,
416 subject,
417 resource,
418 "read",
419 service_nodes_for_payment.clone(),
420 None,
421 );
422 assert!(
423 result.is_ok(),
424 "Token with auth attestation verification failed"
425 );
426
427 let service_nodes_for_order = vec![
429 ServiceNode {
430 component: "auth_service".to_string(),
431 public_key: auth_service_pk_str.to_string(),
432 },
433 ServiceNode {
434 component: "payment_service".to_string(),
435 public_key: payment_service_pk_str.to_string(),
436 },
437 ];
438
439 let result = verify_service_chain_token_local(
440 token_after_payment,
441 root_public_key,
442 subject,
443 resource,
444 "read",
445 service_nodes_for_order.clone(),
446 None,
447 );
448 assert!(
449 result.is_ok(),
450 "Token with payment attestation verification failed"
451 );
452
453 let service_nodes_complete = vec![
455 ServiceNode {
456 component: "auth_service".to_string(),
457 public_key: auth_service_pk_str.to_string(),
458 },
459 ServiceNode {
460 component: "payment_service".to_string(),
461 public_key: payment_service_pk_str.to_string(),
462 },
463 ServiceNode {
464 component: "order_service".to_string(),
465 public_key: order_service_pk_str.to_string(),
466 },
467 ];
468
469 let result = verify_service_chain_token_local(
470 final_token,
471 root_public_key,
472 subject,
473 resource,
474 "read",
475 service_nodes_complete.clone(),
476 None,
477 );
478
479 if result.is_err() {
481 println!("Error details: {:?}", result);
482
483 let decoded_final = decode_token(final_token).unwrap();
485 if let Ok(biscuit) = Biscuit::from(&decoded_final, root_public_key) {
486 println!("Token blocks: {}", biscuit.print());
487 } else {
488 println!("Failed to parse token");
489 }
490 }
491
492 assert!(result.is_ok(), "Final token verification failed");
493
494 let result = verify_service_chain_token_local(
496 token_after_auth,
497 root_public_key,
498 subject,
499 resource,
500 "read",
501 service_nodes_complete,
502 None,
503 );
504 assert!(
506 result.is_err(),
507 "Incomplete service chain should be rejected"
508 );
509 }
510}