Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix snapshot de-serializing #257

Merged
merged 2 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion biscuit-auth/src/datalog/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
208 changes: 208 additions & 0 deletions biscuit-auth/src/token/authorizer/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<crate::token::Scope> = 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)?;

Expand Down Expand Up @@ -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());
}
}
2 changes: 1 addition & 1 deletion biscuit-auth/src/token/builder/authorizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub struct AuthorizerBuilder {
authorizer_block_builder: BlockBuilder,
policies: Vec<Policy>,
extern_funcs: HashMap<String, ExternFunc>,
limits: AuthorizerLimits,
pub(crate) limits: AuthorizerLimits,
}

impl AuthorizerBuilder {
Expand Down
Loading