diff --git a/biscuit-auth/src/datalog/mod.rs b/biscuit-auth/src/datalog/mod.rs index eac57d95..6820f053 100644 --- a/biscuit-auth/src/datalog/mod.rs +++ b/biscuit-auth/src/datalog/mod.rs @@ -733,7 +733,7 @@ impl World { } /// runtime limits for the Datalog engine -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct RunLimits { /// maximum number of Datalog facts (memory usage) pub max_facts: u64, diff --git a/biscuit-auth/src/token/authorizer/snapshot.rs b/biscuit-auth/src/token/authorizer/snapshot.rs index 2d523ff9..95b06d79 100644 --- a/biscuit-auth/src/token/authorizer/snapshot.rs +++ b/biscuit-auth/src/token/authorizer/snapshot.rs @@ -115,6 +115,46 @@ impl super::Authorizer { authorizer.blocks = Some(blocks); } + let mut authorizer_origin = Origin::default(); + authorizer_origin.insert(usize::MAX); + + let authorizer_scopes: Vec = authorizer + .authorizer_block_builder + .scopes + .clone() + .iter() + .map(|s| s.convert(&mut authorizer.symbols)) + .collect(); + + let authorizer_trusted_origins = TrustedOrigins::from_scopes( + &authorizer_scopes, + &TrustedOrigins::default(), + usize::MAX, + &authorizer.public_key_to_block_id, + ); + for fact in &authorizer.authorizer_block_builder.facts { + authorizer + .world + .facts + .insert(&authorizer_origin, fact.convert(&mut authorizer.symbols)); + } + + for rule in &authorizer.authorizer_block_builder.rules { + let rule = rule.convert(&mut authorizer.symbols); + + let rule_trusted_origins = TrustedOrigins::from_scopes( + &rule.scopes, + &authorizer_trusted_origins, + usize::MAX, + &authorizer.public_key_to_block_id, + ); + + authorizer + .world + .rules + .insert(usize::MAX, &rule_trusted_origins, rule); + } + for GeneratedFacts { origins, facts } in world.generated_facts { let origin = proto_origin_to_authorizer_origin(&origins)?; @@ -271,3 +311,171 @@ pub(crate) fn proto_origin_to_authorizer_origin( Ok(new_origin) } + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + use std::time::Duration; + + use crate::{datalog::RunLimits, Algorithm, AuthorizerBuilder}; + use crate::{Authorizer, BiscuitBuilder, KeyPair}; + + #[test] + fn roundtrip_builder() { + let secp_pubkey = KeyPair::new_with_algorithm(Algorithm::Secp256r1).public(); + let ed_pubkey = KeyPair::new_with_algorithm(Algorithm::Ed25519).public(); + let builder = AuthorizerBuilder::new() + .limits(RunLimits { + max_facts: 42, + max_iterations: 42, + max_time: Duration::from_secs(1), + }) + .code_with_params( + r#" + fact(true); + head($a) <- fact($a); + check if head(true) trusting authority, {ed_pubkey}, {secp_pubkey}; + allow if head(true); + deny if head(false); + "#, + HashMap::default(), + HashMap::from([ + ("ed_pubkey".to_string(), ed_pubkey), + ("secp_pubkey".to_string(), secp_pubkey), + ]), + ) + .unwrap(); + let snapshot = builder.snapshot().unwrap(); + + let parsed = AuthorizerBuilder::from_snapshot(snapshot).unwrap(); + assert_eq!(parsed.dump_code(), builder.dump_code()); + assert_eq!(parsed.limits, builder.limits); + } + + #[test] + fn roundtrip_with_token() { + let secp_pubkey = KeyPair::new_with_algorithm(Algorithm::Secp256r1).public(); + let ed_pubkey = KeyPair::new_with_algorithm(Algorithm::Ed25519).public(); + let builder = AuthorizerBuilder::new() + .limits(RunLimits { + max_facts: 42, + max_iterations: 42, + max_time: Duration::from_secs(1), + }) + .code_with_params( + r#" + fact(true); + head($a) <- fact($a); + check if head(true) trusting authority, {ed_pubkey}, {secp_pubkey}; + allow if head(true); + deny if head(false); + "#, + HashMap::default(), + HashMap::from([ + ("ed_pubkey".to_string(), ed_pubkey), + ("secp_pubkey".to_string(), secp_pubkey), + ]), + ) + .unwrap(); + let biscuit = BiscuitBuilder::new() + .code_with_params( + r#" + bfact(true); + bhead($a) <- fact($a); + check if bhead(true) trusting authority, {ed_pubkey}, {secp_pubkey}; + "#, + HashMap::default(), + HashMap::from([ + ("ed_pubkey".to_string(), ed_pubkey), + ("secp_pubkey".to_string(), secp_pubkey), + ]), + ) + .unwrap() + .build(&KeyPair::new()) + .unwrap(); + + let authorizer_pre_run = builder.build(&biscuit).unwrap(); + + let snapshot = authorizer_pre_run.snapshot().unwrap(); + + let parsed = Authorizer::from_snapshot(snapshot).unwrap(); + assert_eq!(parsed.dump_code(), authorizer_pre_run.dump_code()); + assert_eq!(parsed.limits(), authorizer_pre_run.limits()); + + let mut authorizer_post_run = authorizer_pre_run.clone(); + let _ = authorizer_post_run.run(); + + let snapshot = authorizer_post_run.snapshot().unwrap(); + + let parsed = Authorizer::from_snapshot(snapshot).unwrap(); + assert_eq!(parsed.dump_code(), authorizer_post_run.dump_code()); + assert_eq!(parsed.limits(), authorizer_post_run.limits()); + } + + #[test] + fn roundtrip_without_token() { + let builder = AuthorizerBuilder::new() + .limits(RunLimits { + max_facts: 42, + max_iterations: 42, + max_time: Duration::from_secs(1), + }) + .code( + r#" + fact(true); + head($a) <- fact($a); + check if head(true); + allow if head(true); + deny if head(false); + "#, + ) + .unwrap(); + let authorizer = builder.build_unauthenticated().unwrap(); + let snapshot = authorizer.snapshot().unwrap(); + + let parsed = Authorizer::from_snapshot(snapshot).unwrap(); + assert_eq!(parsed.dump_code(), authorizer.dump_code()); + assert_eq!(parsed.limits(), authorizer.limits()); + + let mut authorizer_post_run = authorizer.clone(); + let _ = authorizer_post_run.run(); + let snapshot = authorizer_post_run.snapshot().unwrap(); + + let parsed = Authorizer::from_snapshot(snapshot).unwrap(); + assert_eq!(parsed.dump_code(), authorizer_post_run.dump_code()); + assert_eq!(parsed.limits(), authorizer_post_run.limits()); + } + + #[test] + fn roundtrip_with_eval_error() { + let builder = AuthorizerBuilder::new() + .limits(RunLimits { + max_facts: 42, + max_iterations: 42, + max_time: Duration::from_secs(1), + }) + .code( + r#" + fact(true); + head($a) <- fact($a), $a.length(); + allow if head(true); + deny if head(false); + "#, + ) + .unwrap(); + let authorizer = builder.build_unauthenticated().unwrap(); + let snapshot = authorizer.snapshot().unwrap(); + + let parsed = Authorizer::from_snapshot(snapshot).unwrap(); + assert_eq!(parsed.dump_code(), authorizer.dump_code()); + assert_eq!(parsed.limits(), authorizer.limits()); + + let mut authorizer_post_run = authorizer.clone(); + let _ = authorizer_post_run.run(); + let snapshot = authorizer_post_run.snapshot().unwrap(); + + let parsed = Authorizer::from_snapshot(snapshot).unwrap(); + assert_eq!(parsed.dump_code(), authorizer_post_run.dump_code()); + assert_eq!(parsed.limits(), authorizer_post_run.limits()); + } +} diff --git a/biscuit-auth/src/token/builder/authorizer.rs b/biscuit-auth/src/token/builder/authorizer.rs index 0ff28ccb..9a9121f2 100644 --- a/biscuit-auth/src/token/builder/authorizer.rs +++ b/biscuit-auth/src/token/builder/authorizer.rs @@ -31,7 +31,7 @@ pub struct AuthorizerBuilder { authorizer_block_builder: BlockBuilder, policies: Vec, extern_funcs: HashMap, - limits: AuthorizerLimits, + pub(crate) limits: AuthorizerLimits, } impl AuthorizerBuilder {