1mod attenuate;
42mod error;
43mod mint;
44mod utils;
45mod verify;
46
47pub use attenuate::add_service_node_attenuation;
48pub use error::TokenError;
49pub use mint::{
50 create_biscuit, create_service_chain_biscuit, create_service_chain_token,
51 create_service_chain_token_with_time, create_token, create_token_with_time, TokenTimeConfig,
52};
53pub use utils::{decode_token, encode_token, parse_token, public_key_from_pem_file};
54pub use verify::{
55 biscuit_key_from_string, verify_biscuit_local, verify_service_chain_biscuit_local, ServiceNode,
56};
57pub use verify::{verify_service_chain_token_local, verify_token_local};
58
59pub use biscuit_auth::{Biscuit, KeyPair, PublicKey};
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65 use biscuit_auth::macros::biscuit;
66 use serde_json::Value;
67 use std::fs;
68
69 #[test]
70 fn test_verify_biscuit_local() {
71 let keypair = KeyPair::new();
73 let public_key = keypair.public();
74
75 let biscuit_builder = biscuit!(
77 r#"
78 right("alice", "resource1", "read");
79 right("alice", "resource1", "write");
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 );
92 assert!(result.is_ok());
93 }
94
95 #[test]
96 fn test_verify_service_chain_biscuit() {
97 let root_keypair = KeyPair::new();
99 let service_keypair = KeyPair::new();
100 let service_public_key_hex = hex::encode(service_keypair.public().to_bytes());
101 let service_public_key_str = format!("ed25519/{}", service_public_key_hex);
102
103 let biscuit_builder = biscuit!(
105 r#"
106 right("alice", "resource1", "read");
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 service_nodes,
127 None,
128 );
129 assert!(result.is_ok());
130 }
131
132 #[test]
133 fn test_add_service_node_attenuation() {
134 let root_keypair = KeyPair::new();
136 let service_keypair = KeyPair::new();
137
138 let biscuit_builder = biscuit!(
140 r#"
141 right("alice", "resource1", "read");
142 right("alice", "resource1", "write");
143 "#
144 );
145 let biscuit = biscuit_builder.build(&root_keypair).unwrap();
146 let token_bytes = biscuit.to_vec().unwrap();
147
148 let attenuated_token = add_service_node_attenuation(
150 token_bytes,
151 root_keypair.public(),
152 "resource1",
153 &service_keypair,
154 );
155 assert!(attenuated_token.is_ok());
156
157 let result = verify_biscuit_local(
159 attenuated_token.unwrap(),
160 root_keypair.public(),
161 "alice".to_string(),
162 "resource1".to_string(),
163 );
164 assert!(result.is_ok());
165 }
166
167 #[test]
168 fn test_base64_utils() {
169 let keypair = KeyPair::new();
171 let biscuit_builder = biscuit!(
172 r#"
173 right("alice", "resource1", "read");
174 "#
175 );
176 let biscuit = biscuit_builder.build(&keypair).unwrap();
177 let original_bytes = biscuit.to_vec().unwrap();
178
179 let encoded = encode_token(&original_bytes);
181 assert!(!encoded.is_empty());
182
183 let decoded = decode_token(&encoded).unwrap();
185 assert_eq!(original_bytes, decoded);
186
187 let result = decode_token("invalid-base64!");
189 assert!(result.is_err());
190 }
191
192 #[test]
193 fn test_verify_token_string() {
194 let keypair = KeyPair::new();
196 let biscuit_builder = biscuit!(
197 r#"
198 right("alice", "resource1", "read");
199 right("alice", "resource1", "write");
200 "#
201 );
202 let biscuit = biscuit_builder.build(&keypair).unwrap();
203 let token_bytes = biscuit.to_vec().unwrap();
204 let token_string = encode_token(&token_bytes);
205
206 let result = verify_token_local(&token_string, keypair.public(), "alice", "resource1");
208 assert!(result.is_ok());
209
210 let result = verify_token_local(&token_string, keypair.public(), "bob", "resource1");
212 assert!(result.is_err());
213 }
214
215 #[test]
216 fn test_token_verification_from_json() {
217 let json_data =
219 fs::read_to_string("tests/test_tokens.json").expect("Failed to read test_tokens.json");
220 let tokens: Value =
221 serde_json::from_str(&json_data).expect("Failed to parse test_tokens.json");
222
223 let public_key = public_key_from_pem_file("tests/hessra_key.pem")
225 .expect("Failed to load test public key");
226
227 for token_value in tokens["tokens"].as_array().unwrap() {
229 let name = token_value["name"].as_str().unwrap();
230 let token_string = token_value["token"].as_str().unwrap();
231 let metadata = &token_value["metadata"];
232
233 let subject = metadata["subject"].as_str().unwrap();
235 let resource = metadata["resource"].as_str().unwrap();
236 let expected_result = metadata["expected_result"].as_bool().unwrap();
237 let description = metadata["description"].as_str().unwrap_or("No description");
238
239 println!("Testing token '{}': {}", name, description);
240
241 let result = parse_token(token_string, public_key).and_then(|biscuit| {
243 println!("Token blocks: {}", biscuit.print());
245
246 if metadata["type"].as_str().unwrap() == "singleton" {
247 verify_token_local(token_string, public_key, subject, resource)
248 } else {
249 let service_nodes = vec![
251 ServiceNode {
252 component: "auth_service".to_string(),
253 public_key: "ed25519/0123456789abcdef0123456789abcdef".to_string(),
254 },
255 ServiceNode {
256 component: "payment_service".to_string(),
257 public_key: "ed25519/fedcba9876543210fedcba9876543210".to_string(),
258 },
259 ];
260
261 verify_service_chain_token_local(
262 token_string,
263 public_key,
264 subject,
265 resource,
266 service_nodes,
267 None,
268 )
269 }
270 });
271
272 let verification_succeeded = result.is_ok();
274 assert_eq!(
275 verification_succeeded, expected_result,
276 "Token '{}' verification resulted in {}, expected: {} - {}",
277 name, verification_succeeded, expected_result, description
278 );
279
280 println!(
281 "✓ Token '{}' - Verification: {}",
282 name,
283 if verification_succeeded == expected_result {
284 "PASSED"
285 } else {
286 "FAILED"
287 }
288 );
289 }
290 }
291
292 #[test]
293 fn test_service_chain_tokens_from_json() {
294 let json_data =
296 fs::read_to_string("tests/test_tokens.json").expect("Failed to read test_tokens.json");
297 let tokens: Value =
298 serde_json::from_str(&json_data).expect("Failed to parse test_tokens.json");
299
300 let public_key = public_key_from_pem_file("tests/hessra_key.pem")
302 .expect("Failed to load test public key");
303
304 if let Some(tokens_array) = tokens["tokens"].as_array() {
306 if let Some(order_service_token) = tokens_array
307 .iter()
308 .find(|t| t["name"].as_str().unwrap() == "argo-cli1_access_order_service")
309 {
310 let token_string = order_service_token["token"].as_str().unwrap();
311 let subject = order_service_token["metadata"]["subject"].as_str().unwrap();
312 let resource = order_service_token["metadata"]["resource"]
313 .as_str()
314 .unwrap();
315 let expected_result = order_service_token["metadata"]["expected_result"]
316 .as_bool()
317 .unwrap();
318
319 let service_nodes = vec![
321 ServiceNode {
322 component: "auth_service".to_string(),
323 public_key: "ed25519/0123456789abcdef0123456789abcdef".to_string(),
324 },
325 ServiceNode {
326 component: "payment_service".to_string(),
327 public_key: "ed25519/fedcba9876543210fedcba9876543210".to_string(),
328 },
329 ];
330
331 let result = verify_service_chain_token_local(
333 token_string,
334 public_key,
335 subject,
336 resource,
337 service_nodes,
338 None,
339 );
340
341 assert_eq!(
343 result.is_ok(),
344 expected_result,
345 "Service chain verification for '{}' resulted in {}, expected: {}",
346 order_service_token["name"].as_str().unwrap(),
347 result.is_ok(),
348 expected_result
349 );
350 }
351 }
352 }
353
354 #[test]
355 fn test_service_chain_lifecycle() {
356 let json_data = fs::read_to_string("tests/service_chain_tokens.json")
358 .expect("Failed to read service_chain_tokens.json");
359 let tokens: Value =
360 serde_json::from_str(&json_data).expect("Failed to parse service_chain_tokens.json");
361
362 let initial_token = tokens["tokens"][0]["token"].as_str().unwrap();
364 let token_after_auth = tokens["tokens"][1]["token"].as_str().unwrap();
365 let token_after_payment = tokens["tokens"][2]["token"].as_str().unwrap();
366 let final_token = tokens["tokens"][3]["token"].as_str().unwrap();
367
368 let subject = "uri:urn:test:argo-cli1";
370 let resource = "order_service";
371
372 let root_public_key = public_key_from_pem_file("tests/hessra_key.pem")
374 .expect("Failed to load test public key");
375
376 let auth_service_pk_str = tokens["tokens"][1]["metadata"]["service_nodes"][0]["public_key"]
378 .as_str()
379 .unwrap();
380 let payment_service_pk_str = tokens["tokens"][2]["metadata"]["service_nodes"][1]
381 ["public_key"]
382 .as_str()
383 .unwrap();
384 let order_service_pk_str = tokens["tokens"][3]["metadata"]["service_nodes"][2]
385 ["public_key"]
386 .as_str()
387 .unwrap();
388
389 let result = verify_token_local(initial_token, root_public_key, subject, resource);
394 assert!(result.is_ok(), "Initial token verification failed");
395
396 let service_nodes_for_payment = vec![ServiceNode {
398 component: "auth_service".to_string(),
399 public_key: auth_service_pk_str.to_string(),
400 }];
401
402 let result = verify_service_chain_token_local(
403 token_after_auth,
404 root_public_key,
405 subject,
406 resource,
407 service_nodes_for_payment.clone(),
408 None,
409 );
410 assert!(
411 result.is_ok(),
412 "Token with auth attestation verification failed"
413 );
414
415 let service_nodes_for_order = vec![
417 ServiceNode {
418 component: "auth_service".to_string(),
419 public_key: auth_service_pk_str.to_string(),
420 },
421 ServiceNode {
422 component: "payment_service".to_string(),
423 public_key: payment_service_pk_str.to_string(),
424 },
425 ];
426
427 let result = verify_service_chain_token_local(
428 token_after_payment,
429 root_public_key,
430 subject,
431 resource,
432 service_nodes_for_order.clone(),
433 None,
434 );
435 assert!(
436 result.is_ok(),
437 "Token with payment attestation verification failed"
438 );
439
440 let service_nodes_complete = 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 ServiceNode {
451 component: "order_service".to_string(),
452 public_key: order_service_pk_str.to_string(),
453 },
454 ];
455
456 let result = verify_service_chain_token_local(
457 final_token,
458 root_public_key,
459 subject,
460 resource,
461 service_nodes_complete.clone(),
462 None,
463 );
464
465 if result.is_err() {
467 println!("Error details: {:?}", result);
468
469 let decoded_final = decode_token(final_token).unwrap();
471 if let Ok(biscuit) = Biscuit::from(&decoded_final, root_public_key) {
472 println!("Token blocks: {}", biscuit.print());
473 } else {
474 println!("Failed to parse token");
475 }
476 }
477
478 assert!(result.is_ok(), "Final token verification failed");
479
480 let result = verify_service_chain_token_local(
482 token_after_auth,
483 root_public_key,
484 subject,
485 resource,
486 service_nodes_complete,
487 None,
488 );
489 assert!(
491 result.is_err(),
492 "Incomplete service chain should be rejected"
493 );
494 }
495}