1mod attenuate;
35mod error;
36mod token;
37mod utils;
38mod verify;
39
40pub use attenuate::add_service_node_attenuation;
41pub use error::TokenError;
42pub use token::{parse_token, verify_service_chain_token, verify_token};
43pub use utils::{decode_token, encode_token, public_key_from_pem_file};
44pub use verify::{
45 biscuit_key_from_string, verify_biscuit_local, verify_service_chain_biscuit_local, ServiceNode,
46};
47
48pub use biscuit_auth::{Biscuit, KeyPair, PublicKey};
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54 use biscuit_auth::macros::biscuit;
55 use serde_json::Value;
56 use std::fs;
57
58 #[test]
59 fn test_verify_biscuit_local() {
60 let keypair = KeyPair::new();
62 let public_key = keypair.public();
63
64 let biscuit_builder = biscuit!(
66 r#"
67 right("alice", "resource1", "read");
68 right("alice", "resource1", "write");
69 "#
70 );
71 let biscuit = biscuit_builder.build(&keypair).unwrap();
72 let token_bytes = biscuit.to_vec().unwrap();
73
74 let result = verify_biscuit_local(
76 token_bytes,
77 public_key,
78 "alice".to_string(),
79 "resource1".to_string(),
80 );
81 assert!(result.is_ok());
82 }
83
84 #[test]
85 fn test_verify_service_chain_biscuit() {
86 let root_keypair = KeyPair::new();
88 let service_keypair = KeyPair::new();
89 let service_public_key_hex = hex::encode(service_keypair.public().to_bytes());
90 let service_public_key_str = format!("ed25519/{}", service_public_key_hex);
91
92 let biscuit_builder = biscuit!(
94 r#"
95 right("alice", "resource1", "read");
96 right("alice", "resource1", "write");
97 node("resource1", "service1");
98 "#
99 );
100 let biscuit = biscuit_builder.build(&root_keypair).unwrap();
101 let token_bytes = biscuit.to_vec().unwrap();
102
103 let service_nodes = vec![ServiceNode {
105 component: "service1".to_string(),
106 public_key: service_public_key_str,
107 }];
108
109 let result = verify_service_chain_biscuit_local(
111 token_bytes,
112 root_keypair.public(),
113 "alice".to_string(),
114 "resource1".to_string(),
115 service_nodes,
116 None,
117 );
118 assert!(result.is_ok());
119 }
120
121 #[test]
122 fn test_add_service_node_attenuation() {
123 let root_keypair = KeyPair::new();
125 let service_keypair = KeyPair::new();
126
127 let biscuit_builder = biscuit!(
129 r#"
130 right("alice", "resource1", "read");
131 right("alice", "resource1", "write");
132 "#
133 );
134 let biscuit = biscuit_builder.build(&root_keypair).unwrap();
135 let token_bytes = biscuit.to_vec().unwrap();
136
137 let attenuated_token = add_service_node_attenuation(
139 token_bytes,
140 root_keypair.public(),
141 "resource1",
142 &service_keypair,
143 );
144 assert!(attenuated_token.is_ok());
145
146 let result = verify_biscuit_local(
148 attenuated_token.unwrap(),
149 root_keypair.public(),
150 "alice".to_string(),
151 "resource1".to_string(),
152 );
153 assert!(result.is_ok());
154 }
155
156 #[test]
157 fn test_base64_utils() {
158 let keypair = KeyPair::new();
160 let biscuit_builder = biscuit!(
161 r#"
162 right("alice", "resource1", "read");
163 "#
164 );
165 let biscuit = biscuit_builder.build(&keypair).unwrap();
166 let original_bytes = biscuit.to_vec().unwrap();
167
168 let encoded = encode_token(&original_bytes);
170 assert!(!encoded.is_empty());
171
172 let decoded = decode_token(&encoded).unwrap();
174 assert_eq!(original_bytes, decoded);
175
176 let result = decode_token("invalid-base64!");
178 assert!(result.is_err());
179 }
180
181 #[test]
182 fn test_verify_token_string() {
183 let keypair = KeyPair::new();
185 let biscuit_builder = biscuit!(
186 r#"
187 right("alice", "resource1", "read");
188 right("alice", "resource1", "write");
189 "#
190 );
191 let biscuit = biscuit_builder.build(&keypair).unwrap();
192 let token_bytes = biscuit.to_vec().unwrap();
193 let token_string = encode_token(&token_bytes);
194
195 let result = verify_token(&token_string, keypair.public(), "alice", "resource1");
197 assert!(result.is_ok());
198
199 let result = verify_token(&token_string, keypair.public(), "bob", "resource1");
201 assert!(result.is_err());
202 }
203
204 #[test]
205 fn test_token_verification_from_json() {
206 let json_data =
208 fs::read_to_string("tests/test_tokens.json").expect("Failed to read test_tokens.json");
209 let tokens: Value =
210 serde_json::from_str(&json_data).expect("Failed to parse test_tokens.json");
211
212 let public_key = public_key_from_pem_file("tests/hessra_key.pem")
214 .expect("Failed to load test public key");
215
216 for token_value in tokens["tokens"].as_array().unwrap() {
218 let name = token_value["name"].as_str().unwrap();
219 let token_string = token_value["token"].as_str().unwrap();
220 let metadata = &token_value["metadata"];
221
222 let subject = metadata["subject"].as_str().unwrap();
224 let resource = metadata["resource"].as_str().unwrap();
225 let expected_result = metadata["expected_result"].as_bool().unwrap();
226 let description = metadata["description"].as_str().unwrap_or("No description");
227
228 println!("Testing token '{}': {}", name, description);
229
230 let result = parse_token(token_string, public_key).and_then(|biscuit| {
232 println!("Token blocks: {}", biscuit.print());
234
235 if metadata["type"].as_str().unwrap() == "singleton" {
236 verify_token(token_string, public_key, subject, resource)
237 } else {
238 let service_nodes = vec![
240 ServiceNode {
241 component: "auth_service".to_string(),
242 public_key: "ed25519/0123456789abcdef0123456789abcdef".to_string(),
243 },
244 ServiceNode {
245 component: "payment_service".to_string(),
246 public_key: "ed25519/fedcba9876543210fedcba9876543210".to_string(),
247 },
248 ];
249
250 verify_service_chain_token(
251 token_string,
252 public_key,
253 subject,
254 resource,
255 service_nodes,
256 None,
257 )
258 }
259 });
260
261 let verification_succeeded = result.is_ok();
263 assert_eq!(
264 verification_succeeded, expected_result,
265 "Token '{}' verification resulted in {}, expected: {} - {}",
266 name, verification_succeeded, expected_result, description
267 );
268
269 println!(
270 "✓ Token '{}' - Verification: {}",
271 name,
272 if verification_succeeded == expected_result {
273 "PASSED"
274 } else {
275 "FAILED"
276 }
277 );
278 }
279 }
280
281 #[test]
282 fn test_service_chain_tokens_from_json() {
283 let json_data =
285 fs::read_to_string("tests/test_tokens.json").expect("Failed to read test_tokens.json");
286 let tokens: Value =
287 serde_json::from_str(&json_data).expect("Failed to parse test_tokens.json");
288
289 let public_key = public_key_from_pem_file("tests/hessra_key.pem")
291 .expect("Failed to load test public key");
292
293 if let Some(tokens_array) = tokens["tokens"].as_array() {
295 if let Some(order_service_token) = tokens_array
296 .iter()
297 .find(|t| t["name"].as_str().unwrap() == "argo-cli1_access_order_service")
298 {
299 let token_string = order_service_token["token"].as_str().unwrap();
300 let subject = order_service_token["metadata"]["subject"].as_str().unwrap();
301 let resource = order_service_token["metadata"]["resource"]
302 .as_str()
303 .unwrap();
304 let expected_result = order_service_token["metadata"]["expected_result"]
305 .as_bool()
306 .unwrap();
307
308 let service_nodes = vec![
310 ServiceNode {
311 component: "auth_service".to_string(),
312 public_key: "ed25519/0123456789abcdef0123456789abcdef".to_string(),
313 },
314 ServiceNode {
315 component: "payment_service".to_string(),
316 public_key: "ed25519/fedcba9876543210fedcba9876543210".to_string(),
317 },
318 ];
319
320 let result = verify_service_chain_token(
322 token_string,
323 public_key,
324 subject,
325 resource,
326 service_nodes,
327 None,
328 );
329
330 assert_eq!(
332 result.is_ok(),
333 expected_result,
334 "Service chain verification for '{}' resulted in {}, expected: {}",
335 order_service_token["name"].as_str().unwrap(),
336 result.is_ok(),
337 expected_result
338 );
339 }
340 }
341 }
342
343 #[test]
344 fn test_service_chain_lifecycle() {
345 let json_data = fs::read_to_string("tests/service_chain_tokens.json")
347 .expect("Failed to read service_chain_tokens.json");
348 let tokens: Value =
349 serde_json::from_str(&json_data).expect("Failed to parse service_chain_tokens.json");
350
351 let initial_token = tokens["tokens"][0]["token"].as_str().unwrap();
353 let token_after_auth = tokens["tokens"][1]["token"].as_str().unwrap();
354 let token_after_payment = tokens["tokens"][2]["token"].as_str().unwrap();
355 let final_token = tokens["tokens"][3]["token"].as_str().unwrap();
356
357 let subject = "uri:urn:test:argo-cli1";
359 let resource = "order_service";
360
361 let root_public_key = public_key_from_pem_file("tests/hessra_key.pem")
363 .expect("Failed to load test public key");
364
365 let auth_service_pk_str = tokens["tokens"][1]["metadata"]["service_nodes"][0]["public_key"]
367 .as_str()
368 .unwrap();
369 let payment_service_pk_str = tokens["tokens"][2]["metadata"]["service_nodes"][1]
370 ["public_key"]
371 .as_str()
372 .unwrap();
373 let order_service_pk_str = tokens["tokens"][3]["metadata"]["service_nodes"][2]
374 ["public_key"]
375 .as_str()
376 .unwrap();
377
378 let result = verify_token(initial_token, root_public_key, subject, resource);
383 assert!(result.is_ok(), "Initial token verification failed");
384
385 let service_nodes_for_payment = vec![ServiceNode {
387 component: "auth_service".to_string(),
388 public_key: auth_service_pk_str.to_string(),
389 }];
390
391 let result = verify_service_chain_token(
392 token_after_auth,
393 root_public_key,
394 subject,
395 resource,
396 service_nodes_for_payment.clone(),
397 None,
398 );
399 assert!(
400 result.is_ok(),
401 "Token with auth attestation verification failed"
402 );
403
404 let service_nodes_for_order = vec![
406 ServiceNode {
407 component: "auth_service".to_string(),
408 public_key: auth_service_pk_str.to_string(),
409 },
410 ServiceNode {
411 component: "payment_service".to_string(),
412 public_key: payment_service_pk_str.to_string(),
413 },
414 ];
415
416 let result = verify_service_chain_token(
417 token_after_payment,
418 root_public_key,
419 subject,
420 resource,
421 service_nodes_for_order.clone(),
422 None,
423 );
424 assert!(
425 result.is_ok(),
426 "Token with payment attestation verification failed"
427 );
428
429 let service_nodes_complete = vec![
431 ServiceNode {
432 component: "auth_service".to_string(),
433 public_key: auth_service_pk_str.to_string(),
434 },
435 ServiceNode {
436 component: "payment_service".to_string(),
437 public_key: payment_service_pk_str.to_string(),
438 },
439 ServiceNode {
440 component: "order_service".to_string(),
441 public_key: order_service_pk_str.to_string(),
442 },
443 ];
444
445 let result = verify_service_chain_token(
446 final_token,
447 root_public_key,
448 subject,
449 resource,
450 service_nodes_complete.clone(),
451 None,
452 );
453
454 if result.is_err() {
456 println!("Error details: {:?}", result);
457
458 let decoded_final = decode_token(final_token).unwrap();
460 if let Ok(biscuit) = Biscuit::from(&decoded_final, root_public_key) {
461 println!("Token blocks: {}", biscuit.print());
462 } else {
463 println!("Failed to parse token");
464 }
465 }
466
467 assert!(result.is_ok(), "Final token verification failed");
468
469 let result = verify_service_chain_token(
471 token_after_auth,
472 root_public_key,
473 subject,
474 resource,
475 service_nodes_complete,
476 None,
477 );
478 assert!(
480 result.is_err(),
481 "Incomplete service chain should be rejected"
482 );
483 }
484}