diff --git a/biscuit-auth/benches/token.rs b/biscuit-auth/benches/token.rs index 58239b1f..3d12bebb 100644 --- a/biscuit-auth/benches/token.rs +++ b/biscuit-auth/benches/token.rs @@ -3,8 +3,10 @@ extern crate biscuit_auth as biscuit; use std::time::Duration; use biscuit::{ - builder::*, builder_ext::BuilderExt, datalog::SymbolTable, AuthorizerLimits, Biscuit, KeyPair, - UnverifiedBiscuit, + builder::*, + builder_ext::{AuthorizerExt, BuilderExt}, + datalog::SymbolTable, + AuthorizerLimits, Biscuit, KeyPair, UnverifiedBiscuit, }; use codspeed_bencher_compat::{benchmark_group, benchmark_main, Bencher}; use rand::rngs::OsRng; @@ -13,12 +15,13 @@ fn create_block_1(b: &mut Bencher) { let mut rng = OsRng; let root = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); - let mut builder = Biscuit::builder(); - builder.add_fact(fact("right", &[string("file1"), string("read")])); - builder.add_fact(fact("right", &[string("file2"), string("read")])); - builder.add_fact(fact("right", &[string("file1"), string("write")])); - - let token = builder + let token = Biscuit::builder() + .fact(fact("right", &[string("file1"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file2"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file1"), string("write")])) + .unwrap() .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); let data = token.to_vec().unwrap(); @@ -26,15 +29,16 @@ fn create_block_1(b: &mut Bencher) { b.bytes = data.len() as u64; assert_eq!(b.bytes, 206); b.iter(|| { - let mut builder = Biscuit::builder(); - builder.add_fact(fact("right", &[string("file1"), string("read")])); - builder.add_fact(fact("right", &[string("file2"), string("read")])); - builder.add_fact(fact("right", &[string("file1"), string("write")])); - - let token = builder + let token = Biscuit::builder() + .fact(fact("right", &[string("file1"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file2"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file1"), string("write")])) + .unwrap() .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let data = token.to_vec().unwrap(); + let _data = token.to_vec().unwrap(); }); } @@ -43,19 +47,20 @@ fn append_block_2(b: &mut Bencher) { let root = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); - let mut builder = Biscuit::builder(); - builder.add_fact(fact("right", &[string("file1"), string("read")])); - builder.add_fact(fact("right", &[string("file2"), string("read")])); - builder.add_fact(fact("right", &[string("file1"), string("write")])); - - let token = builder + let token = Biscuit::builder() + .fact(fact("right", &[string("file1"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file2"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file1"), string("write")])) + .unwrap() .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); let base_data = token.to_vec().unwrap(); - let mut block_builder = BlockBuilder::new(); - block_builder.check_resource("file1"); - block_builder.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); let token2 = token.append_with_keypair(&keypair2, block_builder).unwrap(); let data = token2.to_vec().unwrap(); @@ -64,12 +69,12 @@ fn append_block_2(b: &mut Bencher) { assert_eq!(b.bytes, 189); b.iter(|| { let token = Biscuit::from(&base_data, &root.public()).unwrap(); - let mut block_builder = BlockBuilder::new(); - block_builder.check_resource("file1"); - block_builder.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); let token2 = token.append_with_keypair(&keypair2, block_builder).unwrap(); - let data = token2.to_vec().unwrap(); + let _data = token2.to_vec().unwrap(); }); } @@ -81,19 +86,20 @@ fn append_block_5(b: &mut Bencher) { let keypair4 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let keypair5 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); - let mut builder = Biscuit::builder(); - builder.add_fact(fact("right", &[string("file1"), string("read")])); - builder.add_fact(fact("right", &[string("file2"), string("read")])); - builder.add_fact(fact("right", &[string("file1"), string("write")])); - - let token = builder + let token = Biscuit::builder() + .fact(fact("right", &[string("file1"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file2"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file1"), string("write")])) + .unwrap() .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); let base_data = token.to_vec().unwrap(); - let mut block_builder = BlockBuilder::new(); - block_builder.check_resource("file1"); - block_builder.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); let token2 = token.append_with_keypair(&keypair2, block_builder).unwrap(); let data = token2.to_vec().unwrap(); @@ -102,28 +108,34 @@ fn append_block_5(b: &mut Bencher) { assert_eq!(b.bytes, 189); b.iter(|| { let token2 = Biscuit::from(&data, &root.public()).unwrap(); - let mut b = BlockBuilder::new(); - b.check_resource("file1"); - b.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); - let token3 = token2.append_with_keypair(&keypair3, b).unwrap(); + let token3 = token2 + .append_with_keypair(&keypair3, block_builder) + .unwrap(); let data = token3.to_vec().unwrap(); let token3 = Biscuit::from(&data, &root.public()).unwrap(); - let mut b = BlockBuilder::new(); - b.check_resource("file1"); - b.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); - let token4 = token3.append_with_keypair(&keypair4, b).unwrap(); + let token4 = token3 + .append_with_keypair(&keypair4, block_builder) + .unwrap(); let data = token4.to_vec().unwrap(); let token4 = Biscuit::from(&data, &root.public()).unwrap(); - let mut b = BlockBuilder::new(); - b.check_resource("file1"); - b.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); - let token5 = token4.append_with_keypair(&keypair5, b).unwrap(); - let data = token5.to_vec().unwrap(); + let token5 = token4 + .append_with_keypair(&keypair5, block_builder) + .unwrap(); + let _data = token5.to_vec().unwrap(); }); } @@ -132,19 +144,20 @@ fn unverified_append_block_2(b: &mut Bencher) { let root = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); - let mut builder = Biscuit::builder(); - builder.add_fact(fact("right", &[string("file1"), string("read")])); - builder.add_fact(fact("right", &[string("file2"), string("read")])); - builder.add_fact(fact("right", &[string("file1"), string("write")])); - - let token = builder + let token = Biscuit::builder() + .fact(fact("right", &[string("file1"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file2"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file1"), string("write")])) + .unwrap() .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); let base_data = token.to_vec().unwrap(); - let mut block_builder = BlockBuilder::new(); - block_builder.check_resource("file1"); - block_builder.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); let token2 = token.append_with_keypair(&keypair2, block_builder).unwrap(); let data = token2.to_vec().unwrap(); @@ -153,12 +166,12 @@ fn unverified_append_block_2(b: &mut Bencher) { assert_eq!(b.bytes, 189); b.iter(|| { let token = UnverifiedBiscuit::from(&base_data).unwrap(); - let mut block_builder = BlockBuilder::new(); - block_builder.check_resource("file1"); - block_builder.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); let token2 = token.append_with_keypair(&keypair2, block_builder).unwrap(); - let data = token2.to_vec().unwrap(); + let _data = token2.to_vec().unwrap(); }); } @@ -170,19 +183,20 @@ fn unverified_append_block_5(b: &mut Bencher) { let keypair4 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let keypair5 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); - let mut builder = Biscuit::builder(); - builder.add_fact(fact("right", &[string("file1"), string("read")])); - builder.add_fact(fact("right", &[string("file2"), string("read")])); - builder.add_fact(fact("right", &[string("file1"), string("write")])); - - let token = builder + let token = Biscuit::builder() + .fact(fact("right", &[string("file1"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file2"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file1"), string("write")])) + .unwrap() .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); let base_data = token.to_vec().unwrap(); - let mut block_builder = BlockBuilder::new(); - block_builder.check_resource("file1"); - block_builder.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); let token2 = token.append_with_keypair(&keypair2, block_builder).unwrap(); let data = token2.to_vec().unwrap(); @@ -191,28 +205,34 @@ fn unverified_append_block_5(b: &mut Bencher) { assert_eq!(b.bytes, 189); b.iter(|| { let token2 = UnverifiedBiscuit::from(&data).unwrap(); - let mut b = BlockBuilder::new(); - b.check_resource("file1"); - b.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); - let token3 = token2.append_with_keypair(&keypair3, b).unwrap(); + let token3 = token2 + .append_with_keypair(&keypair3, block_builder) + .unwrap(); let data = token3.to_vec().unwrap(); let token3 = UnverifiedBiscuit::from(&data).unwrap(); - let mut b = BlockBuilder::new(); - b.check_resource("file1"); - b.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); - let token4 = token3.append_with_keypair(&keypair4, b).unwrap(); + let token4 = token3 + .append_with_keypair(&keypair4, block_builder) + .unwrap(); let data = token4.to_vec().unwrap(); let token4 = UnverifiedBiscuit::from(&data).unwrap(); - let mut b = BlockBuilder::new(); - b.check_resource("file1"); - b.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); - let token5 = token4.append_with_keypair(&keypair5, b).unwrap(); - let data = token5.to_vec().unwrap(); + let token5 = token4 + .append_with_keypair(&keypair5, block_builder) + .unwrap(); + let _data = token5.to_vec().unwrap(); }); } @@ -222,29 +242,35 @@ fn verify_block_2(b: &mut Bencher) { let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let data = { - let mut builder = Biscuit::builder(); - builder.add_fact(fact("right", &[string("file1"), string("read")])); - builder.add_fact(fact("right", &[string("file2"), string("read")])); - builder.add_fact(fact("right", &[string("file1"), string("write")])); - - let token = builder + let token = Biscuit::builder() + .fact(fact("right", &[string("file1"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file2"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file1"), string("write")])) + .unwrap() .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let base_data = token.to_vec().unwrap(); + let _base_data = token.to_vec().unwrap(); - let mut block_builder = BlockBuilder::new(); - block_builder.check_resource("file1"); - block_builder.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); let token2 = token.append_with_keypair(&keypair2, block_builder).unwrap(); token2.to_vec().unwrap() }; let token = Biscuit::from(&data, &root.public()).unwrap(); - let mut verifier = token.authorizer().unwrap(); - verifier.add_fact("resource(\"file1\")"); - verifier.add_fact("operation(\"read\")"); - verifier.allow(); + let mut verifier = AuthorizerBuilder::new() + .token(&token) + .fact("resource(\"file1\")") + .unwrap() + .fact("operation(\"read\")") + .unwrap() + .allow_all() + .build() + .unwrap(); verifier .authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -254,10 +280,15 @@ fn verify_block_2(b: &mut Bencher) { b.bytes = data.len() as u64; b.iter(|| { let token = Biscuit::from(&data, &root.public()).unwrap(); - let mut verifier = token.authorizer().unwrap(); - verifier.add_fact("resource(\"file1\")"); - verifier.add_fact("operation(\"read\")"); - verifier.allow(); + let mut verifier = AuthorizerBuilder::new() + .token(&token) + .fact("resource(\"file1\")") + .unwrap() + .fact("operation(\"read\")") + .unwrap() + .allow_all() + .build() + .unwrap(); verifier .authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -276,41 +307,42 @@ fn verify_block_5(b: &mut Bencher) { let keypair5 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let data = { - let mut builder = Biscuit::builder(); - builder.add_fact(fact("right", &[string("file1"), string("read")])); - builder.add_fact(fact("right", &[string("file2"), string("read")])); - builder.add_fact(fact("right", &[string("file1"), string("write")])); - - let token = builder + let token = Biscuit::builder() + .fact(fact("right", &[string("file1"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file2"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file1"), string("write")])) + .unwrap() .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let base_data = token.to_vec().unwrap(); + let _base_data = token.to_vec().unwrap(); - let mut block_builder = BlockBuilder::new(); - block_builder.check_resource("file1"); - block_builder.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); let token2 = token.append_with_keypair(&keypair2, block_builder).unwrap(); - let mut block_builder = BlockBuilder::new(); - block_builder.check_resource("file1"); - block_builder.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); let token3 = token2 .append_with_keypair(&keypair3, block_builder) .unwrap(); - let mut block_builder = BlockBuilder::new(); - block_builder.check_resource("file1"); - block_builder.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); let token4 = token3 .append_with_keypair(&keypair4, block_builder) .unwrap(); - let mut block_builder = BlockBuilder::new(); - block_builder.check_resource("file1"); - block_builder.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); let token5 = token4 .append_with_keypair(&keypair5, block_builder) @@ -319,10 +351,15 @@ fn verify_block_5(b: &mut Bencher) { }; let token = Biscuit::from(&data, &root.public()).unwrap(); - let mut verifier = token.authorizer().unwrap(); - verifier.add_fact("resource(\"file1\")"); - verifier.add_fact("operation(\"read\")"); - verifier.allow(); + let mut verifier = AuthorizerBuilder::new() + .token(&token) + .fact("resource(\"file1\")") + .unwrap() + .fact("operation(\"read\")") + .unwrap() + .allow_all() + .build() + .unwrap(); verifier .authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -333,10 +370,15 @@ fn verify_block_5(b: &mut Bencher) { b.bytes = data.len() as u64; b.iter(|| { let token = Biscuit::from(&data, &root.public()).unwrap(); - let mut verifier = token.authorizer().unwrap(); - verifier.add_fact("resource(\"file1\")"); - verifier.add_fact("operation(\"read\")"); - verifier.allow(); + let mut verifier = AuthorizerBuilder::new() + .token(&token) + .fact("resource(\"file1\")") + .unwrap() + .fact("operation(\"read\")") + .unwrap() + .allow_all() + .build() + .unwrap(); verifier .authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -352,29 +394,35 @@ fn check_signature_2(b: &mut Bencher) { let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let data = { - let mut builder = Biscuit::builder(); - builder.add_fact(fact("right", &[string("file1"), string("read")])); - builder.add_fact(fact("right", &[string("file2"), string("read")])); - builder.add_fact(fact("right", &[string("file1"), string("write")])); - - let token = builder + let token = Biscuit::builder() + .fact(fact("right", &[string("file1"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file2"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file1"), string("write")])) + .unwrap() .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let base_data = token.to_vec().unwrap(); + let _base_data = token.to_vec().unwrap(); - let mut block_builder = BlockBuilder::new(); - block_builder.check_resource("file1"); - block_builder.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); let token2 = token.append_with_keypair(&keypair2, block_builder).unwrap(); token2.to_vec().unwrap() }; let token = Biscuit::from(&data, &root.public()).unwrap(); - let mut verifier = token.authorizer().unwrap(); - verifier.add_fact("resource(\"file1\")"); - verifier.add_fact("operation(\"read\")"); - verifier.allow(); + let mut verifier = AuthorizerBuilder::new() + .token(&token) + .fact("resource(\"file1\")") + .unwrap() + .fact("operation(\"read\")") + .unwrap() + .allow_all() + .build() + .unwrap(); verifier .authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -384,7 +432,7 @@ fn check_signature_2(b: &mut Bencher) { b.bytes = data.len() as u64; b.iter(|| { - let token = Biscuit::from(&data, &root.public()).unwrap(); + let _token = Biscuit::from(&data, &root.public()).unwrap(); }); } @@ -397,40 +445,41 @@ fn check_signature_5(b: &mut Bencher) { let keypair5 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let data = { - let mut builder = Biscuit::builder(); - builder.add_fact(fact("right", &[string("file1"), string("read")])); - builder.add_fact(fact("right", &[string("file2"), string("read")])); - builder.add_fact(fact("right", &[string("file1"), string("write")])); - - let token = builder + let token = Biscuit::builder() + .fact(fact("right", &[string("file1"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file2"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file1"), string("write")])) + .unwrap() .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let base_data = token.to_vec().unwrap(); + let _base_data = token.to_vec().unwrap(); - let mut block_builder = BlockBuilder::new(); - block_builder.check_resource("file1"); - block_builder.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); let token2 = token.append_with_keypair(&keypair2, block_builder).unwrap(); - let mut block_builder = BlockBuilder::new(); - block_builder.check_resource("file1"); - block_builder.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); let token3 = token2 .append_with_keypair(&keypair3, block_builder) .unwrap(); - let mut block_builder = BlockBuilder::new(); - block_builder.check_resource("file1"); - block_builder.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); let token4 = token3 .append_with_keypair(&keypair4, block_builder) .unwrap(); - let mut block_builder = BlockBuilder::new(); - block_builder.check_resource("file1"); - block_builder.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); let token5 = token4 .append_with_keypair(&keypair5, block_builder) @@ -439,10 +488,15 @@ fn check_signature_5(b: &mut Bencher) { }; let token = Biscuit::from(&data, &root.public()).unwrap(); - let mut verifier = token.authorizer().unwrap(); - verifier.add_fact("resource(\"file1\")"); - verifier.add_fact("operation(\"read\")"); - verifier.allow(); + let mut verifier = AuthorizerBuilder::new() + .token(&token) + .fact("resource(\"file1\")") + .unwrap() + .fact("operation(\"read\")") + .unwrap() + .allow_all() + .build() + .unwrap(); verifier .authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -452,7 +506,7 @@ fn check_signature_5(b: &mut Bencher) { b.bytes = data.len() as u64; b.iter(|| { - let token = Biscuit::from(&data, &root.public()).unwrap(); + let _token = Biscuit::from(&data, &root.public()).unwrap(); }); } @@ -462,29 +516,35 @@ fn checks_block_2(b: &mut Bencher) { let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let data = { - let mut builder = Biscuit::builder(); - builder.add_fact(fact("right", &[string("file1"), string("read")])); - builder.add_fact(fact("right", &[string("file2"), string("read")])); - builder.add_fact(fact("right", &[string("file1"), string("write")])); - - let token = builder + let token = Biscuit::builder() + .fact(fact("right", &[string("file1"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file2"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file1"), string("write")])) + .unwrap() .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let base_data = token.to_vec().unwrap(); + let _base_data = token.to_vec().unwrap(); - let mut block_builder = BlockBuilder::new(); - block_builder.check_resource("file1"); - block_builder.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); let token2 = token.append_with_keypair(&keypair2, block_builder).unwrap(); token2.to_vec().unwrap() }; let token = Biscuit::from(&data, &root.public()).unwrap(); - let mut verifier = token.authorizer().unwrap(); - verifier.add_fact("resource(\"file1\")"); - verifier.add_fact("operation(\"read\")"); - verifier.allow(); + let mut verifier = AuthorizerBuilder::new() + .token(&token) + .fact("resource(\"file1\")") + .unwrap() + .fact("operation(\"read\")") + .unwrap() + .allow_all() + .build() + .unwrap(); verifier .authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -495,10 +555,15 @@ fn checks_block_2(b: &mut Bencher) { let token = Biscuit::from(&data, &root.public()).unwrap(); b.bytes = data.len() as u64; b.iter(|| { - let mut verifier = token.authorizer().unwrap(); - verifier.add_fact("resource(\"file1\")"); - verifier.add_fact("operation(\"read\")"); - verifier.allow(); + let mut verifier = AuthorizerBuilder::new() + .token(&token) + .fact("resource(\"file1\")") + .unwrap() + .fact("operation(\"read\")") + .unwrap() + .allow_all() + .build() + .unwrap(); verifier .authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -514,29 +579,35 @@ fn checks_block_create_verifier2(b: &mut Bencher) { let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let data = { - let mut builder = Biscuit::builder(); - builder.add_fact(fact("right", &[string("file1"), string("read")])); - builder.add_fact(fact("right", &[string("file2"), string("read")])); - builder.add_fact(fact("right", &[string("file1"), string("write")])); - - let token = builder + let token = Biscuit::builder() + .fact(fact("right", &[string("file1"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file2"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file1"), string("write")])) + .unwrap() .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let base_data = token.to_vec().unwrap(); + let _base_data = token.to_vec().unwrap(); - let mut block_builder = BlockBuilder::new(); - block_builder.check_resource("file1"); - block_builder.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); let token2 = token.append_with_keypair(&keypair2, block_builder).unwrap(); token2.to_vec().unwrap() }; let token = Biscuit::from(&data, &root.public()).unwrap(); - let mut verifier = token.authorizer().unwrap(); - verifier.add_fact("resource(\"file1\")"); - verifier.add_fact("operation(\"read\")"); - verifier.allow(); + let mut verifier = AuthorizerBuilder::new() + .token(&token) + .fact("resource(\"file1\")") + .unwrap() + .fact("operation(\"read\")") + .unwrap() + .allow_all() + .build() + .unwrap(); verifier .authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -547,7 +618,7 @@ fn checks_block_create_verifier2(b: &mut Bencher) { let token = Biscuit::from(&data, &root.public()).unwrap(); b.bytes = data.len() as u64; b.iter(|| { - let mut verifier = token.authorizer().unwrap(); + let _verifier = token.authorizer().unwrap(); }); } @@ -557,29 +628,35 @@ fn checks_block_verify_only2(b: &mut Bencher) { let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let data = { - let mut builder = Biscuit::builder(); - builder.add_fact(fact("right", &[string("file1"), string("read")])); - builder.add_fact(fact("right", &[string("file2"), string("read")])); - builder.add_fact(fact("right", &[string("file1"), string("write")])); - - let token = builder + let token = Biscuit::builder() + .fact(fact("right", &[string("file1"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file2"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file1"), string("write")])) + .unwrap() .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let base_data = token.to_vec().unwrap(); + let _base_data = token.to_vec().unwrap(); - let mut block_builder = BlockBuilder::new(); - block_builder.check_resource("file1"); - block_builder.check_operation("read"); + let block_builder = BlockBuilder::new() + .check_resource("file1") + .check_operation("read"); let token2 = token.append_with_keypair(&keypair2, block_builder).unwrap(); token2.to_vec().unwrap() }; let token = Biscuit::from(&data, &root.public()).unwrap(); - let mut verifier = token.authorizer().unwrap(); - verifier.add_fact("resource(\"file1\")"); - verifier.add_fact("operation(\"read\")"); - verifier.allow(); + let mut verifier = AuthorizerBuilder::new() + .token(&token) + .fact("resource(\"file1\")") + .unwrap() + .fact("operation(\"read\")") + .unwrap() + .allow_all() + .build() + .unwrap(); verifier .authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -589,10 +666,15 @@ fn checks_block_verify_only2(b: &mut Bencher) { let token = Biscuit::from(&data, &root.public()).unwrap(); b.iter(|| { - let mut verifier = token.authorizer().unwrap(); - verifier.add_fact("resource(\"file1\")"); - verifier.add_fact("operation(\"read\")"); - verifier.allow(); + let mut verifier = AuthorizerBuilder::new() + .token(&token) + .fact("resource(\"file1\")") + .unwrap() + .fact("operation(\"read\")") + .unwrap() + .allow_all() + .build() + .unwrap(); verifier .authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), diff --git a/biscuit-auth/examples/testcases.rs b/biscuit-auth/examples/testcases.rs index d8a33d9f..f107d007 100644 --- a/biscuit-auth/examples/testcases.rs +++ b/biscuit-auth/examples/testcases.rs @@ -7,7 +7,6 @@ use biscuit::datalog::SymbolTable; use biscuit::error; use biscuit::format::convert::v2 as convert; use biscuit::macros::*; -use biscuit::Authorizer; use biscuit::{builder::*, builder_ext::*, Biscuit}; use biscuit::{KeyPair, PrivateKey, PublicKey}; use biscuit_auth::builder; @@ -344,12 +343,13 @@ fn validate_token_with_limits_and_external_functions( revocation_ids.push(hex::encode(&bytes)); } - let mut authorizer = Authorizer::new(); - authorizer.set_extern_funcs(extern_funcs); - authorizer.add_code(authorizer_code).unwrap(); - let authorizer_code = authorizer.dump_code(); + let builder = AuthorizerBuilder::new() + .set_extern_funcs(extern_funcs) + .code(authorizer_code) + .unwrap(); + let authorizer_code = builder.dump_code(); - match authorizer.add_token(&token) { + let mut authorizer = match builder.token(&token).build() { Ok(v) => v, Err(e) => { return Validation { @@ -878,9 +878,9 @@ fn scoped_rules(target: &str, root: &KeyPair, test: bool) -> TestResult { ) .unwrap(); - let mut block3 = BlockBuilder::new(); - - block3.add_fact(r#"owner("alice", "file2")"#).unwrap(); + let block3 = BlockBuilder::new() + .fact(r#"owner("alice", "file2")"#) + .unwrap(); let keypair3 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit3 = biscuit2.append_with_keypair(&keypair3, block3).unwrap(); @@ -973,14 +973,13 @@ fn expired_token(target: &str, root: &KeyPair, test: bool) -> TestResult { .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let mut block2 = block!(r#"check if resource("file1");"#); - - // January 1 2019 - block2.check_expiration_date( - UNIX_EPOCH - .checked_add(Duration::from_secs(49 * 365 * 24 * 3600)) - .unwrap(), - ); + let block2 = block!(r#"check if resource("file1");"#) + // January 1 2019 + .check_expiration_date( + UNIX_EPOCH + .checked_add(Duration::from_secs(49 * 365 * 24 * 3600)) + .unwrap(), + ); let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1.append_with_keypair(&keypair2, block2).unwrap(); @@ -1410,11 +1409,9 @@ fn unbound_variables_in_rule(target: &str, root: &KeyPair, test: bool) -> TestRe .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); - let mut block2 = BlockBuilder::new(); - - // this one does not go through the parser because it checks for unused variables - block2 - .add_rule(rule( + let block2 = BlockBuilder::new() + // this one does not go through the parser because it checks for unused variables + .rule(rule( "operation", &[var("unbound"), string("read")], &[pred("operation", &[var("any1"), var("any2")])], diff --git a/biscuit-auth/examples/third_party.rs b/biscuit-auth/examples/third_party.rs index 67fd6d99..5f135e2e 100644 --- a/biscuit-auth/examples/third_party.rs +++ b/biscuit-auth/examples/third_party.rs @@ -1,5 +1,8 @@ use biscuit_auth::{ - builder::Algorithm, builder::BlockBuilder, datalog::SymbolTable, Biscuit, KeyPair, + builder::{Algorithm, AuthorizerBuilder, BlockBuilder}, + builder_ext::AuthorizerExt, + datalog::SymbolTable, + Biscuit, KeyPair, }; use rand::{prelude::StdRng, SeedableRng}; @@ -7,18 +10,13 @@ fn main() { let mut rng: StdRng = SeedableRng::seed_from_u64(0); let root = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); let external = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng); - - let mut builder = Biscuit::builder(); - let external_pub = hex::encode(external.public().to_bytes()); - builder - .add_check( + let biscuit1 = Biscuit::builder() + .check( format!("check if external_fact(\"hello\") trusting ed25519/{external_pub}").as_str(), ) - .unwrap(); - - let biscuit1 = builder + .unwrap() .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); @@ -27,21 +25,30 @@ fn main() { let serialized_req = biscuit1.third_party_request().unwrap().serialize().unwrap(); let req = biscuit_auth::ThirdPartyRequest::deserialize(&serialized_req).unwrap(); - let mut builder = BlockBuilder::new(); - builder.add_fact("external_fact(\"hello\")").unwrap(); + let builder = BlockBuilder::new() + .fact("external_fact(\"hello\")") + .unwrap(); let res = req.create_block(&external.private(), builder).unwrap(); let biscuit2 = biscuit1.append_third_party(external.public(), res).unwrap(); println!("biscuit2: {}", biscuit2); - let mut authorizer = biscuit1.authorizer().unwrap(); - authorizer.allow().unwrap(); + let mut authorizer = AuthorizerBuilder::new() + .token(&biscuit1) + .allow_all() + .build() + .unwrap(); + println!("authorize biscuit1:\n{:?}", authorizer.authorize()); println!("world:\n{}", authorizer.print_world()); - let mut authorizer = biscuit2.authorizer().unwrap(); - authorizer.allow().unwrap(); + let mut authorizer = AuthorizerBuilder::new() + .token(&biscuit2) + .allow_all() + .build() + .unwrap(); + println!("authorize biscuit2:\n{:?}", authorizer.authorize()); println!("world:\n{}", authorizer.print_world()); } diff --git a/biscuit-auth/examples/verifying_printer.rs b/biscuit-auth/examples/verifying_printer.rs index cb55df40..177b560a 100644 --- a/biscuit-auth/examples/verifying_printer.rs +++ b/biscuit-auth/examples/verifying_printer.rs @@ -1,4 +1,4 @@ -use biscuit_auth::PublicKey; +use biscuit_auth::{builder::AuthorizerBuilder, builder_ext::AuthorizerExt, PublicKey}; fn main() { let mut args = std::env::args(); @@ -25,8 +25,11 @@ fn main() { } println!("token:\n{}", token); - let mut authorizer = token.authorizer().unwrap(); - authorizer.allow().unwrap(); + let mut authorizer = AuthorizerBuilder::new() + .token(&token) + .allow_all() + .build() + .unwrap(); println!("authorizer result: {:?}", authorizer.authorize()); println!("authorizer world:\n{}", authorizer.print_world()); diff --git a/biscuit-auth/src/lib.rs b/biscuit-auth/src/lib.rs index f5719a3a..e1d3b4bb 100644 --- a/biscuit-auth/src/lib.rs +++ b/biscuit-auth/src/lib.rs @@ -88,7 +88,7 @@ //! // - one for /a/file1.txt and a write operation //! // - one for /a/file2.txt and a read operation //! -//! let v1 = authorizer!(r#" +//! let mut v1 = authorizer!(r#" //! resource("/a/file1.txt"); //! operation("read"); //! @@ -101,26 +101,32 @@ //! // explicit catch-all deny. here it is not necessary: if no policy //! // matches, a default deny applies //! deny if true; -//! "#); +//! "#) +//! .token(&biscuit2) +//! .build()?; //! //! let mut v2 = authorizer!(r#" //! resource("/a/file1.txt"); //! operation("write"); //! allow if right("/a/file1.txt", "write"); -//! "#); -//! +//! "#) +//! .token(&biscuit2) +//! .build()?; +//! //! let mut v3 = authorizer!(r#" //! resource("/a/file2.txt"); //! operation("read"); //! allow if right("/a/file2.txt", "read"); -//! "#); +//! "#) +//! .token(&biscuit2) +//! .build()?; //! //! // the token restricts to read operations: -//! assert!(biscuit2.authorize(&v1).is_ok()); +//! assert!(v1.authorize().is_ok()); //! // the second verifier requested a read operation -//! assert!(biscuit2.authorize(&v2).is_err()); +//! assert!(v2.authorize().is_err()); //! // the third verifier requests /a/file2.txt -//! assert!(biscuit2.authorize(&v3).is_err()); +//! assert!(v3.authorize().is_err()); //! //! Ok(()) //! } diff --git a/biscuit-auth/src/macros.rs b/biscuit-auth/src/macros.rs index cbd47669..9a24da99 100644 --- a/biscuit-auth/src/macros.rs +++ b/biscuit-auth/src/macros.rs @@ -26,7 +26,7 @@ //! expiration = SystemTime::now() + Duration::from_secs(86_400), //! )).expect("Failed to append block"); //! -//! new_biscuit.authorize(&authorizer!( +//! authorizer!( //! r#" //! time({now}); //! operation({operation}); @@ -42,7 +42,12 @@ //! operation = "read", //! resource = "file1", //! user_id = "1234", -//! )).expect("Failed to authorize biscuit"); +//! ) +//! .token(&new_biscuit) +//! .build() +//! .expect("failed to build the authorizer") +//! .authorize() +//! .expect("Failed to authorize biscuit"); //! ``` /// Create an `Authorizer` from a datalog string and optional parameters. @@ -78,8 +83,8 @@ pub use biscuit_quote::authorizer; /// now = SystemTime::now() /// ); /// -/// authorizer_merge!( -/// &mut b, +/// b = authorizer_merge!( +/// b, /// r#" /// allow if true; /// "# @@ -128,8 +133,8 @@ pub use biscuit_quote::biscuit; /// user_id = "1234" /// ); /// -/// biscuit_merge!( -/// &mut b, +/// b = biscuit_merge!( +/// b, /// r#" /// check if time($time), $time < {expiration} /// "#, @@ -173,8 +178,8 @@ pub use biscuit_quote::block; /// user_id = "1234" /// ); /// -/// block_merge!( -/// &mut b, +/// b = block_merge!( +/// b, /// r#" /// check if user($id); /// "# diff --git a/biscuit-auth/src/token/authorizer.rs b/biscuit-auth/src/token/authorizer.rs index f43f1d01..278b6943 100644 --- a/biscuit-auth/src/token/authorizer.rs +++ b/biscuit-auth/src/token/authorizer.rs @@ -1,17 +1,11 @@ //! Authorizer structure and associated functions -use super::builder::{ - constrained_rule, date, fact, pred, rule, string, var, Binary, BlockBuilder, Check, Expression, - Fact, Op, Policy, PolicyKind, Rule, Scope, Term, -}; -use super::builder_ext::{AuthorizerExt, BuilderExt}; +use super::builder::{AuthorizerBuilder, BlockBuilder, Check, Fact, Policy, PolicyKind, Rule}; use super::{Biscuit, Block}; -use crate::builder::{self, CheckKind, Convert}; -use crate::crypto::PublicKey; -use crate::datalog::{self, ExternFunc, Origin, RunLimits, SymbolTable, TrustedOrigins}; +use crate::builder::{CheckKind, Convert}; +use crate::datalog::{self, ExternFunc, Origin, RunLimits, TrustedOrigins}; use crate::error; use crate::time::Instant; use crate::token; -use biscuit_parser::parser::parse_source; use prost::Message; use std::collections::{BTreeMap, HashSet}; use std::time::Duration; @@ -20,7 +14,6 @@ use std::{ convert::{TryFrom, TryInto}, default::Default, fmt::Write, - time::SystemTime, }; mod snapshot; @@ -30,23 +23,20 @@ mod snapshot; /// can be created from [Biscuit::authorizer] or [Authorizer::new] #[derive(Clone)] pub struct Authorizer { - authorizer_block_builder: BlockBuilder, - world: datalog::World, + pub(crate) authorizer_block_builder: BlockBuilder, + pub(crate) world: datalog::World, pub(crate) symbols: datalog::SymbolTable, - token_origins: TrustedOrigins, - policies: Vec, - blocks: Option>, - public_key_to_block_id: HashMap>, - limits: AuthorizerLimits, - execution_time: Duration, + pub(crate) token_origins: TrustedOrigins, + pub(crate) policies: Vec, + pub(crate) blocks: Option>, + pub(crate) public_key_to_block_id: HashMap>, + pub(crate) limits: AuthorizerLimits, + pub(crate) execution_time: Duration, } impl Authorizer { pub(crate) fn from_token(token: &Biscuit) -> Result { - let mut v = Authorizer::new(); - v.add_token(token)?; - - Ok(v) + AuthorizerBuilder::new().token(token).build() } /// creates a new empty authorizer @@ -58,7 +48,7 @@ impl Authorizer { /// In the latter case, we can create an empty authorizer, load it /// with the facts, rules and checks, and each time a token must be checked, /// clone the authorizer and load the token with [`Authorizer::add_token`] - pub fn new() -> Self { + fn new() -> Self { let world = datalog::World::new(); let symbols = super::default_symbol_table(); let authorizer_block_builder = BlockBuilder::new(); @@ -81,106 +71,6 @@ impl Authorizer { AuthorizerPolicies::deserialize(data)?.try_into() } - /// add a token to an empty authorizer - pub fn add_token(&mut self, token: &Biscuit) -> Result<(), error::Token> { - if self.blocks.is_some() { - return Err(error::Logic::AuthorizerNotEmpty.into()); - } - - for (i, block) in token.container.blocks.iter().enumerate() { - if let Some(sig) = block.external_signature.as_ref() { - let new_key_id = self.symbols.public_keys.insert(&sig.public_key); - - self.public_key_to_block_id - .entry(new_key_id as usize) - .or_default() - .push(i + 1); - } - } - - let mut blocks = Vec::new(); - - for i in 0..token.block_count() { - let mut block = token.block(i)?; - - self.load_and_translate_block(&mut block, i, &token.symbols)?; - - blocks.push(block); - } - - self.blocks = Some(blocks); - self.token_origins = TrustedOrigins::from_scopes( - &[token::Scope::Previous], - &TrustedOrigins::default(), - token.block_count(), - &self.public_key_to_block_id, - ); - - Ok(()) - } - - /// we need to modify the block loaded from the token, because the authorizer's and the token's symbol table can differ - fn load_and_translate_block( - &mut self, - block: &mut Block, - i: usize, - token_symbols: &SymbolTable, - ) -> Result<(), error::Token> { - // if it is a 3rd party block, it should not affect the main symbol table - let block_symbols = if i == 0 || block.external_key.is_none() { - token_symbols.clone() - } else { - block.symbols.clone() - }; - - let mut block_origin = Origin::default(); - block_origin.insert(i); - - for scope in block.scopes.iter_mut() { - *scope = builder::Scope::convert_from(scope, &block_symbols) - .map(|s| s.convert(&mut self.symbols))?; - } - - let block_trusted_origins = TrustedOrigins::from_scopes( - &block.scopes, - &TrustedOrigins::default(), - i, - &self.public_key_to_block_id, - ); - - for fact in block.facts.iter_mut() { - *fact = Fact::convert_from(fact, &block_symbols)?.convert(&mut self.symbols); - self.world.facts.insert(&block_origin, fact.clone()); - } - - for rule in block.rules.iter_mut() { - if let Err(_message) = rule.validate_variables(&block_symbols) { - return Err( - error::Logic::InvalidBlockRule(0, block_symbols.print_rule(rule)).into(), - ); - } - *rule = rule.translate(&block_symbols, &mut self.symbols)?; - - let rule_trusted_origins = TrustedOrigins::from_scopes( - &rule.scopes, - &block_trusted_origins, - i, - &self.public_key_to_block_id, - ); - - self.world - .rules - .insert(i, &rule_trusted_origins, rule.clone()); - } - - for check in block.checks.iter_mut() { - let c = Check::convert_from(check, &block_symbols)?; - *check = c.convert(&mut self.symbols); - } - - Ok(()) - } - /// serializes a authorizer's content /// /// you can use this to save a set of policies and load them quickly before @@ -201,188 +91,6 @@ impl Authorizer { }) } - /// Add the rules, facts, checks, and policies of another `Authorizer`. - /// If a token has already been added to `other`, it is not merged into `self`. - pub fn merge(&mut self, mut other: Authorizer) { - self.merge_block(other.authorizer_block_builder); - self.policies.append(&mut other.policies); - } - - /// Add the rules, facts, and checks of another `BlockBuilder`. - pub fn merge_block(&mut self, other: BlockBuilder) { - self.authorizer_block_builder.merge(other) - } - - pub fn add_fact>(&mut self, fact: F) -> Result<(), error::Token> - where - error::Token: From<>::Error>, - { - self.authorizer_block_builder.add_fact(fact) - } - - pub fn add_rule>(&mut self, rule: Ru) -> Result<(), error::Token> - where - error::Token: From<>::Error>, - { - self.authorizer_block_builder.add_rule(rule) - } - - pub fn add_check>(&mut self, check: C) -> Result<(), error::Token> - where - error::Token: From<>::Error>, - { - self.authorizer_block_builder.add_check(check) - } - - /// adds some datalog code to the authorizer - /// - /// ```rust - /// extern crate biscuit_auth as biscuit; - /// - /// use biscuit::Authorizer; - /// - /// let mut authorizer = Authorizer::new(); - /// - /// authorizer.add_code(r#" - /// resource("/file1.txt"); - /// - /// check if user(1234); - /// - /// // default allow - /// allow if true; - /// "#).expect("should parse correctly"); - /// ``` - pub fn add_code>(&mut self, source: T) -> Result<(), error::Token> { - self.add_code_with_params(source, HashMap::new(), HashMap::new()) - } - - pub fn add_code_with_params>( - &mut self, - source: T, - params: HashMap, - scope_params: HashMap, - ) -> Result<(), error::Token> { - let source = source.as_ref(); - - let source_result = parse_source(source).map_err(|e| { - let e2: biscuit_parser::error::LanguageError = e.into(); - e2 - })?; - - for (_, fact) in source_result.facts.into_iter() { - let mut fact: Fact = fact.into(); - for (name, value) in ¶ms { - let res = match fact.set(name, value) { - Ok(_) => Ok(()), - Err(error::Token::Language( - biscuit_parser::error::LanguageError::Parameters { - missing_parameters, .. - }, - )) if missing_parameters.is_empty() => Ok(()), - Err(e) => Err(e), - }; - res?; - } - fact.validate()?; - self.authorizer_block_builder.facts.push(fact); - } - - for (_, rule) in source_result.rules.into_iter() { - let mut rule: Rule = rule.into(); - for (name, value) in ¶ms { - let res = match rule.set(name, value) { - Ok(_) => Ok(()), - Err(error::Token::Language( - biscuit_parser::error::LanguageError::Parameters { - missing_parameters, .. - }, - )) if missing_parameters.is_empty() => Ok(()), - Err(e) => Err(e), - }; - res?; - } - for (name, value) in &scope_params { - let res = match rule.set_scope(name, *value) { - Ok(_) => Ok(()), - Err(error::Token::Language( - biscuit_parser::error::LanguageError::Parameters { - missing_parameters, .. - }, - )) if missing_parameters.is_empty() => Ok(()), - Err(e) => Err(e), - }; - res?; - } - rule.validate_parameters()?; - self.authorizer_block_builder.rules.push(rule); - } - - for (_, check) in source_result.checks.into_iter() { - let mut check: Check = check.into(); - for (name, value) in ¶ms { - let res = match check.set(name, value) { - Ok(_) => Ok(()), - Err(error::Token::Language( - biscuit_parser::error::LanguageError::Parameters { - missing_parameters, .. - }, - )) if missing_parameters.is_empty() => Ok(()), - Err(e) => Err(e), - }; - res?; - } - for (name, value) in &scope_params { - let res = match check.set_scope(name, *value) { - Ok(_) => Ok(()), - Err(error::Token::Language( - biscuit_parser::error::LanguageError::Parameters { - missing_parameters, .. - }, - )) if missing_parameters.is_empty() => Ok(()), - Err(e) => Err(e), - }; - res?; - } - check.validate_parameters()?; - self.authorizer_block_builder.checks.push(check); - } - for (_, policy) in source_result.policies.into_iter() { - let mut policy: Policy = policy.into(); - for (name, value) in ¶ms { - let res = match policy.set(name, value) { - Ok(_) => Ok(()), - Err(error::Token::Language( - biscuit_parser::error::LanguageError::Parameters { - missing_parameters, .. - }, - )) if missing_parameters.is_empty() => Ok(()), - Err(e) => Err(e), - }; - res?; - } - for (name, value) in &scope_params { - let res = match policy.set_scope(name, *value) { - Ok(_) => Ok(()), - Err(error::Token::Language( - biscuit_parser::error::LanguageError::Parameters { - missing_parameters, .. - }, - )) if missing_parameters.is_empty() => Ok(()), - Err(e) => Err(e), - }; - res?; - } - policy.validate_parameters()?; - self.policies.push(policy); - } - - Ok(()) - } - - pub fn add_scope(&mut self, scope: Scope) { - self.authorizer_block_builder.add_scope(scope); - } - /// Returns the runtime limits of the authorizer /// /// Those limits cover all the executions under the `authorize`, `query` and `query_all` methods @@ -390,33 +98,11 @@ impl Authorizer { &self.limits } - /// Sets the runtime limits of the authorizer - /// - /// Those limits cover all the executions under the `authorize`, `query` and `query_all` methods - pub fn set_limits(&mut self, limits: AuthorizerLimits) { - self.limits = limits; - } - /// Returns the currently registered external functions pub fn external_funcs(&self) -> &HashMap { &self.world.extern_funcs } - /// Replaces the registered external functions - pub fn set_extern_funcs(&mut self, extern_funcs: HashMap) { - self.world.extern_funcs = extern_funcs; - } - - /// Registers the provided external functions (possibly replacing already registered functions) - pub fn register_extern_funcs(&mut self, extern_funcs: HashMap) { - self.world.extern_funcs.extend(extern_funcs); - } - - /// Registers the provided external function (possibly replacing an already registered function) - pub fn register_extern_func(&mut self, name: String, func: ExternFunc) { - self.world.extern_funcs.insert(name, func); - } - /// run a query over the authorizer's Datalog engine to gather data /// /// ```rust @@ -424,10 +110,11 @@ impl Authorizer { /// # use biscuit_auth::Biscuit; /// # use biscuit_auth::builder::Algorithm; /// let keypair = KeyPair::new(Algorithm::Ed25519); - /// let mut builder = Biscuit::builder(); - /// builder.add_fact("user(\"John Doe\", 42)"); - /// - /// let biscuit = builder.build(&keypair).unwrap(); + /// let biscuit = Biscuit::builder() + /// .fact("user(\"John Doe\", 42)") + /// .expect("parse error") + /// .build(&keypair) + /// .unwrap(); /// /// let mut authorizer = biscuit.authorizer().unwrap(); /// let res: Vec<(String, i64)> = authorizer.query("data($name, $id) <- user($name, $id)").unwrap(); @@ -478,7 +165,7 @@ impl Authorizer { fn query_inner, E: Into>( &mut self, rule: datalog::Rule, - limits: AuthorizerLimits, + _limits: AuthorizerLimits, ) -> Result, error::Token> { let rule_trusted_origins = TrustedOrigins::from_scopes( &rule.scopes, @@ -490,7 +177,6 @@ impl Authorizer { &self.public_key_to_block_id, ); - self.world.run_with_limits(&self.symbols, limits)?; let res = self .world .query_rule(rule, usize::MAX, &rule_trusted_origins, &self.symbols)?; @@ -515,10 +201,11 @@ impl Authorizer { /// # use biscuit_auth::Biscuit; /// # use biscuit_auth::builder::Algorithm; /// let keypair = KeyPair::new(Algorithm::Ed25519,); - /// let mut builder = Biscuit::builder(); - /// builder.add_fact("user(\"John Doe\", 42)"); - /// - /// let biscuit = builder.build(&keypair).unwrap(); + /// let biscuit = Biscuit::builder() + /// .fact("user(\"John Doe\", 42)") + /// .expect("parse error") + /// .build(&keypair) + /// .unwrap(); /// /// let mut authorizer = biscuit.authorizer().unwrap(); /// let res: Vec<(String, i64)> = authorizer.query_all("data($name, $id) <- user($name, $id)").unwrap(); @@ -572,10 +259,8 @@ impl Authorizer { fn query_all_inner, E: Into>( &mut self, rule: datalog::Rule, - limits: AuthorizerLimits, + _limits: AuthorizerLimits, ) -> Result, error::Token> { - self.world.run_with_limits(&self.symbols, limits)?; - let rule_trusted_origins = if rule.scopes.is_empty() { self.token_origins.clone() } else { @@ -605,34 +290,6 @@ impl Authorizer { .collect::, _>>() } - /// adds a fact with the current time - pub fn set_time(&mut self) { - let fact = fact("time", &[date(&SystemTime::now())]); - self.authorizer_block_builder.add_fact(fact).unwrap(); - } - - /// add a policy to the authorizer - pub fn add_policy>(&mut self, policy: P) -> Result<(), error::Token> - where - error::Token: From<

>::Error>, - { - let policy = policy.try_into()?; - policy.validate_parameters()?; - self.policies.push(policy); - Ok(()) - } - - /// todo remove, it's covered in BuilderExt - /// adds a `allow if true` policy - pub fn allow(&mut self) -> Result<(), error::Token> { - self.add_policy("allow if true") - } - - /// adds a `deny if true` policy - pub fn deny(&mut self) -> Result<(), error::Token> { - self.add_policy("deny if true") - } - /// returns the elapsed execution time pub fn execution_time(&self) -> Duration { self.execution_time @@ -663,7 +320,6 @@ impl Authorizer { self.authorize_with_limits(limits) } - /// TODO: consume the input to prevent further direct use /// verifies the checks and policies /// /// on error, this can return a list of all the failed checks or deny policy @@ -680,10 +336,9 @@ impl Authorizer { result } - fn authorize_inner(&mut self, mut limits: AuthorizerLimits) -> Result { + fn authorize_inner(&mut self, limits: AuthorizerLimits) -> Result { let start = Instant::now(); let time_limit = start + limits.max_time; - let mut current_iterations = self.world.iterations; let mut errors = vec![]; let mut policy_result: Option> = None; @@ -706,45 +361,6 @@ impl Authorizer { &self.public_key_to_block_id, ); - for fact in &self.authorizer_block_builder.facts { - self.world - .facts - .insert(&authorizer_origin, fact.convert(&mut self.symbols)); - } - - for rule in &self.authorizer_block_builder.rules { - let rule = rule.convert(&mut self.symbols); - - let rule_trusted_origins = TrustedOrigins::from_scopes( - &rule.scopes, - &authorizer_trusted_origins, - usize::MAX, - &self.public_key_to_block_id, - ); - - self.world - .rules - .insert(usize::MAX, &rule_trusted_origins, rule); - } - - limits.max_time = time_limit - Instant::now(); - self.world.run_with_limits(&self.symbols, limits.clone())?; - - let authorizer_scopes: Vec = self - .authorizer_block_builder - .scopes - .clone() - .iter() - .map(|s| s.convert(&mut self.symbols)) - .collect(); - - let authorizer_trusted_origins = TrustedOrigins::from_scopes( - &authorizer_scopes, - &TrustedOrigins::default(), - usize::MAX, - &self.public_key_to_block_id, - ); - for (i, check) in self.authorizer_block_builder.checks.iter().enumerate() { let c = check.convert(&mut self.symbols); let mut successful = false; @@ -897,12 +513,6 @@ impl Authorizer { &self.public_key_to_block_id, ); - limits.max_time = time_limit - Instant::now(); - limits.max_iterations -= self.world.iterations - current_iterations; - current_iterations = self.world.iterations; - - self.world.run_with_limits(&self.symbols, limits.clone())?; - for (j, check) in block.checks.iter().enumerate() { let mut successful = false; @@ -991,23 +601,21 @@ impl Authorizer { } } - let mut facts = self + let facts = self .world .facts .iter_all() .map(|f| Fact::convert_from(f.1, &self.symbols)) .collect::, error::Format>>() .unwrap(); - facts.extend(self.authorizer_block_builder.facts.clone()); - let mut rules = self + let rules = self .world .rules .iter_all() .map(|r| Rule::convert_from(r.1, &self.symbols)) .collect::, error::Format>>() .unwrap(); - rules.extend(self.authorizer_block_builder.rules.clone()); (facts, rules, checks, self.policies.clone()) } @@ -1015,6 +623,9 @@ impl Authorizer { pub fn dump_code(&self) -> String { let (facts, rules, checks, policies) = self.dump(); let mut f = String::new(); + + let mut facts = facts.into_iter().map(|f| f.to_string()).collect::>(); + facts.sort(); for fact in &facts { let _ = writeln!(f, "{fact};"); } @@ -1057,24 +668,6 @@ impl std::fmt::Display for Authorizer { all_facts.insert(origin, facts); } - let builder_facts = self - .authorizer_block_builder - .facts - .iter() - .map(|f| f.to_string()) - .collect::>(); - has_facts = has_facts || !builder_facts.is_empty(); - let mut authorizer_origin = Origin::default(); - authorizer_origin.insert(usize::MAX); - match all_facts.get_mut(&authorizer_origin) { - Some(e) => { - e.extend(builder_facts); - } - None => { - all_facts.insert(&authorizer_origin, builder_facts); - } - } - if has_facts { writeln!(f, "// Facts:")?; } @@ -1108,19 +701,6 @@ impl std::fmt::Display for Authorizer { } } - let builder_rules = self - .authorizer_block_builder - .rules - .iter() - .map(|rule| rule.to_string()) - .collect::>(); - has_rules = has_rules || !builder_rules.is_empty(); - - rules_map - .entry(usize::MAX) - .or_default() - .extend(builder_rules); - if has_rules { writeln!(f, "// Rules:")?; } @@ -1213,15 +793,16 @@ impl TryFrom for Authorizer { let mut authorizer = Self::new(); for fact in facts.into_iter() { - authorizer.authorizer_block_builder.add_fact(fact)?; + authorizer.authorizer_block_builder = authorizer.authorizer_block_builder.fact(fact)?; } for rule in rules.into_iter() { - authorizer.authorizer_block_builder.add_rule(rule)?; + authorizer.authorizer_block_builder = authorizer.authorizer_block_builder.rule(rule)?; } for check in checks.into_iter() { - authorizer.authorizer_block_builder.add_check(check)?; + authorizer.authorizer_block_builder = + authorizer.authorizer_block_builder.check(check)?; } for policy in policies { @@ -1270,116 +851,15 @@ impl AuthorizerPolicies { pub type AuthorizerLimits = RunLimits; -impl BuilderExt for Authorizer { - fn add_resource(&mut self, name: &str) { - let f = fact("resource", &[string(name)]); - self.add_fact(f).unwrap(); - } - fn check_resource(&mut self, name: &str) { - self.add_check(Check { - queries: vec![rule( - "resource_check", - &[string("resource_check")], - &[pred("resource", &[string(name)])], - )], - kind: CheckKind::One, - }) - .unwrap(); - } - fn add_operation(&mut self, name: &str) { - let f = fact("operation", &[string(name)]); - self.add_fact(f).unwrap(); - } - fn check_operation(&mut self, name: &str) { - self.add_check(Check { - queries: vec![rule( - "operation_check", - &[string("operation_check")], - &[pred("operation", &[string(name)])], - )], - kind: CheckKind::One, - }) - .unwrap(); - } - fn check_resource_prefix(&mut self, prefix: &str) { - let check = constrained_rule( - "prefix", - &[var("resource")], - &[pred("resource", &[var("resource")])], - &[Expression { - ops: vec![ - Op::Value(var("resource")), - Op::Value(string(prefix)), - Op::Binary(Binary::Prefix), - ], - }], - ); - - self.add_check(Check { - queries: vec![check], - kind: CheckKind::One, - }) - .unwrap(); - } - - fn check_resource_suffix(&mut self, suffix: &str) { - let check = constrained_rule( - "suffix", - &[var("resource")], - &[pred("resource", &[var("resource")])], - &[Expression { - ops: vec![ - Op::Value(var("resource")), - Op::Value(string(suffix)), - Op::Binary(Binary::Suffix), - ], - }], - ); - - self.add_check(Check { - queries: vec![check], - kind: CheckKind::One, - }) - .unwrap(); - } - - fn check_expiration_date(&mut self, exp: SystemTime) { - let check = constrained_rule( - "expiration", - &[var("time")], - &[pred("time", &[var("time")])], - &[Expression { - ops: vec![ - Op::Value(var("time")), - Op::Value(date(&exp)), - Op::Binary(Binary::LessOrEqual), - ], - }], - ); - - self.add_check(Check { - queries: vec![check], - kind: CheckKind::One, - }) - .unwrap(); - } -} - -impl AuthorizerExt for Authorizer { - fn add_allow_all(&mut self) { - self.add_policy("allow if true").unwrap(); - } - fn add_deny_all(&mut self) { - self.add_policy("deny if true").unwrap(); - } -} - #[cfg(test)] mod tests { use std::time::Duration; + use datalog::{SymbolTable, World}; + use token::builder::{self, load_and_translate_block, var}; use token::{public_keys::PublicKeys, DATALOG_3_1}; + use crate::PublicKey; use crate::{ builder::{Algorithm, BiscuitBuilder, BlockBuilder}, KeyPair, @@ -1389,8 +869,11 @@ mod tests { #[test] fn empty_authorizer() { - let mut authorizer = Authorizer::new(); - authorizer.add_policy("allow if true").unwrap(); + let mut authorizer = AuthorizerBuilder::new() + .policy("allow if true") + .unwrap() + .build() + .unwrap(); assert_eq!( authorizer.authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -1402,7 +885,6 @@ mod tests { #[test] fn parameter_substitution() { - let mut authorizer = Authorizer::new(); let mut params = HashMap::new(); params.insert("p1".to_string(), "value".into()); params.insert("p2".to_string(), 0i64.into()); @@ -1417,8 +899,8 @@ mod tests { ) .unwrap(), ); - authorizer - .add_code_with_params( + let _authorizer = AuthorizerBuilder::new() + .code_with_params( r#" fact({p1}, "value"); rule($var, {p2}) <- fact($var, {p2}); @@ -1428,75 +910,69 @@ mod tests { params, scope_params, ) + .unwrap() + .build() .unwrap(); } #[test] fn forbid_unbound_parameters() { - let mut builder = Authorizer::new(); + let builder = AuthorizerBuilder::new(); let mut fact = Fact::try_from("fact({p1}, {p4})").unwrap(); fact.set("p1", "hello").unwrap(); - let res = builder.add_fact(fact); + let res = builder.clone().fact(fact); assert_eq!( - res, - Err(error::Token::Language( - biscuit_parser::error::LanguageError::Parameters { - missing_parameters: vec!["p4".to_string()], - unused_parameters: vec![], - } - )) + res.unwrap_err(), + error::Token::Language(biscuit_parser::error::LanguageError::Parameters { + missing_parameters: vec!["p4".to_string()], + unused_parameters: vec![], + }) ); let mut rule = Rule::try_from( "fact($var1, {p2}) <- f1($var1, $var3), f2({p2}, $var3, {p4}), $var3.starts_with({p2})", ) .unwrap(); rule.set("p2", "hello").unwrap(); - let res = builder.add_rule(rule); + let res = builder.clone().rule(rule); assert_eq!( - res, - Err(error::Token::Language( - biscuit_parser::error::LanguageError::Parameters { - missing_parameters: vec!["p4".to_string()], - unused_parameters: vec![], - } - )) + res.unwrap_err(), + error::Token::Language(biscuit_parser::error::LanguageError::Parameters { + missing_parameters: vec!["p4".to_string()], + unused_parameters: vec![], + }) ); let mut check = Check::try_from("check if {p4}, {p3}").unwrap(); check.set("p3", true).unwrap(); - let res = builder.add_check(check); + let res = builder.clone().check(check); assert_eq!( - res, - Err(error::Token::Language( - biscuit_parser::error::LanguageError::Parameters { - missing_parameters: vec!["p4".to_string()], - unused_parameters: vec![], - } - )) + res.unwrap_err(), + error::Token::Language(biscuit_parser::error::LanguageError::Parameters { + missing_parameters: vec!["p4".to_string()], + unused_parameters: vec![], + }) ); let mut policy = Policy::try_from("allow if {p4}, {p3}").unwrap(); policy.set("p3", true).unwrap(); - let res = builder.add_policy(policy); + let res = builder.clone().policy(policy); assert_eq!( - res, - Err(error::Token::Language( - biscuit_parser::error::LanguageError::Parameters { - missing_parameters: vec!["p4".to_string()], - unused_parameters: vec![], - } - )) + res.unwrap_err(), + error::Token::Language(biscuit_parser::error::LanguageError::Parameters { + missing_parameters: vec!["p4".to_string()], + unused_parameters: vec![], + }) ); } #[test] fn forbid_unbound_parameters_in_add_code() { - let mut builder = Authorizer::new(); + let builder = AuthorizerBuilder::new(); let mut params = HashMap::new(); params.insert("p1".to_string(), "hello".into()); params.insert("p2".to_string(), 1i64.into()); params.insert("p4".to_string(), "this will be ignored".into()); - let res = builder.add_code_with_params( + let res = builder.code_with_params( r#"fact({p1}, "value"); rule($head_var) <- f1($head_var), {p2} > 0; check if {p3}; @@ -1507,13 +983,11 @@ mod tests { ); assert_eq!( - res, - Err(error::Token::Language( - biscuit_parser::error::LanguageError::Parameters { - missing_parameters: vec!["p3".to_string()], - unused_parameters: vec![], - } - )) + res.unwrap_err(), + error::Token::Language(biscuit_parser::error::LanguageError::Parameters { + missing_parameters: vec!["p3".to_string()], + unused_parameters: vec![], + }) ) } @@ -1522,10 +996,11 @@ mod tests { use crate::Biscuit; use crate::KeyPair; let keypair = KeyPair::new(Algorithm::Ed25519); - let mut builder = Biscuit::builder(); - builder.add_fact("user(\"John Doe\", 42)").unwrap(); - - let biscuit = builder.build(&keypair).unwrap(); + let biscuit = Biscuit::builder() + .fact("user(\"John Doe\", 42)") + .unwrap() + .build(&keypair) + .unwrap(); let mut authorizer = biscuit.authorizer().unwrap(); let res: Vec<(String, i64)> = authorizer @@ -1542,10 +1017,11 @@ mod tests { use crate::Biscuit; use crate::KeyPair; let keypair = KeyPair::new(Algorithm::Ed25519); - let mut builder = Biscuit::builder(); - builder.add_fact("user(\"John Doe\")").unwrap(); - - let biscuit = builder.build(&keypair).unwrap(); + let biscuit = Biscuit::builder() + .fact("user(\"John Doe\")") + .unwrap() + .build(&keypair) + .unwrap(); let mut authorizer = biscuit.authorizer().unwrap(); let res: Vec<(String,)> = authorizer.query("data($name) <- user($name)").unwrap(); @@ -1559,26 +1035,25 @@ mod tests { let root = KeyPair::new(Algorithm::Ed25519); let external = KeyPair::new(Algorithm::Ed25519); - let mut builder = Biscuit::builder(); let mut scope_params = HashMap::new(); scope_params.insert("external_pub".to_string(), external.public()); - builder - .add_code_with_params( + + let biscuit1 = Biscuit::builder() + .code_with_params( r#"right("read"); - check if group("admin") trusting {external_pub}; - "#, + check if group("admin") trusting {external_pub}; + "#, HashMap::new(), scope_params, ) + .unwrap() + .build(&root) .unwrap(); - let biscuit1 = builder.build(&root).unwrap(); - let req = biscuit1.third_party_request().unwrap(); - let mut builder = BlockBuilder::new(); - builder - .add_code( + let builder = BlockBuilder::new() + .code( r#"group("admin"); check if right("read"); "#, @@ -1589,15 +1064,15 @@ mod tests { let serialized = biscuit2.to_vec().unwrap(); let biscuit2 = Biscuit::from(serialized, root.public()).unwrap(); - let mut authorizer = Authorizer::new(); + let builder = AuthorizerBuilder::new(); let external2 = KeyPair::new(Algorithm::Ed25519); let mut scope_params = HashMap::new(); scope_params.insert("external".to_string(), external.public()); scope_params.insert("external2".to_string(), external2.public()); - authorizer - .add_code_with_params( + let mut authorizer = builder + .code_with_params( r#" // this rule trusts both the third-party block and the authority, and can access facts // from both @@ -1616,18 +1091,18 @@ mod tests { HashMap::new(), scope_params, ) + .unwrap() + .token(&biscuit2) + .limits(AuthorizerLimits { + max_time: Duration::from_millis(10), //Set 10 milliseconds as the maximum time allowed for the authorization due to "cheap" worker on GitHub Actions + ..Default::default() + }) + .build() .unwrap(); - authorizer.add_token(&biscuit2).unwrap(); - println!("token:\n{}", biscuit2); println!("world:\n{}", authorizer.print_world()); - authorizer.set_limits(AuthorizerLimits { - max_time: Duration::from_millis(10), //Set 10 milliseconds as the maximum time allowed for the authorization due to "cheap" worker on GitHub Actions - ..Default::default() - }); - let res = authorizer.authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), ..Default::default() @@ -1741,21 +1216,21 @@ mod tests { fn authorizer_display_before_and_after_authorization() { let root = KeyPair::new(Algorithm::Ed25519); - let mut token_builder = BiscuitBuilder::new(); - token_builder - .add_code( + let token = BiscuitBuilder::new() + .code( r#" authority_fact(true); authority_rule($v) <- authority_fact($v); check if authority_fact(true), authority_rule(true); "#, ) + .unwrap() + .build(&root) .unwrap(); - let token = token_builder.build(&root).unwrap(); - let mut authorizer = token.authorizer().unwrap(); - authorizer - .add_code( + let mut authorizer = AuthorizerBuilder::new() + .token(&token) + .code( r#" authorizer_fact(true); authorizer_rule($v) <- authorizer_fact($v); @@ -1763,6 +1238,8 @@ mod tests { allow if true; "#, ) + .unwrap() + .build() .unwrap(); let output_before_authorization = authorizer.to_string(); @@ -1820,7 +1297,7 @@ allow if true; #[test] fn rule_validate_variables() { - let mut authorizer = Authorizer::new(); + let builder = AuthorizerBuilder::new(); let mut syms = SymbolTable::new(); let rule_name = syms.insert("test"); let pred_name = syms.insert("pred"); @@ -1841,10 +1318,19 @@ allow if true; scopes: vec![], }; + // FIXME assert_eq!( - authorizer - .load_and_translate_block(&mut block, 0, &syms) - .unwrap_err(), + /*builder + .load_and_translate_block(&mut block, 0, &syms)*/ + load_and_translate_block( + &mut block, + 0, + &syms, + &mut SymbolTable::new(), + &mut HashMap::new(), + &mut World::new(), + ) + .unwrap_err(), error::Token::FailedLogic(error::Logic::InvalidBlockRule( 0, "test($unbound) <- pred($any)".to_string() @@ -1852,12 +1338,14 @@ allow if true; ); // broken rules directly added to the authorizer currently don’t trigger any error, but silently fail to generate facts when they match - authorizer - .add_rule(builder::rule( + let mut authorizer = builder + .rule(builder::rule( "test", &[var("unbound")], &[builder::pred("pred", &[builder::var("any")])], )) + .unwrap() + .build() .unwrap(); let res: Vec<(String,)> = authorizer .query(builder::rule( diff --git a/biscuit-auth/src/token/authorizer/snapshot.rs b/biscuit-auth/src/token/authorizer/snapshot.rs index 373aff9f..905da00d 100644 --- a/biscuit-auth/src/token/authorizer/snapshot.rs +++ b/biscuit-auth/src/token/authorizer/snapshot.rs @@ -2,7 +2,7 @@ use prost::Message; use std::{collections::HashMap, time::Duration}; use crate::{ - builder::{BlockBuilder, Convert, Policy}, + builder::{load_and_translate_block, BlockBuilder, Convert, Policy}, datalog::{Origin, RunLimits, TrustedOrigins}, error, format::{ @@ -91,7 +91,14 @@ impl super::Authorizer { .push(i); } - authorizer.load_and_translate_block(&mut block, i, &token_symbols)?; + load_and_translate_block( + &mut block, + i, + &token_symbols, + &mut authorizer.symbols, + &mut public_key_to_block_id, + &mut authorizer.world, + )?; blocks.push(block); } @@ -224,7 +231,7 @@ impl super::Authorizer { } } -fn authorizer_origin_to_proto_origin(origin: &Origin) -> Vec { +pub(crate) fn authorizer_origin_to_proto_origin(origin: &Origin) -> Vec { origin .inner .iter() @@ -242,7 +249,9 @@ fn authorizer_origin_to_proto_origin(origin: &Origin) -> Vec { .collect() } -fn proto_origin_to_authorizer_origin(origins: &[schema::Origin]) -> Result { +pub(crate) fn proto_origin_to_authorizer_origin( + origins: &[schema::Origin], +) -> Result { let mut new_origin = Origin::default(); for origin in origins { diff --git a/biscuit-auth/src/token/builder.rs b/biscuit-auth/src/token/builder.rs index 6af2a438..aa80194a 100644 --- a/biscuit-auth/src/token/builder.rs +++ b/biscuit-auth/src/token/builder.rs @@ -16,6 +16,7 @@ pub use crate::datalog::{ use crate::error; mod algorithm; +mod authorizer; mod biscuit; mod block; mod check; @@ -28,6 +29,7 @@ mod scope; mod term; pub use algorithm::*; +pub use authorizer::*; pub use biscuit::*; pub use block::*; pub use check::*; @@ -249,8 +251,8 @@ mod tests { .unwrap(); let mut scope_params = HashMap::new(); scope_params.insert("pk".to_string(), pubkey); - builder - .add_code_with_params( + builder = builder + .code_with_params( r#"fact({p1}, "value"); rule($head_var) <- f1($head_var), {p2} > 0 trusting {pk}; check if {p3} trusting {pk}; @@ -270,57 +272,51 @@ check if true trusting ed25519/6e9e6d5a75cf0c0e87ec1256b4dfed0ca3ba452912d213fcc #[test] fn forbid_unbound_parameters() { - let mut builder = BlockBuilder::new(); + let builder = BlockBuilder::new(); let mut fact = Fact::try_from("fact({p1}, {p4})").unwrap(); fact.set("p1", "hello").unwrap(); - let res = builder.add_fact(fact); + let res = builder.clone().fact(fact); assert_eq!( - res, - Err(error::Token::Language( - biscuit_parser::error::LanguageError::Parameters { - missing_parameters: vec!["p4".to_string()], - unused_parameters: vec![], - } - )) + res.unwrap_err(), + error::Token::Language(biscuit_parser::error::LanguageError::Parameters { + missing_parameters: vec!["p4".to_string()], + unused_parameters: vec![], + }) ); let mut rule = Rule::try_from( "fact($var1, {p2}) <- f1($var1, $var3), f2({p2}, $var3, {p4}), $var3.starts_with({p2})", ) .unwrap(); rule.set("p2", "hello").unwrap(); - let res = builder.add_rule(rule); + let res = builder.clone().rule(rule); assert_eq!( - res, - Err(error::Token::Language( - biscuit_parser::error::LanguageError::Parameters { - missing_parameters: vec!["p4".to_string()], - unused_parameters: vec![], - } - )) + res.unwrap_err(), + error::Token::Language(biscuit_parser::error::LanguageError::Parameters { + missing_parameters: vec!["p4".to_string()], + unused_parameters: vec![], + }) ); let mut check = Check::try_from("check if {p4}, {p3}").unwrap(); check.set("p3", true).unwrap(); - let res = builder.add_check(check); + let res = builder.clone().check(check); assert_eq!( - res, - Err(error::Token::Language( - biscuit_parser::error::LanguageError::Parameters { - missing_parameters: vec!["p4".to_string()], - unused_parameters: vec![], - } - )) + res.unwrap_err(), + error::Token::Language(biscuit_parser::error::LanguageError::Parameters { + missing_parameters: vec!["p4".to_string()], + unused_parameters: vec![], + }) ); } #[test] fn forbid_unbound_parameters_in_set_code() { - let mut builder = BlockBuilder::new(); + let builder = BlockBuilder::new(); let mut params = HashMap::new(); params.insert("p1".to_string(), "hello".into()); params.insert("p2".to_string(), 1i64.into()); params.insert("p4".to_string(), "this will be ignored".into()); - let res = builder.add_code_with_params( + let res = builder.code_with_params( r#"fact({p1}, "value"); rule($head_var) <- f1($head_var), {p2} > 0; check if {p3}; @@ -330,13 +326,11 @@ check if true trusting ed25519/6e9e6d5a75cf0c0e87ec1256b4dfed0ca3ba452912d213fcc ); assert_eq!( - res, - Err(error::Token::Language( - biscuit_parser::error::LanguageError::Parameters { - missing_parameters: vec!["p3".to_string()], - unused_parameters: vec![], - } - )) - ) + res.unwrap_err(), + error::Token::Language(biscuit_parser::error::LanguageError::Parameters { + missing_parameters: vec!["p3".to_string()], + unused_parameters: vec![], + }) + ); } } diff --git a/biscuit-auth/src/token/builder/authorizer.rs b/biscuit-auth/src/token/builder/authorizer.rs new file mode 100644 index 00000000..aa8a4719 --- /dev/null +++ b/biscuit-auth/src/token/builder/authorizer.rs @@ -0,0 +1,655 @@ +use std::{ + collections::HashMap, + convert::TryInto, + fmt::Write, + time::{Duration, Instant, SystemTime}, +}; + +use biscuit_parser::parser::parse_source; +use prost::Message; + +use crate::{ + builder::Convert, + builder_ext::{AuthorizerExt, BuilderExt}, + datalog::{ExternFunc, Origin, RunLimits, SymbolTable, TrustedOrigins, World}, + error, + format::{ + convert::{ + proto_snapshot_block_to_token_block, token_block_to_proto_snapshot_block, + v2::{policy_to_proto_policy, proto_policy_to_policy}, + }, + schema, + }, + token::{self, default_symbol_table, Block, MAX_SCHEMA_VERSION, MIN_SCHEMA_VERSION}, + Authorizer, AuthorizerLimits, Biscuit, PublicKey, +}; + +use super::{date, fact, BlockBuilder, Check, Fact, Policy, Rule, Scope, Term}; + +#[derive(Clone, Debug, Default)] +pub struct AuthorizerBuilder<'a> { + authorizer_block_builder: BlockBuilder, + policies: Vec, + extern_funcs: HashMap, + limits: AuthorizerLimits, + token: Option<&'a Biscuit>, +} + +impl<'a> AuthorizerBuilder<'a> { + pub fn new() -> AuthorizerBuilder<'a> { + AuthorizerBuilder::default() + } + + pub fn fact>(mut self, fact: F) -> Result + where + error::Token: From<>::Error>, + { + self.authorizer_block_builder = self.authorizer_block_builder.fact(fact)?; + Ok(self) + } + + pub fn rule>(mut self, rule: R) -> Result + where + error::Token: From<>::Error>, + { + self.authorizer_block_builder = self.authorizer_block_builder.rule(rule)?; + Ok(self) + } + + pub fn check>(mut self, check: C) -> Result + where + error::Token: From<>::Error>, + { + self.authorizer_block_builder = self.authorizer_block_builder.check(check)?; + Ok(self) + } + + /// adds some datalog code to the authorizer + /// + /// ```rust + /// extern crate biscuit_auth as biscuit; + /// + /// use biscuit::builder::AuthorizerBuilder; + /// + /// let mut authorizer = AuthorizerBuilder::new() + /// .code(r#" + /// resource("/file1.txt"); + /// + /// check if user(1234); + /// + /// // default allow + /// allow if true; + /// "#) + /// .expect("should parse correctly") + /// .build(); + /// ``` + pub fn code>(self, source: T) -> Result { + self.code_with_params(source, HashMap::new(), HashMap::new()) + } + + /// Add datalog code to the builder, performing parameter subsitution as required + /// Unknown parameters are ignored + pub fn code_with_params>( + mut self, + source: T, + params: HashMap, + scope_params: HashMap, + ) -> Result { + let source = source.as_ref(); + + let source_result = parse_source(source).map_err(|e| { + let e2: biscuit_parser::error::LanguageError = e.into(); + e2 + })?; + + for (_, fact) in source_result.facts.into_iter() { + let mut fact: Fact = fact.into(); + for (name, value) in ¶ms { + let res = match fact.set(name, value) { + Ok(_) => Ok(()), + Err(error::Token::Language( + biscuit_parser::error::LanguageError::Parameters { + missing_parameters, .. + }, + )) if missing_parameters.is_empty() => Ok(()), + Err(e) => Err(e), + }; + res?; + } + fact.validate()?; + self.authorizer_block_builder.facts.push(fact); + } + + for (_, rule) in source_result.rules.into_iter() { + let mut rule: Rule = rule.into(); + for (name, value) in ¶ms { + let res = match rule.set(name, value) { + Ok(_) => Ok(()), + Err(error::Token::Language( + biscuit_parser::error::LanguageError::Parameters { + missing_parameters, .. + }, + )) if missing_parameters.is_empty() => Ok(()), + Err(e) => Err(e), + }; + res?; + } + for (name, value) in &scope_params { + let res = match rule.set_scope(name, *value) { + Ok(_) => Ok(()), + Err(error::Token::Language( + biscuit_parser::error::LanguageError::Parameters { + missing_parameters, .. + }, + )) if missing_parameters.is_empty() => Ok(()), + Err(e) => Err(e), + }; + res?; + } + rule.validate_parameters()?; + self.authorizer_block_builder.rules.push(rule); + } + + for (_, check) in source_result.checks.into_iter() { + let mut check: Check = check.into(); + for (name, value) in ¶ms { + let res = match check.set(name, value) { + Ok(_) => Ok(()), + Err(error::Token::Language( + biscuit_parser::error::LanguageError::Parameters { + missing_parameters, .. + }, + )) if missing_parameters.is_empty() => Ok(()), + Err(e) => Err(e), + }; + res?; + } + for (name, value) in &scope_params { + let res = match check.set_scope(name, *value) { + Ok(_) => Ok(()), + Err(error::Token::Language( + biscuit_parser::error::LanguageError::Parameters { + missing_parameters, .. + }, + )) if missing_parameters.is_empty() => Ok(()), + Err(e) => Err(e), + }; + res?; + } + check.validate_parameters()?; + self.authorizer_block_builder.checks.push(check); + } + for (_, policy) in source_result.policies.into_iter() { + let mut policy: Policy = policy.into(); + for (name, value) in ¶ms { + let res = match policy.set(name, value) { + Ok(_) => Ok(()), + Err(error::Token::Language( + biscuit_parser::error::LanguageError::Parameters { + missing_parameters, .. + }, + )) if missing_parameters.is_empty() => Ok(()), + Err(e) => Err(e), + }; + res?; + } + for (name, value) in &scope_params { + let res = match policy.set_scope(name, *value) { + Ok(_) => Ok(()), + Err(error::Token::Language( + biscuit_parser::error::LanguageError::Parameters { + missing_parameters, .. + }, + )) if missing_parameters.is_empty() => Ok(()), + Err(e) => Err(e), + }; + res?; + } + policy.validate_parameters()?; + self.policies.push(policy); + } + + Ok(self) + } + + pub fn scope(mut self, scope: Scope) -> Self { + self.authorizer_block_builder = self.authorizer_block_builder.scope(scope); + self + } + + /// add a policy to the authorizer + pub fn policy>(mut self, policy: P) -> Result + where + error::Token: From<

>::Error>, + { + let policy = policy.try_into()?; + policy.validate_parameters()?; + self.policies.push(policy); + Ok(self) + } + + /// adds a fact with the current time + pub fn time(mut self) -> Self { + let fact = fact("time", &[date(&SystemTime::now())]); + self.authorizer_block_builder = self.authorizer_block_builder.fact(fact).unwrap(); + self + } + + /// Sets the runtime limits of the authorizer + /// + /// Those limits cover all the executions under the `authorize`, `query` and `query_all` methods + pub fn limits(mut self, limits: AuthorizerLimits) -> Self { + self.limits = limits; + self + } + + /// Replaces the registered external functions + pub fn set_extern_funcs(mut self, extern_funcs: HashMap) -> Self { + self.extern_funcs = extern_funcs; + self + } + + /// Registers the provided external functions (possibly replacing already registered functions) + pub fn register_extern_funcs(mut self, extern_funcs: HashMap) -> Self { + self.extern_funcs.extend(extern_funcs); + self + } + + /// Registers the provided external function (possibly replacing an already registered function) + pub fn register_extern_func(mut self, name: String, func: ExternFunc) -> Self { + self.extern_funcs.insert(name, func); + self + } + + pub fn token(mut self, token: &'a Biscuit) -> Self { + self.token = Some(token); + self + } + + pub fn dump_code(&self) -> String { + let mut f = String::new(); + for fact in &self.authorizer_block_builder.facts { + let _ = writeln!(f, "{fact};"); + } + if !self.authorizer_block_builder.facts.is_empty() { + let _ = writeln!(f); + } + + for rule in &self.authorizer_block_builder.rules { + let _ = writeln!(f, "{rule};"); + } + if !self.authorizer_block_builder.rules.is_empty() { + let _ = writeln!(f); + } + + for check in &self.authorizer_block_builder.checks { + let _ = writeln!(f, "{check};"); + } + if !self.authorizer_block_builder.checks.is_empty() { + let _ = writeln!(f); + } + + for policy in &self.policies { + let _ = writeln!(f, "{policy};"); + } + f + } + + pub fn build(self) -> Result { + let mut world = World::new(); + world.extern_funcs = self.extern_funcs; + + let mut symbols = SymbolTable::new(); + let mut public_key_to_block_id: HashMap> = HashMap::new(); + let mut token_origins = TrustedOrigins::default(); + let mut blocks: Option> = None; + + // load the token if present + if let Some(token) = self.token { + for (i, block) in token.container.blocks.iter().enumerate() { + if let Some(sig) = block.external_signature.as_ref() { + let new_key_id = symbols.public_keys.insert(&sig.public_key); + + public_key_to_block_id + .entry(new_key_id as usize) + .or_default() + .push(i + 1); + } + } + + blocks = Some( + token + .blocks() + .enumerate() + .map(|(i, block)| { + block.and_then(|mut b| { + load_and_translate_block( + &mut b, + i, + &token.symbols, + &mut symbols, + &mut public_key_to_block_id, + &mut world, + )?; + Ok(b) + }) + }) + .collect::, _>>()?, + ); + + token_origins = TrustedOrigins::from_scopes( + &[token::Scope::Previous], + &TrustedOrigins::default(), + token.block_count(), + &public_key_to_block_id, + ); + } + let mut authorizer_origin = Origin::default(); + authorizer_origin.insert(usize::MAX); + + let authorizer_scopes: Vec = self + .authorizer_block_builder + .scopes + .clone() + .iter() + .map(|s| s.convert(&mut symbols)) + .collect(); + + let authorizer_trusted_origins = TrustedOrigins::from_scopes( + &authorizer_scopes, + &TrustedOrigins::default(), + usize::MAX, + &public_key_to_block_id, + ); + for fact in &self.authorizer_block_builder.facts { + world + .facts + .insert(&authorizer_origin, fact.convert(&mut symbols)); + } + + for rule in &self.authorizer_block_builder.rules { + let rule = rule.convert(&mut symbols); + + let rule_trusted_origins = TrustedOrigins::from_scopes( + &rule.scopes, + &authorizer_trusted_origins, + usize::MAX, + &public_key_to_block_id, + ); + + world.rules.insert(usize::MAX, &rule_trusted_origins, rule); + } + + let start = Instant::now(); + world.run_with_limits(&symbols, self.limits.clone())?; + let execution_time = start.elapsed(); + + Ok(Authorizer { + authorizer_block_builder: self.authorizer_block_builder, + world, + symbols, + token_origins, + policies: self.policies, + blocks, + public_key_to_block_id, + limits: self.limits, + execution_time, + }) + } +} + +/// we need to modify the block loaded from the token, because the authorizer's and the token's symbol table can differ +pub(crate) fn load_and_translate_block( + block: &mut Block, + i: usize, + token_symbols: &SymbolTable, + authorizer_symbols: &mut SymbolTable, + public_key_to_block_id: &mut HashMap>, + world: &mut World, +) -> Result<(), error::Token> { + // if it is a 3rd party block, it should not affect the main symbol table + let block_symbols = if i == 0 || block.external_key.is_none() { + token_symbols.clone() + } else { + block.symbols.clone() + }; + + let mut block_origin = Origin::default(); + block_origin.insert(i); + + for scope in block.scopes.iter_mut() { + *scope = crate::token::builder::Scope::convert_from(scope, &block_symbols) + .map(|s| s.convert(authorizer_symbols))?; + } + + let block_trusted_origins = TrustedOrigins::from_scopes( + &block.scopes, + &TrustedOrigins::default(), + i, + &public_key_to_block_id, + ); + + for fact in block.facts.iter_mut() { + *fact = Fact::convert_from(fact, &block_symbols)?.convert(authorizer_symbols); + world.facts.insert(&block_origin, fact.clone()); + } + + for rule in block.rules.iter_mut() { + if let Err(_message) = rule.validate_variables(&block_symbols) { + return Err(error::Logic::InvalidBlockRule(0, block_symbols.print_rule(rule)).into()); + } + *rule = rule.translate(&block_symbols, authorizer_symbols)?; + + let rule_trusted_origins = TrustedOrigins::from_scopes( + &rule.scopes, + &block_trusted_origins, + i, + &public_key_to_block_id, + ); + + world.rules.insert(i, &rule_trusted_origins, rule.clone()); + } + + for check in block.checks.iter_mut() { + let c = Check::convert_from(check, &block_symbols)?; + *check = c.convert(authorizer_symbols); + } + + Ok(()) +} + +impl<'a> BuilderExt for AuthorizerBuilder<'a> { + fn resource(mut self, name: &str) -> Self { + self.authorizer_block_builder = self.authorizer_block_builder.resource(name); + self + } + fn check_resource(mut self, name: &str) -> Self { + self.authorizer_block_builder = self.authorizer_block_builder.check_resource(name); + self + } + fn operation(mut self, name: &str) -> Self { + self.authorizer_block_builder = self.authorizer_block_builder.operation(name); + self + } + fn check_operation(mut self, name: &str) -> Self { + self.authorizer_block_builder = self.authorizer_block_builder.check_operation(name); + self + } + fn check_resource_prefix(mut self, prefix: &str) -> Self { + self.authorizer_block_builder = self.authorizer_block_builder.check_resource_prefix(prefix); + self + } + + fn check_resource_suffix(mut self, suffix: &str) -> Self { + self.authorizer_block_builder = self.authorizer_block_builder.check_resource_suffix(suffix); + self + } + + fn check_expiration_date(mut self, exp: SystemTime) -> Self { + self.authorizer_block_builder = self.authorizer_block_builder.check_expiration_date(exp); + self + } +} + +impl<'a> AuthorizerExt for AuthorizerBuilder<'a> { + fn allow_all(self) -> Self { + self.policy("allow if true").unwrap() + } + fn deny_all(self) -> Self { + self.policy("deny if true").unwrap() + } +} + +impl<'a> AuthorizerBuilder<'a> { + pub fn from_snapshot(input: schema::AuthorizerSnapshot) -> Result { + let schema::AuthorizerSnapshot { + limits, + execution_time, + world, + } = input; + + let limits = RunLimits { + max_facts: limits.max_facts, + max_iterations: limits.max_iterations, + max_time: Duration::from_nanos(limits.max_time), + }; + + let version = world.version.unwrap_or(0); + if !(MIN_SCHEMA_VERSION..=MAX_SCHEMA_VERSION).contains(&version) { + return Err(error::Format::Version { + minimum: crate::token::MIN_SCHEMA_VERSION, + maximum: crate::token::MAX_SCHEMA_VERSION, + actual: version, + } + .into()); + } + + if !world.blocks.is_empty() { + return Err(error::Format::DeserializationError( + "cannot deserialize an AuthorizerBuilder fro a snapshot with blocks".to_string(), + ) + .into()); + } + + if !world.generated_facts.is_empty() { + return Err(error::Format::DeserializationError( + "cannot deserialize an AuthorizerBuilder from a snapshot with generated facts" + .to_string(), + ) + .into()); + } + + if world.iterations != 0 { + return Err(error::Format::DeserializationError( + "cannot deserialize an AuthorizerBuilder from a snapshot with non-zero iterations" + .to_string(), + ) + .into()); + } + + if execution_time != 0 { + return Err(error::Format::DeserializationError( + "cannot deserialize an AuthorizerBuilder from a snapshot with non-zero execution time".to_string(), + ) + .into()); + } + + let mut symbols = default_symbol_table(); + for symbol in world.symbols { + symbols.insert(&symbol); + } + for public_key in world.public_keys { + symbols + .public_keys + .insert(&PublicKey::from_proto(&public_key)?); + } + + let authorizer_block = proto_snapshot_block_to_token_block(&world.authorizer_block)?; + + let authorizer_block_builder = BlockBuilder::convert_from(&authorizer_block, &symbols)?; + let policies = world + .authorizer_policies + .iter() + .map(|policy| proto_policy_to_policy(policy, &symbols, version)) + .collect::, error::Format>>()?; + + let mut authorizer: AuthorizerBuilder<'_> = AuthorizerBuilder::new(); + authorizer.authorizer_block_builder = authorizer_block_builder; + authorizer.policies = policies; + authorizer.limits = limits; + + Ok(authorizer) + } + + pub fn from_raw_snapshot(input: &[u8]) -> Result { + let snapshot = schema::AuthorizerSnapshot::decode(input).map_err(|e| { + error::Format::DeserializationError(format!("deserialization error: {:?}", e)) + })?; + Self::from_snapshot(snapshot) + } + + pub fn from_base64_snapshot(input: &str) -> Result { + let bytes = base64::decode_config(input, base64::URL_SAFE)?; + Self::from_raw_snapshot(&bytes) + } + + pub fn snapshot(&self) -> Result { + let mut symbols = default_symbol_table(); + + let authorizer_policies = self + .policies + .iter() + .map(|policy| policy_to_proto_policy(policy, &mut symbols)) + .collect(); + + let authorizer_block = self.authorizer_block_builder.clone().build(symbols.clone()); + symbols.extend(&authorizer_block.symbols)?; + symbols.public_keys.extend(&authorizer_block.public_keys)?; + + let authorizer_block = token_block_to_proto_snapshot_block(&authorizer_block); + + let blocks = vec![]; + + let generated_facts = vec![]; + + let world = schema::AuthorizerWorld { + version: Some(MAX_SCHEMA_VERSION), + symbols: symbols.strings(), + public_keys: symbols + .public_keys + .into_inner() + .into_iter() + .map(|key| key.to_proto()) + .collect(), + blocks, + authorizer_block, + authorizer_policies, + generated_facts, + iterations: 0, + }; + + Ok(schema::AuthorizerSnapshot { + world, + execution_time: 0u64, + limits: schema::RunLimits { + max_facts: self.limits.max_facts, + max_iterations: self.limits.max_iterations, + max_time: self.limits.max_time.as_nanos() as u64, + }, + }) + } + + pub fn to_raw_snapshot(&self) -> Result, error::Format> { + let snapshot = self.snapshot()?; + let mut bytes = Vec::new(); + snapshot.encode(&mut bytes).map_err(|e| { + error::Format::SerializationError(format!("serialization error: {:?}", e)) + })?; + Ok(bytes) + } + + pub fn to_base64_snapshot(&self) -> Result { + let snapshot_bytes = self.to_raw_snapshot()?; + Ok(base64::encode_config(snapshot_bytes, base64::URL_SAFE)) + } +} diff --git a/biscuit-auth/src/token/builder/biscuit.rs b/biscuit-auth/src/token/builder/biscuit.rs index 8d1fd82e..4bd769f4 100644 --- a/biscuit-auth/src/token/builder/biscuit.rs +++ b/biscuit-auth/src/token/builder/biscuit.rs @@ -25,65 +25,75 @@ impl BiscuitBuilder { } } - pub fn merge(&mut self, other: BlockBuilder) { - self.inner.merge(other) + pub fn merge(mut self, other: BlockBuilder) -> Self { + self.inner = self.inner.merge(other); + self } - pub fn add_fact>(&mut self, fact: F) -> Result<(), error::Token> + pub fn fact>(mut self, fact: F) -> Result where error::Token: From<>::Error>, { - self.inner.add_fact(fact) + self.inner = self.inner.fact(fact)?; + Ok(self) } - pub fn add_rule>(&mut self, rule: Ru) -> Result<(), error::Token> + pub fn rule>(mut self, rule: Ru) -> Result where error::Token: From<>::Error>, { - self.inner.add_rule(rule) + self.inner = self.inner.rule(rule)?; + Ok(self) } - pub fn add_check>(&mut self, check: C) -> Result<(), error::Token> + pub fn check>(mut self, check: C) -> Result where error::Token: From<>::Error>, { - self.inner.add_check(check) + self.inner = self.inner.check(check)?; + Ok(self) } - pub fn add_code>(&mut self, source: T) -> Result<(), error::Token> { - self.inner - .add_code_with_params(source, HashMap::new(), HashMap::new()) + pub fn code>(mut self, source: T) -> Result { + self.inner = self + .inner + .code_with_params(source, HashMap::new(), HashMap::new())?; + Ok(self) } - pub fn add_code_with_params>( - &mut self, + pub fn code_with_params>( + mut self, source: T, params: HashMap, scope_params: HashMap, - ) -> Result<(), error::Token> { - self.inner - .add_code_with_params(source, params, scope_params) + ) -> Result { + self.inner = self.inner.code_with_params(source, params, scope_params)?; + Ok(self) } - pub fn add_scope(&mut self, scope: Scope) { - self.inner.add_scope(scope); + pub fn scope(mut self, scope: Scope) -> Self { + self.inner = self.inner.scope(scope); + self } #[cfg(test)] - pub(crate) fn add_right(&mut self, resource: &str, right: &str) { + pub(crate) fn right(self, resource: &str, right: &str) -> Self { use crate::builder::fact; use super::string; - let _ = self.add_fact(fact("right", &[string(resource), string(right)])); + self.fact(fact("right", &[string(resource), string(right)])) + .unwrap() } - pub fn set_context(&mut self, context: String) { - self.inner.set_context(context); + pub fn context(mut self, context: String) -> Self { + self.inner = self.inner.context(context); + self } - pub fn set_root_key_id(&mut self, root_key_id: u32) { + pub fn root_key_id(mut self, root_key_id: u32) -> Self { self.root_key_id = Some(root_key_id); + self } /// returns all of the datalog loaded in the biscuit builder @@ -154,25 +164,32 @@ impl fmt::Display for BiscuitBuilder { } impl BuilderExt for BiscuitBuilder { - fn add_resource(&mut self, name: &str) { - self.inner.add_resource(name); + fn resource(mut self, name: &str) -> Self { + self.inner = self.inner.resource(name); + self } - fn check_resource(&mut self, name: &str) { - self.inner.check_resource(name); + fn check_resource(mut self, name: &str) -> Self { + self.inner = self.inner.check_resource(name); + self } - fn check_resource_prefix(&mut self, prefix: &str) { - self.inner.check_resource_prefix(prefix); + fn check_resource_prefix(mut self, prefix: &str) -> Self { + self.inner = self.inner.check_resource_prefix(prefix); + self } - fn check_resource_suffix(&mut self, suffix: &str) { - self.inner.check_resource_suffix(suffix); + fn check_resource_suffix(mut self, suffix: &str) -> Self { + self.inner = self.inner.check_resource_suffix(suffix); + self } - fn add_operation(&mut self, name: &str) { - self.inner.add_operation(name); + fn operation(mut self, name: &str) -> Self { + self.inner = self.inner.operation(name); + self } - fn check_operation(&mut self, name: &str) { - self.inner.check_operation(name); + fn check_operation(mut self, name: &str) -> Self { + self.inner = self.inner.check_operation(name); + self } - fn check_expiration_date(&mut self, date: SystemTime) { - self.inner.check_expiration_date(date); + fn check_expiration_date(mut self, date: SystemTime) -> Self { + self.inner = self.inner.check_expiration_date(date); + self } } diff --git a/biscuit-auth/src/token/builder/block.rs b/biscuit-auth/src/token/builder/block.rs index 4967d07e..d0bf0459 100644 --- a/biscuit-auth/src/token/builder/block.rs +++ b/biscuit-auth/src/token/builder/block.rs @@ -26,17 +26,18 @@ impl BlockBuilder { BlockBuilder::default() } - pub fn merge(&mut self, mut other: BlockBuilder) { + pub fn merge(mut self, mut other: BlockBuilder) -> Self { self.facts.append(&mut other.facts); self.rules.append(&mut other.rules); self.checks.append(&mut other.checks); if let Some(c) = other.context { - self.set_context(c); + self.context = Some(c); } + self } - pub fn add_fact>(&mut self, fact: F) -> Result<(), error::Token> + pub fn fact>(mut self, fact: F) -> Result where error::Token: From<>::Error>, { @@ -44,41 +45,41 @@ impl BlockBuilder { fact.validate()?; self.facts.push(fact); - Ok(()) + Ok(self) } - pub fn add_rule>(&mut self, rule: R) -> Result<(), error::Token> + pub fn rule>(mut self, rule: R) -> Result where error::Token: From<>::Error>, { let rule = rule.try_into()?; rule.validate_parameters()?; self.rules.push(rule); - Ok(()) + Ok(self) } - pub fn add_check>(&mut self, check: C) -> Result<(), error::Token> + pub fn check>(mut self, check: C) -> Result where error::Token: From<>::Error>, { let check = check.try_into()?; check.validate_parameters()?; self.checks.push(check); - Ok(()) + Ok(self) } - pub fn add_code>(&mut self, source: T) -> Result<(), error::Token> { - self.add_code_with_params(source, HashMap::new(), HashMap::new()) + pub fn code>(self, source: T) -> Result { + self.code_with_params(source, HashMap::new(), HashMap::new()) } /// Add datalog code to the builder, performing parameter subsitution as required /// Unknown parameters are ignored - pub fn add_code_with_params>( - &mut self, + pub fn code_with_params>( + mut self, source: T, params: HashMap, scope_params: HashMap, - ) -> Result<(), error::Token> { + ) -> Result { let input = source.as_ref(); let source_result = parse_block_source(input).map_err(|e| { @@ -164,15 +165,17 @@ impl BlockBuilder { self.checks.push(check); } - Ok(()) + Ok(self) } - pub fn add_scope(&mut self, scope: Scope) { + pub fn scope(mut self, scope: Scope) -> Self { self.scopes.push(scope); + self } - pub fn set_context(&mut self, context: String) { + pub fn context(mut self, context: String) -> Self { self.context = Some(context); + self } pub(crate) fn build(self, mut symbols: SymbolTable) -> Block { @@ -247,7 +250,7 @@ impl BlockBuilder { // still used in tests but does not make sense for the public API #[cfg(test)] - pub(crate) fn check_right(&mut self, right: &str) { + pub(crate) fn check_right(self, right: &str) -> Result { use crate::builder::{pred, string, var}; use super::rule; @@ -263,7 +266,7 @@ impl BlockBuilder { ], ); - let _ = self.add_check(check); + self.check(check) } } @@ -286,10 +289,11 @@ impl fmt::Display for BlockBuilder { } impl BuilderExt for BlockBuilder { - fn add_resource(&mut self, name: &str) { + fn resource(mut self, name: &str) -> Self { self.facts.push(fact("resource", &[string(name)])); + self } - fn check_resource(&mut self, name: &str) { + fn check_resource(mut self, name: &str) -> Self { self.checks.push(Check { queries: vec![rule( "resource_check", @@ -298,11 +302,13 @@ impl BuilderExt for BlockBuilder { )], kind: CheckKind::One, }); + self } - fn add_operation(&mut self, name: &str) { + fn operation(mut self, name: &str) -> Self { self.facts.push(fact("operation", &[string(name)])); + self } - fn check_operation(&mut self, name: &str) { + fn check_operation(mut self, name: &str) -> Self { self.checks.push(Check { queries: vec![rule( "operation_check", @@ -311,8 +317,9 @@ impl BuilderExt for BlockBuilder { )], kind: CheckKind::One, }); + self } - fn check_resource_prefix(&mut self, prefix: &str) { + fn check_resource_prefix(mut self, prefix: &str) -> Self { let check = constrained_rule( "prefix", &[var("resource")], @@ -330,9 +337,10 @@ impl BuilderExt for BlockBuilder { queries: vec![check], kind: CheckKind::One, }); + self } - fn check_resource_suffix(&mut self, suffix: &str) { + fn check_resource_suffix(mut self, suffix: &str) -> Self { let check = constrained_rule( "suffix", &[var("resource")], @@ -350,9 +358,10 @@ impl BuilderExt for BlockBuilder { queries: vec![check], kind: CheckKind::One, }); + self } - fn check_expiration_date(&mut self, exp: SystemTime) { + fn check_expiration_date(mut self, exp: SystemTime) -> Self { let empty: Vec = Vec::new(); let ops = vec![ Op::Value(var("time")), @@ -370,5 +379,6 @@ impl BuilderExt for BlockBuilder { queries: vec![check], kind: CheckKind::One, }); + self } } diff --git a/biscuit-auth/src/token/builder_ext.rs b/biscuit-auth/src/token/builder_ext.rs index 021cb44a..93ca14d8 100644 --- a/biscuit-auth/src/token/builder_ext.rs +++ b/biscuit-auth/src/token/builder_ext.rs @@ -1,16 +1,16 @@ use std::time::SystemTime; pub trait BuilderExt { - fn add_resource(&mut self, name: &str); - fn check_resource(&mut self, name: &str); - fn check_resource_prefix(&mut self, prefix: &str); - fn check_resource_suffix(&mut self, suffix: &str); - fn add_operation(&mut self, name: &str); - fn check_operation(&mut self, name: &str); - fn check_expiration_date(&mut self, date: SystemTime); + fn resource(self, name: &str) -> Self; + fn check_resource(self, name: &str) -> Self; + fn check_resource_prefix(self, prefix: &str) -> Self; + fn check_resource_suffix(self, suffix: &str) -> Self; + fn operation(self, name: &str) -> Self; + fn check_operation(self, name: &str) -> Self; + fn check_expiration_date(self, date: SystemTime) -> Self; } pub trait AuthorizerExt { - fn add_allow_all(&mut self); - fn add_deny_all(&mut self); + fn allow_all(self) -> Self; + fn deny_all(self) -> Self; } diff --git a/biscuit-auth/src/token/mod.rs b/biscuit-auth/src/token/mod.rs index b0aa3d40..7a042b86 100644 --- a/biscuit-auth/src/token/mod.rs +++ b/biscuit-auth/src/token/mod.rs @@ -1,5 +1,6 @@ //! main structures to interact with Biscuit tokens use std::fmt::Display; +use std::iter::once; use builder::{BiscuitBuilder, BlockBuilder}; use prost::Message; @@ -53,25 +54,26 @@ pub fn default_symbol_table() -> SymbolTable { /// /// use biscuit::{KeyPair, Biscuit, builder::*, builder_ext::*}; /// -/// fn main() { +/// fn main() -> Result<(), biscuit::error::Token> { /// let root = KeyPair::new(Algorithm::Ed25519); /// /// // first we define the authority block for global data, /// // like access rights /// // data from the authority block cannot be created in any other block -/// let mut builder = Biscuit::builder(); -/// builder.add_fact(fact("right", &[string("/a/file1.txt"), string("read")])); +/// let token1 = Biscuit::builder() +/// .fact(fact("right", &[string("/a/file1.txt"), string("read")]))? /// -/// // facts and rules can also be parsed from a string -/// builder.add_fact("right(\"/a/file1.txt\", \"read\")").expect("parse error"); -/// -/// let token1 = builder.build(&root).unwrap(); +/// // facts and rules can also be parsed from a string +/// .fact("right(\"/a/file1.txt\", \"read\")")? +/// .build(&root)?; /// /// // we can create a new block builder from that token -/// let mut builder2 = BlockBuilder::new(); -/// builder2.check_operation("read"); +/// let builder2 = BlockBuilder::new() +/// .check_operation("read"); +/// +/// let token2 = token1.append(builder2)?; /// -/// let token2 = token1.append(builder2).unwrap(); +/// Ok(()) /// } /// ``` #[derive(Clone, Debug)] @@ -156,18 +158,11 @@ impl Biscuit { Ok(token) } - /// creates a authorizer from this token + /// creates an authorizer from this token pub fn authorizer(&self) -> Result { Authorizer::from_token(self) } - /// runs authorization with the provided authorizer - pub fn authorize(&self, authorizer: &Authorizer) -> Result { - let mut a = authorizer.clone(); - a.add_token(self)?; - a.authorize() - } - /// adds a new block to the token /// /// since the public key is integrated into the token, the keypair can be @@ -575,6 +570,32 @@ impl Biscuit { Ok(block) } + + pub(crate) fn blocks(&self) -> impl Iterator> + use<'_> { + once( + proto_block_to_token_block( + &self.authority, + self.container + .authority + .external_signature + .as_ref() + .map(|ex| ex.public_key), + ) + .map_err(error::Token::Format), + ) + .chain(self.blocks.iter().zip(self.container.blocks.iter()).map( + |(block, container)| { + proto_block_to_token_block( + block, + container + .external_signature + .as_ref() + .map(|ex| ex.public_key), + ) + .map_err(error::Token::Format) + }, + )) + } } impl Display for Biscuit { @@ -711,6 +732,8 @@ mod tests { use crate::builder::CheckKind; use crate::crypto::KeyPair; use crate::{error::*, AuthorizerLimits, UnverifiedBiscuit}; + use builder::AuthorizerBuilder; + use builder_ext::AuthorizerExt; use rand::prelude::*; use std::time::{Duration, SystemTime}; @@ -720,13 +743,13 @@ mod tests { let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let serialized1 = { - let mut builder = Biscuit::builder(); - - builder.add_fact("right(\"file1\", \"read\")").unwrap(); - builder.add_fact("right(\"file2\", \"read\")").unwrap(); - builder.add_fact("right(\"file1\", \"write\")").unwrap(); - - let biscuit1 = builder + let biscuit1 = Biscuit::builder() + .fact("right(\"file1\", \"read\")") + .unwrap() + .fact("right(\"file2\", \"read\")") + .unwrap() + .fact("right(\"file1\", \"write\")") + .unwrap() .build_with_rng(&root, default_symbol_table(), &mut rng) .unwrap(); @@ -772,10 +795,8 @@ mod tests { let biscuit1_deser = Biscuit::from(&serialized1, root.public()).unwrap(); // new check: can only have read access1 - let mut block2 = BlockBuilder::new(); - - block2 - .add_check(rule( + let block2 = BlockBuilder::new() + .check(rule( "check1", &[var("resource")], &[ @@ -803,10 +824,8 @@ mod tests { let biscuit2_deser = Biscuit::from(&serialized2, root.public()).unwrap(); // new check: can only access file1 - let mut block3 = BlockBuilder::new(); - - block3 - .add_check(rule( + let block3 = BlockBuilder::new() + .check(rule( "check2", &[string("file1")], &[pred("resource", &[string("file1")])], @@ -828,7 +847,7 @@ mod tests { let final_token = Biscuit::from(&serialized3, root.public()).unwrap(); println!("final token:\n{}", final_token); { - let mut authorizer = final_token.authorizer().unwrap(); + let mut builder = AuthorizerBuilder::new().token(&final_token); let mut facts = vec![ fact("resource", &[string("file1")]), @@ -836,11 +855,12 @@ mod tests { ]; for fact in facts.drain(..) { - authorizer.add_fact(fact).unwrap(); + builder = builder.fact(fact).unwrap(); } //println!("final token: {:#?}", final_token); - authorizer.allow().unwrap(); + + let mut authorizer = builder.allow_all().build().unwrap(); let res = authorizer.authorize(); println!("res1: {:?}", res); @@ -848,7 +868,7 @@ mod tests { } { - let mut authorizer = final_token.authorizer().unwrap(); + let mut builder = AuthorizerBuilder::new().token(&final_token); let mut facts = vec![ fact("resource", &[string("file2")]), @@ -856,10 +876,11 @@ mod tests { ]; for fact in facts.drain(..) { - authorizer.add_fact(fact).unwrap(); + builder = builder.fact(fact).unwrap(); } + builder = builder.allow_all(); - authorizer.allow().unwrap(); + let mut authorizer = builder.build().unwrap(); let res = authorizer.authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -882,33 +903,35 @@ mod tests { let mut rng: StdRng = SeedableRng::seed_from_u64(0); let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); - let mut builder = Biscuit::builder(); - - builder.add_right("/folder1/file1", "read"); - builder.add_right("/folder1/file1", "write"); - builder.add_right("/folder1/file2", "read"); - builder.add_right("/folder1/file2", "write"); - builder.add_right("/folder2/file3", "read"); - - let biscuit1 = builder + let biscuit1 = Biscuit::builder() + .right("/folder1/file1", "read") + .right("/folder1/file1", "write") + .right("/folder1/file2", "read") + .right("/folder1/file2", "write") + .right("/folder2/file3", "read") .build_with_rng(&root, default_symbol_table(), &mut rng) .unwrap(); println!("biscuit1 (authority): {}", biscuit1); - let mut block2 = BlockBuilder::new(); - - block2.check_resource_prefix("/folder1/"); - block2.check_right("read"); + let block2 = BlockBuilder::new() + .check_resource_prefix("/folder1/") + .check_right("read") + .unwrap(); let keypair2 = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1.append_with_keypair(&keypair2, block2).unwrap(); { - let mut authorizer = biscuit2.authorizer().unwrap(); - authorizer.add_fact("resource(\"/folder1/file1\")").unwrap(); - authorizer.add_fact("operation(\"read\")").unwrap(); - authorizer.allow().unwrap(); + let mut authorizer = AuthorizerBuilder::new() + .token(&biscuit2) + .fact("resource(\"/folder1/file1\")") + .unwrap() + .fact("operation(\"read\")") + .unwrap() + .allow_all() + .build() + .unwrap(); let res = authorizer.authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -920,10 +943,15 @@ mod tests { } { - let mut authorizer = biscuit2.authorizer().unwrap(); - authorizer.add_fact("resource(\"/folder2/file3\")").unwrap(); - authorizer.add_fact("operation(\"read\")").unwrap(); - authorizer.allow().unwrap(); + let mut authorizer = AuthorizerBuilder::new() + .token(&biscuit2) + .fact("resource(\"/folder2/file3\")") + .unwrap() + .fact("operation(\"read\")") + .unwrap() + .allow_all() + .build() + .unwrap(); let res = authorizer.authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -946,9 +974,14 @@ mod tests { } { - let mut authorizer = biscuit2.authorizer().unwrap(); - authorizer.add_fact("resource(\"/folder2/file1\")").unwrap(); - authorizer.add_fact("operation(\"write\")").unwrap(); + let mut authorizer = AuthorizerBuilder::new() + .token(&biscuit2) + .fact("resource(\"/folder2/file1\")") + .unwrap() + .fact("operation(\"write\")") + .unwrap() + .build() + .unwrap(); let res = authorizer.authorize(); println!("res3: {:?}", res); @@ -966,31 +999,33 @@ mod tests { let mut rng: StdRng = SeedableRng::seed_from_u64(0); let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); - let mut builder = Biscuit::builder(); - - builder.add_right("file1", "read"); - builder.add_right("file2", "read"); - - let biscuit1 = builder + let biscuit1 = Biscuit::builder() + .right("file1", "read") + .right("file2", "read") .build_with_rng(&root, default_symbol_table(), &mut rng) .unwrap(); println!("biscuit1 (authority): {}", biscuit1); - let mut block2 = BlockBuilder::new(); - - block2.check_expiration_date(SystemTime::now() + Duration::from_secs(30)); - block2.add_fact("key(1234)").unwrap(); + let block2 = BlockBuilder::new() + .check_expiration_date(SystemTime::now() + Duration::from_secs(30)) + .fact("key(1234)") + .unwrap(); let keypair2 = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1.append_with_keypair(&keypair2, block2).unwrap(); { - let mut authorizer = biscuit2.authorizer().unwrap(); - authorizer.add_fact("resource(\"file1\")").unwrap(); - authorizer.add_fact("operation(\"read\")").unwrap(); - authorizer.set_time(); - authorizer.allow().unwrap(); + let mut authorizer = AuthorizerBuilder::new() + .token(&biscuit2) + .fact("resource(\"file1\")") + .unwrap() + .fact("operation(\"read\")") + .unwrap() + .time() + .allow_all() + .build() + .unwrap(); let res = authorizer.authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -1002,11 +1037,16 @@ mod tests { { println!("biscuit2: {}", biscuit2); - let mut authorizer = biscuit2.authorizer().unwrap(); - authorizer.add_fact("resource(\"file1\")").unwrap(); - authorizer.add_fact("operation(\"read\")").unwrap(); - authorizer.set_time(); - authorizer.allow().unwrap(); + let mut authorizer = AuthorizerBuilder::new() + .token(&biscuit2) + .fact("resource(\"file1\")") + .unwrap() + .fact("operation(\"read\")") + .unwrap() + .time() + .allow_all() + .build() + .unwrap(); let res = authorizer.authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -1024,24 +1064,21 @@ mod tests { fn sealed_token() { let mut rng: StdRng = SeedableRng::seed_from_u64(0); let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); - let mut builder = Biscuit::builder(); - - builder.add_right("/folder1/file1", "read"); - builder.add_right("/folder1/file1", "write"); - builder.add_right("/folder1/file2", "read"); - builder.add_right("/folder1/file2", "write"); - builder.add_right("/folder2/file3", "read"); - - let biscuit1 = builder + let biscuit1 = Biscuit::builder() + .right("/folder1/file1", "read") + .right("/folder1/file1", "write") + .right("/folder1/file2", "read") + .right("/folder1/file2", "write") + .right("/folder2/file3", "read") .build_with_rng(&root, default_symbol_table(), &mut rng) .unwrap(); println!("biscuit1 (authority): {}", biscuit1); - let mut block2 = BlockBuilder::new(); - - block2.check_resource_prefix("/folder1/"); - block2.check_right("read"); + let block2 = BlockBuilder::new() + .check_resource_prefix("/folder1/") + .check_right("read") + .unwrap(); let keypair2 = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1.append_with_keypair(&keypair2, block2).unwrap(); @@ -1049,10 +1086,15 @@ mod tests { //println!("biscuit2:\n{:#?}", biscuit2); //panic!(); { - let mut authorizer = biscuit2.authorizer().unwrap(); - authorizer.add_fact("resource(\"/folder1/file1\")").unwrap(); - authorizer.add_fact("operation(\"read\")").unwrap(); - authorizer.allow().unwrap(); + let mut authorizer = AuthorizerBuilder::new() + .token(&biscuit2) + .fact("resource(\"/folder1/file1\")") + .unwrap() + .fact("operation(\"read\")") + .unwrap() + .allow_all() + .build() + .unwrap(); let res = authorizer.authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -1071,10 +1113,15 @@ mod tests { let biscuit3 = Biscuit::from(sealed, root.public()).unwrap(); { - let mut authorizer = biscuit3.authorizer().unwrap(); - authorizer.add_fact("resource(\"/folder1/file1\")").unwrap(); - authorizer.add_fact("operation(\"read\")").unwrap(); - authorizer.allow().unwrap(); + let mut authorizer = AuthorizerBuilder::new() + .token(&biscuit3) + .fact("resource(\"/folder1/file1\")") + .unwrap() + .fact("operation(\"read\")") + .unwrap() + .allow_all() + .build() + .unwrap(); let res = authorizer.authorize(); println!("res1: {:?}", res); @@ -1089,34 +1136,30 @@ mod tests { let mut rng: StdRng = SeedableRng::seed_from_u64(1234); let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); - let mut builder = Biscuit::builder(); - - builder - .add_fact(fact("right", &[string("file1"), string("read")])) - .unwrap(); - builder - .add_fact(fact("right", &[string("file2"), string("read")])) - .unwrap(); - builder - .add_fact(fact("right", &[string("file1"), string("write")])) - .unwrap(); - - let biscuit1 = builder + let biscuit1 = Biscuit::builder() + .fact(fact("right", &[string("file1"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file2"), string("read")])) + .unwrap() + .fact(fact("right", &[string("file1"), string("write")])) + .unwrap() .build_with_rng(&root, default_symbol_table(), &mut rng) .unwrap(); println!("{}", biscuit1); - let mut v = biscuit1.authorizer().expect("omg authorizer"); - - v.add_check(rule( - "right", - &[string("right")], - &[pred("right", &[string("file2"), string("write")])], - )) - .unwrap(); + let mut authorizer = AuthorizerBuilder::new() + .token(&biscuit1) + .check(rule( + "right", + &[string("right")], + &[pred("right", &[string("file2"), string("write")])], + )) + .unwrap() + .build() + .unwrap(); //assert!(v.verify().is_err()); - let res = v.authorize_with_limits(AuthorizerLimits { + let res = authorizer.authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), ..Default::default() }); @@ -1137,40 +1180,43 @@ mod tests { let mut rng: StdRng = SeedableRng::seed_from_u64(0); let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); - let mut builder = Biscuit::builder(); - - builder.add_right("file1", "read"); - builder.add_right("file2", "read"); - builder.add_fact("key(0000)").unwrap(); - - let biscuit1 = builder + let biscuit1 = Biscuit::builder() + .right("file1", "read") + .right("file2", "read") + .fact("key(0000)") + .unwrap() .build_with_rng(&root, default_symbol_table(), &mut rng) .unwrap(); println!("biscuit1 (authority): {}", biscuit1); - let mut block2 = BlockBuilder::new(); - - block2.check_expiration_date(SystemTime::now() + Duration::from_secs(30)); - block2.add_fact("key(1234)").unwrap(); + let block2 = BlockBuilder::new() + .check_expiration_date(SystemTime::now() + Duration::from_secs(30)) + .fact("key(1234)") + .unwrap(); let keypair2 = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1.append_with_keypair(&keypair2, block2).unwrap(); - let mut block3 = BlockBuilder::new(); - - block3.check_expiration_date(SystemTime::now() + Duration::from_secs(10)); - block3.add_fact("key(5678)").unwrap(); + let block3 = BlockBuilder::new() + .check_expiration_date(SystemTime::now() + Duration::from_secs(10)) + .fact("key(5678)") + .unwrap(); let keypair3 = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let biscuit3 = biscuit2.append_with_keypair(&keypair3, block3).unwrap(); { println!("biscuit3: {}", biscuit3); - let mut authorizer = biscuit3.authorizer().unwrap(); - authorizer.add_fact("resource(\"file1\")").unwrap(); - authorizer.add_fact("operation(\"read\")").unwrap(); - authorizer.set_time(); + let mut authorizer = AuthorizerBuilder::new() + .token(&biscuit3) + .fact("resource(\"file1\")") + .unwrap() + .fact("operation(\"read\")") + .unwrap() + .time() + .build() + .unwrap(); // test that cloning correctly embeds the first block's facts let mut other_authorizer = authorizer.clone(); @@ -1225,24 +1271,21 @@ mod tests { let mut rng: StdRng = SeedableRng::seed_from_u64(0); let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); - let mut builder = Biscuit::builder(); - - builder - .add_check(check( + let biscuit1 = Biscuit::builder() + .check(check( &[pred("resource", &[string("hello")])], CheckKind::One, )) - .unwrap(); - - let biscuit1 = builder + .unwrap() .build_with_rng(&root, default_symbol_table(), &mut rng) .unwrap(); println!("biscuit1 (authority): {}", biscuit1); // new check: can only have read access1 - let mut block2 = BlockBuilder::new(); - block2.add_fact(fact("check1", &[string("test")])).unwrap(); + let block2 = BlockBuilder::new() + .fact(fact("check1", &[string("test")])) + .unwrap(); let keypair2 = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1.append_with_keypair(&keypair2, block2).unwrap(); @@ -1251,11 +1294,15 @@ mod tests { //println!("generated biscuit token 2: {} bytes\n{}", serialized2.len(), serialized2.to_hex(16)); { - let mut authorizer = biscuit2.authorizer().unwrap(); - authorizer.add_fact("resource(\"file1\")").unwrap(); - authorizer.add_fact("operation(\"read\")").unwrap(); - println!("symbols before time: {:?}", authorizer.symbols); - authorizer.set_time(); + let mut authorizer = AuthorizerBuilder::new() + .token(&biscuit2) + .fact("resource(\"file1\")") + .unwrap() + .fact("operation(\"read\")") + .unwrap() + .time() + .build() + .unwrap(); println!("world:\n{}", authorizer.print_world()); println!("symbols: {:?}", authorizer.symbols); @@ -1329,26 +1376,27 @@ mod tests { let mut rng: StdRng = SeedableRng::seed_from_u64(0); let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); - let mut builder = Biscuit::builder(); - builder.add_fact("bytes(hex:0102AB)").unwrap(); - let biscuit1 = builder + let biscuit1 = Biscuit::builder() + .fact("bytes(hex:0102AB)") + .unwrap() .build_with_rng(&root, default_symbol_table(), &mut rng) .unwrap(); println!("biscuit1 (authority): {}", biscuit1); - let mut block2 = BlockBuilder::new(); - block2 - .add_rule("has_bytes($0) <- bytes($0), { hex:00000000, hex:0102AB }.contains($0)") + let block2 = BlockBuilder::new() + .rule("has_bytes($0) <- bytes($0), { hex:00000000, hex:0102AB }.contains($0)") .unwrap(); let keypair2 = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let biscuit2 = biscuit1.append_with_keypair(&keypair2, block2).unwrap(); - let mut authorizer = biscuit2.authorizer().unwrap(); - authorizer - .add_check("check if bytes($0), { hex:00000000, hex:0102AB }.contains($0)") + let mut authorizer = AuthorizerBuilder::new() + .token(&biscuit2) + .check("check if bytes($0), { hex:00000000, hex:0102AB }.contains($0)") + .unwrap() + .allow_all() + .build() .unwrap(); - authorizer.allow().unwrap(); let res = authorizer.authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -1376,20 +1424,15 @@ mod tests { let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); let serialized1 = { - let mut builder = Biscuit::builder(); - - builder - .add_fact("right(\"/folder1/file1\", \"read\")") - .unwrap(); - builder - .add_fact("right(\"/folder1/file1\", \"write\")") - .unwrap(); - builder - .add_fact("right(\"/folder2/file1\", \"read\")") - .unwrap(); - builder.add_check("check if operation(\"read\")").unwrap(); - - let biscuit1 = builder + let biscuit1 = Biscuit::builder() + .fact("right(\"/folder1/file1\", \"read\")") + .unwrap() + .fact("right(\"/folder1/file1\", \"write\")") + .unwrap() + .fact("right(\"/folder2/file1\", \"read\")") + .unwrap() + .check("check if operation(\"read\")") + .unwrap() .build_with_rng(&root, default_symbol_table(), &mut rng) .unwrap(); @@ -1406,20 +1449,18 @@ mod tests { let biscuit1_deser = Biscuit::from(&serialized1, |_| Ok(root.public())).unwrap(); // new check: can only have read access1 - let mut block2 = BlockBuilder::new(); + let block2 = BlockBuilder::new() // Bypass `check if operation("read")` from authority block - block2 - .add_rule("operation(\"read\") <- operation($any)") - .unwrap(); + .rule("operation(\"read\") <- operation($any)") + .unwrap() // Bypass `check if resource($file), $file.starts_with("/folder1/")` from block #1 - block2 - .add_rule("resource(\"/folder1/\") <- resource($any)") - .unwrap(); + .rule("resource(\"/folder1/\") <- resource($any)") + .unwrap() // Add missing rights - block2.add_rule("right($file, $right) <- right($any1, $any2), resource($file), operation($right)") + .rule("right($file, $right) <- right($any1, $any2), resource($file), operation($right)") .unwrap(); let keypair2 = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); @@ -1438,13 +1479,17 @@ mod tests { let final_token = Biscuit::from(&serialized2, root.public()).unwrap(); println!("final token:\n{}", final_token); - let mut authorizer = final_token.authorizer().unwrap(); - authorizer.add_fact("resource(\"/folder2/file1\")").unwrap(); - authorizer.add_fact("operation(\"write\")").unwrap(); - authorizer - .add_policy("allow if resource($file), operation($op), right($file, $op)") + let mut authorizer = AuthorizerBuilder::new() + .token(&final_token) + .fact("resource(\"/folder2/file1\")") + .unwrap() + .fact("operation(\"write\")") + .unwrap() + .policy("allow if resource($file), operation($op), right($file, $op)") + .unwrap() + .deny_all() + .build() .unwrap(); - authorizer.deny().unwrap(); let res = authorizer.authorize_with_limits(crate::token::authorizer::AuthorizerLimits { max_time: Duration::from_secs(1), @@ -1461,33 +1506,33 @@ mod tests { let mut rng: StdRng = SeedableRng::seed_from_u64(0); let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); - let mut builder = Biscuit::builder(); - - builder.add_check("check if fact($v), $v < 1").unwrap(); - - let biscuit1 = builder + let biscuit1 = Biscuit::builder() + .check("check if fact($v), $v < 1") + .unwrap() .build_with_rng(&root, default_symbol_table(), &mut rng) .unwrap(); println!("biscuit1 (authority): {}", biscuit1); - let mut builder = Biscuit::builder(); - - builder.add_check("check all fact($v), $v < 1").unwrap(); - - let biscuit2 = builder + let biscuit2 = Biscuit::builder() + .check("check all fact($v), $v < 1") + .unwrap() .build_with_rng(&root, default_symbol_table(), &mut rng) .unwrap(); println!("biscuit2 (authority): {}", biscuit2); { - let mut authorizer = biscuit1.authorizer().unwrap(); - authorizer.add_fact("fact(0)").unwrap(); - authorizer.add_fact("fact(1)").unwrap(); - - //println!("final token: {:#?}", final_token); - authorizer.allow().unwrap(); + let mut authorizer = AuthorizerBuilder::new() + .token(&biscuit1) + .fact("fact(0)") + .unwrap() + .fact("fact(1)") + .unwrap() + //println!("final token: {:#?}", final_token); + .allow_all() + .build() + .unwrap(); let res = authorizer.authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -1498,12 +1543,16 @@ mod tests { } { - let mut authorizer = biscuit2.authorizer().unwrap(); - authorizer.add_fact("fact(0)").unwrap(); - authorizer.add_fact("fact(1)").unwrap(); - - //println!("final token: {:#?}", final_token); - authorizer.allow().unwrap(); + let mut authorizer = AuthorizerBuilder::new() + .token(&biscuit2) + .fact("fact(0)") + .unwrap() + .fact("fact(1)") + .unwrap() + //println!("final token: {:#?}", final_token); + .allow_all() + .build() + .unwrap(); let res = authorizer.authorize_with_limits(AuthorizerLimits { max_time: Duration::from_secs(10), @@ -1582,13 +1631,13 @@ mod tests { fn verified_unverified_consistency() { let mut rng: StdRng = SeedableRng::seed_from_u64(0); let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); - let mut builder = Biscuit::builder(); - - builder.add_fact("right(\"file1\", \"read\")").unwrap(); - builder.add_fact("right(\"file2\", \"read\")").unwrap(); - builder.add_fact("right(\"file1\", \"write\")").unwrap(); - - let biscuit1 = builder + let biscuit1 = Biscuit::builder() + .fact("right(\"file1\", \"read\")") + .unwrap() + .fact("right(\"file2\", \"read\")") + .unwrap() + .fact("right(\"file1\", \"write\")") + .unwrap() .build_with_rng(&root, default_symbol_table(), &mut rng) .unwrap(); diff --git a/biscuit-auth/src/token/third_party.rs b/biscuit-auth/src/token/third_party.rs index e5b7ea62..64b43d22 100644 --- a/biscuit-auth/src/token/third_party.rs +++ b/biscuit-auth/src/token/third_party.rs @@ -159,13 +159,13 @@ mod tests { fn third_party_request_roundtrip() { let mut rng: rand::rngs::StdRng = rand::SeedableRng::seed_from_u64(0); let root = KeyPair::new_with_rng(crate::builder::Algorithm::Ed25519, &mut rng); - let mut builder = crate::Biscuit::builder(); - - builder.add_fact("right(\"file1\", \"read\")").unwrap(); - builder.add_fact("right(\"file2\", \"read\")").unwrap(); - builder.add_fact("right(\"file1\", \"write\")").unwrap(); - - let biscuit1 = builder + let biscuit1 = crate::Biscuit::builder() + .fact("right(\"file1\", \"read\")") + .unwrap() + .fact("right(\"file2\", \"read\")") + .unwrap() + .fact("right(\"file1\", \"write\")") + .unwrap() .build_with_rng(&root, crate::token::default_symbol_table(), &mut rng) .unwrap(); let req = biscuit1.third_party_request().unwrap(); diff --git a/biscuit-auth/tests/macros.rs b/biscuit-auth/tests/macros.rs index 0922b342..3bf92906 100644 --- a/biscuit-auth/tests/macros.rs +++ b/biscuit-auth/tests/macros.rs @@ -23,7 +23,7 @@ fn block_macro() { ); let is_true = true; - block_merge!(&mut b, r#"appended({is_true});"#); + b = block_merge!(b, r#"appended({is_true});"#); assert_eq!( b.to_string(), @@ -68,17 +68,19 @@ fn authorizer_macro() { ); let is_true = true; - authorizer_merge!( - &mut b, + b = authorizer_merge!( + b, r#"appended({is_true}); allow if true; "# ); + let authorizer = b.build().unwrap(); assert_eq!( - b.dump_code(), - r#"fact("test", hex:aabbcc, [true], "my_value"); -appended(true); + authorizer.dump_code(), + r#"appended(true); +fact("test", hex:aabbcc, [true], "my_value"); +rule("test", true); rule($0, true) <- fact($0, $1, $2, "my_value"); @@ -92,7 +94,9 @@ allow if true; #[test] fn authorizer_macro_trailing_comma() { - let a = authorizer!(r#"fact("test", {my_key});"#, my_key = "my_value",); + let a = authorizer!(r#"fact("test", {my_key});"#, my_key = "my_value",) + .build() + .unwrap(); assert_eq!( a.dump_code(), r#"fact("test", "my_value"); @@ -119,12 +123,11 @@ fn biscuit_macro() { check if true trusting ed25519/6e9e6d5a75cf0c0e87ec1256b4dfed0ca3ba452912d213fcc70f8516583db9db; "#, my_key_bytes = s.into_bytes(), - ); - b.set_root_key_id(2); + ).root_key_id(2); let is_true = true; - biscuit_merge!( - &mut b, + b = biscuit_merge!( + b, r#"appended({is_true}); check if true; "# @@ -257,9 +260,10 @@ fn json() { user_roles($value), $value.get("id") == $id, $value.get("roles").contains("admin");"# - ); - - authorizer.add_token(&biscuit).unwrap(); + ) + .token(&biscuit) + .build() + .unwrap(); assert_eq!( authorizer .authorize_with_limits(RunLimits { diff --git a/biscuit-auth/tests/rights.rs b/biscuit-auth/tests/rights.rs index 3741abad..84006470 100644 --- a/biscuit-auth/tests/rights.rs +++ b/biscuit-auth/tests/rights.rs @@ -11,40 +11,43 @@ fn main() { let mut rng: StdRng = SeedableRng::seed_from_u64(1234); let root = KeyPair::new_with_rng(builder::Algorithm::Ed25519, &mut rng); - let mut builder = Biscuit::builder(); - - builder.add_fact(fact( - "right", - &[string("authority"), string("file1"), string("read")], - )); - builder.add_fact(fact( - "right", - &[string("authority"), string("file2"), string("read")], - )); - builder.add_fact(fact( - "right", - &[string("authority"), string("file1"), string("write")], - )); - - let biscuit1 = builder + let biscuit1 = Biscuit::builder() + .fact(fact( + "right", + &[string("authority"), string("file1"), string("read")], + )) + .unwrap() + .fact(fact( + "right", + &[string("authority"), string("file2"), string("read")], + )) + .unwrap() + .fact(fact( + "right", + &[string("authority"), string("file1"), string("write")], + )) + .unwrap() .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); println!("{}", biscuit1); - let mut v = biscuit1.authorizer().expect("omg verifier"); + let mut v = AuthorizerBuilder::new() + .token(&biscuit1) + .check(rule( + "right", + &[string("right")], + &[pred( + "right", + &[string("authority"), string("file2"), string("write")], + )], + )) + .unwrap() + .build() + .unwrap(); //v.add_resource("file2"); //v.add_operation("read"); //v.add_operation("write"); - v.add_check(rule( - "right", - &[string("right")], - &[pred( - "right", - &[string("authority"), string("file2"), string("write")], - )], - )); - let res = v.authorize(); println!("{:#?}", res); panic!() diff --git a/biscuit-capi/src/lib.rs b/biscuit-capi/src/lib.rs index 2232c47f..010fb360 100644 --- a/biscuit-capi/src/lib.rs +++ b/biscuit-capi/src/lib.rs @@ -292,9 +292,10 @@ pub extern "C" fn error_check_is_authorizer(check_index: u64) -> bool { pub struct Biscuit(biscuit_auth::Biscuit); pub struct KeyPair(biscuit_auth::KeyPair); pub struct PublicKey(biscuit_auth::PublicKey); -pub struct BiscuitBuilder(biscuit_auth::builder::BiscuitBuilder); -pub struct BlockBuilder(biscuit_auth::builder::BlockBuilder); +pub struct BiscuitBuilder(Option); +pub struct BlockBuilder(Option); pub struct Authorizer(biscuit_auth::Authorizer); +pub struct AuthorizerBuilder<'a>(Option>); #[repr(C)] pub enum SignatureAlgorithm { @@ -422,9 +423,45 @@ pub unsafe extern "C" fn public_key_deserialize( #[no_mangle] pub unsafe extern "C" fn public_key_free(_kp: Option>) {} +impl BiscuitBuilder { + fn set_context(&mut self, context: &str) { + let mut inner = self.0.take().unwrap(); + inner = inner.context(context.to_string()); + self.0 = Some(inner); + } + + fn set_root_key_id(&mut self, root_key_id: u32) { + let mut inner = self.0.take().unwrap(); + inner = inner.root_key_id(root_key_id); + self.0 = Some(inner); + } + + fn add_fact(&mut self, fact: &str) -> Result<(), biscuit_auth::error::Token> { + let mut inner = self.0.take().unwrap(); + inner = inner.fact(fact)?; + self.0 = Some(inner); + Ok(()) + } + + fn add_rule(&mut self, rule: &str) -> Result<(), biscuit_auth::error::Token> { + let mut inner = self.0.take().unwrap(); + inner = inner.rule(rule)?; + self.0 = Some(inner); + Ok(()) + } + + fn add_check(&mut self, check: &str) -> Result<(), biscuit_auth::error::Token> { + let mut inner = self.0.take().unwrap(); + inner = inner.check(check)?; + self.0 = Some(inner); + Ok(()) + } +} #[no_mangle] pub unsafe extern "C" fn biscuit_builder() -> Option> { - Some(Box::new(BiscuitBuilder(biscuit_auth::Biscuit::builder()))) + Some(Box::new(BiscuitBuilder(Some( + biscuit_auth::Biscuit::builder(), + )))) } #[no_mangle] @@ -446,7 +483,7 @@ pub unsafe extern "C" fn biscuit_builder_set_context( false } Ok(context) => { - builder.0.set_context(context.to_string()); + builder.set_context(context); true } } @@ -463,7 +500,7 @@ pub unsafe extern "C" fn biscuit_builder_set_root_key_id( } let builder = builder.unwrap(); - builder.0.set_root_key_id(root_key_id); + builder.set_root_key_id(root_key_id); true } @@ -486,7 +523,6 @@ pub unsafe extern "C" fn biscuit_builder_add_fact( } builder - .0 .add_fact(s.unwrap()) .map_err(|e| { update_last_error(Error::Biscuit(e)); @@ -513,7 +549,6 @@ pub unsafe extern "C" fn biscuit_builder_add_rule( } builder - .0 .add_rule(s.unwrap()) .map_err(|e| { update_last_error(Error::Biscuit(e)); @@ -540,14 +575,15 @@ pub unsafe extern "C" fn biscuit_builder_add_check( } builder - .0 .add_check(s.unwrap()) .map_err(|e| { update_last_error(Error::Biscuit(e)); }) .is_ok() } - +/// Build a biscuit token from a builder +/// +/// The builder will be freed automatically when the biscuit is returned #[no_mangle] pub unsafe extern "C" fn biscuit_builder_build( builder: Option<&BiscuitBuilder>, @@ -577,6 +613,7 @@ pub unsafe extern "C" fn biscuit_builder_build( (*builder) .0 .clone() + .expect("builder is none") .build_with_rng(&key_pair.0, SymbolTable::default(), &mut rng) .map(Biscuit) .map(Box::new) @@ -762,9 +799,40 @@ pub unsafe extern "C" fn biscuit_block_context( } } +impl BlockBuilder { + fn set_context(&mut self, context: &str) { + let mut inner = self.0.take().unwrap(); + inner = inner.context(context.to_string()); + self.0 = Some(inner); + } + + fn add_fact(&mut self, fact: &str) -> Result<(), biscuit_auth::error::Token> { + let mut inner = self.0.take().unwrap(); + inner = inner.fact(fact)?; + self.0 = Some(inner); + Ok(()) + } + + fn add_rule(&mut self, rule: &str) -> Result<(), biscuit_auth::error::Token> { + let mut inner = self.0.take().unwrap(); + inner = inner.rule(rule)?; + self.0 = Some(inner); + Ok(()) + } + + fn add_check(&mut self, check: &str) -> Result<(), biscuit_auth::error::Token> { + let mut inner = self.0.take().unwrap(); + inner = inner.check(check)?; + self.0 = Some(inner); + Ok(()) + } +} + #[no_mangle] pub unsafe extern "C" fn create_block() -> Box { - Box::new(BlockBuilder(biscuit_auth::builder::BlockBuilder::new())) + Box::new(BlockBuilder(Some( + biscuit_auth::builder::BlockBuilder::new(), + ))) } #[no_mangle] @@ -790,7 +858,7 @@ pub unsafe extern "C" fn biscuit_append_block( match biscuit .0 - .append_with_keypair(&key_pair.0, builder.0.clone()) + .append_with_keypair(&key_pair.0, builder.0.clone().expect("builder is none")) { Ok(token) => Some(Box::new(Biscuit(token))), Err(e) => { @@ -834,7 +902,7 @@ pub unsafe extern "C" fn block_builder_set_context( false } Ok(context) => { - builder.0.set_context(context.to_string()); + builder.set_context(context); true } } @@ -859,7 +927,6 @@ pub unsafe extern "C" fn block_builder_add_fact( } builder - .0 .add_fact(s.unwrap()) .map_err(|e| { update_last_error(Error::Biscuit(e)); @@ -886,7 +953,6 @@ pub unsafe extern "C" fn block_builder_add_rule( } builder - .0 .add_rule(s.unwrap()) .map_err(|e| { update_last_error(Error::Biscuit(e)); @@ -913,7 +979,6 @@ pub unsafe extern "C" fn block_builder_add_check( } builder - .0 .add_check(s.unwrap()) .map_err(|e| { update_last_error(Error::Biscuit(e)); @@ -924,16 +989,45 @@ pub unsafe extern "C" fn block_builder_add_check( #[no_mangle] pub unsafe extern "C" fn block_builder_free(_builder: Option>) {} +impl<'a> AuthorizerBuilder<'a> { + fn add_fact(&mut self, fact: &str) -> Result<(), biscuit_auth::error::Token> { + let mut inner = self.0.take().unwrap(); + inner = inner.fact(fact)?; + self.0 = Some(inner); + Ok(()) + } + + fn add_rule(&mut self, rule: &str) -> Result<(), biscuit_auth::error::Token> { + let mut inner = self.0.take().unwrap(); + inner = inner.rule(rule)?; + self.0 = Some(inner); + Ok(()) + } + + fn add_check(&mut self, check: &str) -> Result<(), biscuit_auth::error::Token> { + let mut inner = self.0.take().unwrap(); + inner = inner.check(check)?; + self.0 = Some(inner); + Ok(()) + } + + fn add_policy(&mut self, policy: &str) -> Result<(), biscuit_auth::error::Token> { + let mut inner = self.0.take().unwrap(); + inner = inner.policy(policy)?; + self.0 = Some(inner); + Ok(()) + } +} #[no_mangle] -pub unsafe extern "C" fn authorizer_add_fact( - authorizer: Option<&mut Authorizer>, +pub unsafe extern "C" fn authorizer_builder_add_fact( + builder: Option<&mut AuthorizerBuilder>, fact: *const c_char, ) -> bool { - if authorizer.is_none() { + if builder.is_none() { update_last_error(Error::InvalidArgument); return false; } - let authorizer = authorizer.unwrap(); + let builder = builder.unwrap(); let fact = CStr::from_ptr(fact); let s = fact.to_str(); @@ -942,8 +1036,7 @@ pub unsafe extern "C" fn authorizer_add_fact( return false; } - authorizer - .0 + builder .add_fact(s.unwrap()) .map_err(|e| { update_last_error(Error::Biscuit(e)); @@ -952,15 +1045,15 @@ pub unsafe extern "C" fn authorizer_add_fact( } #[no_mangle] -pub unsafe extern "C" fn authorizer_add_rule( - authorizer: Option<&mut Authorizer>, +pub unsafe extern "C" fn authorizer_builder_add_rule( + builder: Option<&mut AuthorizerBuilder>, rule: *const c_char, ) -> bool { - if authorizer.is_none() { + if builder.is_none() { update_last_error(Error::InvalidArgument); return false; } - let authorizer = authorizer.unwrap(); + let builder = builder.unwrap(); let rule = CStr::from_ptr(rule); let s = rule.to_str(); @@ -969,8 +1062,7 @@ pub unsafe extern "C" fn authorizer_add_rule( return false; } - authorizer - .0 + builder .add_rule(s.unwrap()) .map_err(|e| { update_last_error(Error::Biscuit(e)); @@ -979,15 +1071,15 @@ pub unsafe extern "C" fn authorizer_add_rule( } #[no_mangle] -pub unsafe extern "C" fn authorizer_add_check( - authorizer: Option<&mut Authorizer>, +pub unsafe extern "C" fn authorizer_builder_add_check( + builder: Option<&mut AuthorizerBuilder>, check: *const c_char, ) -> bool { - if authorizer.is_none() { + if builder.is_none() { update_last_error(Error::InvalidArgument); return false; } - let authorizer = authorizer.unwrap(); + let builder = builder.unwrap(); let check = CStr::from_ptr(check); let s = check.to_str(); @@ -996,8 +1088,7 @@ pub unsafe extern "C" fn authorizer_add_check( return false; } - authorizer - .0 + builder .add_check(s.unwrap()) .map_err(|e| { update_last_error(Error::Biscuit(e)); @@ -1006,15 +1097,15 @@ pub unsafe extern "C" fn authorizer_add_check( } #[no_mangle] -pub unsafe extern "C" fn authorizer_add_policy( - authorizer: Option<&mut Authorizer>, +pub unsafe extern "C" fn authorizer_builder_add_policy( + builder: Option<&mut AuthorizerBuilder>, policy: *const c_char, ) -> bool { - if authorizer.is_none() { + if builder.is_none() { update_last_error(Error::InvalidArgument); return false; } - let authorizer = authorizer.unwrap(); + let builder = builder.unwrap(); let policy = CStr::from_ptr(policy); let s = policy.to_str(); @@ -1023,8 +1114,7 @@ pub unsafe extern "C" fn authorizer_add_policy( return false; } - authorizer - .0 + builder .add_policy(s.unwrap()) .map_err(|e| { update_last_error(Error::Biscuit(e)); @@ -1032,6 +1122,31 @@ pub unsafe extern "C" fn authorizer_add_policy( .is_ok() } +/// Build an authorizer +/// +/// The builder will be freed automatically when the authorizer is returned +#[no_mangle] +pub unsafe extern "C" fn authorizer_builder_build( + builder: Option>, +) -> Option> { + if builder.is_none() { + update_last_error(Error::InvalidArgument); + } + let builder = builder.unwrap(); + builder + .0 + .clone() + .take() + .unwrap() + .build() + .map(Authorizer) + .map(Box::new) + .ok() +} + +#[no_mangle] +pub unsafe extern "C" fn authorizer_builder_free(_builder: Option>) {} + #[no_mangle] pub unsafe extern "C" fn authorizer_authorize(authorizer: Option<&mut Authorizer>) -> bool { if authorizer.is_none() { diff --git a/biscuit-quote/src/lib.rs b/biscuit-quote/src/lib.rs index 8b2380dc..5263b44d 100644 --- a/biscuit-quote/src/lib.rs +++ b/biscuit-quote/src/lib.rs @@ -129,7 +129,7 @@ pub fn authorizer(input: proc_macro::TokenStream) -> proc_macro::TokenStream { parameters, } = syn::parse_macro_input!(input as ParsedCreateNew); - let ty = syn::parse_quote!(::biscuit_auth::Authorizer); + let ty = syn::parse_quote!(::biscuit_auth::builder::AuthorizerBuilder); let builder = Builder::source(ty, None, datalog, parameters) .unwrap_or_else(|e| abort_call_site!(e.to_string())); @@ -148,7 +148,7 @@ pub fn authorizer_merge(input: proc_macro::TokenStream) -> proc_macro::TokenStre parameters, } = syn::parse_macro_input!(input as ParsedMerge); - let ty = syn::parse_quote!(::biscuit_auth::Authorizer); + let ty = syn::parse_quote!(::biscuit_auth::builder::AuthorizerBuilder); let builder = Builder::source(ty, Some(target), datalog, parameters) .unwrap_or_else(|e| abort_call_site!(e.to_string())); @@ -349,7 +349,7 @@ impl Item { }, middle: TokenStream::new(), end: quote! { - __biscuit_auth_builder.add_fact(__biscuit_auth_item).unwrap(); + __biscuit_auth_builder = __biscuit_auth_builder.fact(__biscuit_auth_item).unwrap(); }, } } @@ -361,7 +361,7 @@ impl Item { }, middle: TokenStream::new(), end: quote! { - __biscuit_auth_builder.add_rule(__biscuit_auth_item).unwrap(); + __biscuit_auth_builder = __biscuit_auth_builder.rule(__biscuit_auth_item).unwrap(); }, } } @@ -374,7 +374,7 @@ impl Item { }, middle: TokenStream::new(), end: quote! { - __biscuit_auth_builder.add_check(__biscuit_auth_item).unwrap(); + __biscuit_auth_builder =__biscuit_auth_builder.check(__biscuit_auth_item).unwrap(); }, } } @@ -387,7 +387,7 @@ impl Item { }, middle: TokenStream::new(), end: quote! { - __biscuit_auth_builder.add_policy(__biscuit_auth_item).unwrap(); + __biscuit_auth_builder = __biscuit_auth_builder.policy(__biscuit_auth_item).unwrap(); }, } } @@ -476,7 +476,7 @@ impl ToTokens for Builder { let builder_type = &self.builder_type; let builder_quote = if let Some(target) = &self.target { quote! { - let __biscuit_auth_builder: &mut #builder_type = #target; + let mut __biscuit_auth_builder: #builder_type = #target; } } else { quote! {