Skip to content

Commit fc0d069

Browse files
authored
authorizer builder (#250)
This adds an `AuthorizerBuilder` struct that is used to create an `Authorizer`. All of the mutable behaviour, like adding facts or executing Datalog rules is moved into the builder, while the authorizer is limited to read-only queries (still requiring self mutability to track execution time). This will solve some awkward behaviour where the authorizer had to run Datalog rules again when facts or rules were added, but it was not done consistently. The `AuthorizerBuilder` is compatible with snapshots, to store and reuse checks and policies. It has a `build` method taking a token as argument, and a `build_unauthenticated` for authorization without token. The builder APIs are alo changing. Before, we had the following: ```rust let mut builder = Biscuit::builder(); builder.add_fact(r"right("file1", "read")"#)?; builder.add_fact(r"right("file2", "read")"#)?; let token = builder.build()?; ``` Builders are now constructed like this: ```rust let token = Biscuit::builder() .fact(r"right("file1", "read")"#)? .fact(r"right("file2", "read")"#)? .build()?; ````
1 parent 6d55705 commit fc0d069

File tree

22 files changed

+1810
-1365
lines changed

22 files changed

+1810
-1365
lines changed

biscuit-auth/benches/token.rs

Lines changed: 291 additions & 220 deletions
Large diffs are not rendered by default.

biscuit-auth/examples/testcases.rs

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use biscuit::datalog::SymbolTable;
77
use biscuit::error;
88
use biscuit::format::convert::v2 as convert;
99
use biscuit::macros::*;
10-
use biscuit::Authorizer;
1110
use biscuit::{builder::*, builder_ext::*, Biscuit};
1211
use biscuit::{KeyPair, PrivateKey, PublicKey};
1312
use biscuit_auth::builder;
@@ -344,12 +343,13 @@ fn validate_token_with_limits_and_external_functions(
344343
revocation_ids.push(hex::encode(&bytes));
345344
}
346345

347-
let mut authorizer = Authorizer::new();
348-
authorizer.set_extern_funcs(extern_funcs);
349-
authorizer.add_code(authorizer_code).unwrap();
350-
let authorizer_code = authorizer.dump_code();
346+
let builder = AuthorizerBuilder::new()
347+
.set_extern_funcs(extern_funcs)
348+
.code(authorizer_code)
349+
.unwrap();
350+
let authorizer_code = builder.dump_code();
351351

352-
match authorizer.add_token(&token) {
352+
let mut authorizer = match builder.build(&token) {
353353
Ok(v) => v,
354354
Err(e) => {
355355
return Validation {
@@ -878,9 +878,9 @@ fn scoped_rules(target: &str, root: &KeyPair, test: bool) -> TestResult {
878878
)
879879
.unwrap();
880880

881-
let mut block3 = BlockBuilder::new();
882-
883-
block3.add_fact(r#"owner("alice", "file2")"#).unwrap();
881+
let block3 = BlockBuilder::new()
882+
.fact(r#"owner("alice", "file2")"#)
883+
.unwrap();
884884

885885
let keypair3 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng);
886886
let biscuit3 = biscuit2.append_with_keypair(&keypair3, block3).unwrap();
@@ -973,14 +973,13 @@ fn expired_token(target: &str, root: &KeyPair, test: bool) -> TestResult {
973973
.build_with_rng(&root, SymbolTable::default(), &mut rng)
974974
.unwrap();
975975

976-
let mut block2 = block!(r#"check if resource("file1");"#);
977-
978-
// January 1 2019
979-
block2.check_expiration_date(
980-
UNIX_EPOCH
981-
.checked_add(Duration::from_secs(49 * 365 * 24 * 3600))
982-
.unwrap(),
983-
);
976+
let block2 = block!(r#"check if resource("file1");"#)
977+
// January 1 2019
978+
.check_expiration_date(
979+
UNIX_EPOCH
980+
.checked_add(Duration::from_secs(49 * 365 * 24 * 3600))
981+
.unwrap(),
982+
);
984983

985984
let keypair2 = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng);
986985
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
14101409
.build_with_rng(&root, SymbolTable::default(), &mut rng)
14111410
.unwrap();
14121411

1413-
let mut block2 = BlockBuilder::new();
1414-
1415-
// this one does not go through the parser because it checks for unused variables
1416-
block2
1417-
.add_rule(rule(
1412+
let block2 = BlockBuilder::new()
1413+
// this one does not go through the parser because it checks for unused variables
1414+
.rule(rule(
14181415
"operation",
14191416
&[var("unbound"), string("read")],
14201417
&[pred("operation", &[var("any1"), var("any2")])],

biscuit-auth/examples/third_party.rs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,22 @@
11
use biscuit_auth::{
2-
builder::Algorithm, builder::BlockBuilder, datalog::SymbolTable, Biscuit, KeyPair,
2+
builder::{Algorithm, AuthorizerBuilder, BlockBuilder},
3+
builder_ext::AuthorizerExt,
4+
datalog::SymbolTable,
5+
Biscuit, KeyPair,
36
};
47
use rand::{prelude::StdRng, SeedableRng};
58

69
fn main() {
710
let mut rng: StdRng = SeedableRng::seed_from_u64(0);
811
let root = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng);
912
let external = KeyPair::new_with_rng(Algorithm::Ed25519, &mut rng);
10-
11-
let mut builder = Biscuit::builder();
12-
1313
let external_pub = hex::encode(external.public().to_bytes());
1414

15-
builder
16-
.add_check(
15+
let biscuit1 = Biscuit::builder()
16+
.check(
1717
format!("check if external_fact(\"hello\") trusting ed25519/{external_pub}").as_str(),
1818
)
19-
.unwrap();
20-
21-
let biscuit1 = builder
19+
.unwrap()
2220
.build_with_rng(&root, SymbolTable::default(), &mut rng)
2321
.unwrap();
2422

@@ -27,21 +25,28 @@ fn main() {
2725
let serialized_req = biscuit1.third_party_request().unwrap().serialize().unwrap();
2826

2927
let req = biscuit_auth::ThirdPartyRequest::deserialize(&serialized_req).unwrap();
30-
let mut builder = BlockBuilder::new();
31-
builder.add_fact("external_fact(\"hello\")").unwrap();
28+
let builder = BlockBuilder::new()
29+
.fact("external_fact(\"hello\")")
30+
.unwrap();
3231
let res = req.create_block(&external.private(), builder).unwrap();
3332

3433
let biscuit2 = biscuit1.append_third_party(external.public(), res).unwrap();
3534

3635
println!("biscuit2: {}", biscuit2);
3736

38-
let mut authorizer = biscuit1.authorizer().unwrap();
39-
authorizer.allow().unwrap();
37+
let mut authorizer = AuthorizerBuilder::new()
38+
.allow_all()
39+
.build(&biscuit1)
40+
.unwrap();
41+
4042
println!("authorize biscuit1:\n{:?}", authorizer.authorize());
4143
println!("world:\n{}", authorizer.print_world());
4244

43-
let mut authorizer = biscuit2.authorizer().unwrap();
44-
authorizer.allow().unwrap();
45+
let mut authorizer = AuthorizerBuilder::new()
46+
.allow_all()
47+
.build(&biscuit2)
48+
.unwrap();
49+
4550
println!("authorize biscuit2:\n{:?}", authorizer.authorize());
4651
println!("world:\n{}", authorizer.print_world());
4752
}

biscuit-auth/examples/verifying_printer.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use biscuit_auth::PublicKey;
1+
use biscuit_auth::{builder::AuthorizerBuilder, builder_ext::AuthorizerExt, PublicKey};
22

33
fn main() {
44
let mut args = std::env::args();
@@ -25,8 +25,7 @@ fn main() {
2525
}
2626
println!("token:\n{}", token);
2727

28-
let mut authorizer = token.authorizer().unwrap();
29-
authorizer.allow().unwrap();
28+
let mut authorizer = AuthorizerBuilder::new().allow_all().build(&token).unwrap();
3029

3130
println!("authorizer result: {:?}", authorizer.authorize());
3231
println!("authorizer world:\n{}", authorizer.print_world());

biscuit-auth/src/datalog/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,7 @@ impl FactSet {
783783
pub fn iterator<'a>(
784784
&'a self,
785785
block_ids: &'a TrustedOrigins,
786-
) -> impl Iterator<Item = (&Origin, &Fact)> + Clone {
786+
) -> impl Iterator<Item = (&'a Origin, &'a Fact)> + Clone {
787787
self.inner
788788
.iter()
789789
.filter_map(move |(ids, facts)| {

biscuit-auth/src/lib.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
//! // - one for /a/file1.txt and a write operation
8989
//! // - one for /a/file2.txt and a read operation
9090
//!
91-
//! let v1 = authorizer!(r#"
91+
//! let mut v1 = authorizer!(r#"
9292
//! resource("/a/file1.txt");
9393
//! operation("read");
9494
//!
@@ -101,26 +101,29 @@
101101
//! // explicit catch-all deny. here it is not necessary: if no policy
102102
//! // matches, a default deny applies
103103
//! deny if true;
104-
//! "#);
104+
//! "#)
105+
//! .build(&biscuit2)?;
105106
//!
106107
//! let mut v2 = authorizer!(r#"
107108
//! resource("/a/file1.txt");
108109
//! operation("write");
109110
//! allow if right("/a/file1.txt", "write");
110-
//! "#);
111-
//!
111+
//! "#)
112+
//! .build(&biscuit2)?;
113+
//!
112114
//! let mut v3 = authorizer!(r#"
113115
//! resource("/a/file2.txt");
114116
//! operation("read");
115117
//! allow if right("/a/file2.txt", "read");
116-
//! "#);
118+
//! "#)
119+
//! .build(&biscuit2)?;
117120
//!
118121
//! // the token restricts to read operations:
119-
//! assert!(biscuit2.authorize(&v1).is_ok());
122+
//! assert!(v1.authorize().is_ok());
120123
//! // the second verifier requested a read operation
121-
//! assert!(biscuit2.authorize(&v2).is_err());
124+
//! assert!(v2.authorize().is_err());
122125
//! // the third verifier requests /a/file2.txt
123-
//! assert!(biscuit2.authorize(&v3).is_err());
126+
//! assert!(v3.authorize().is_err());
124127
//!
125128
//! Ok(())
126129
//! }

biscuit-auth/src/macros.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
//! expiration = SystemTime::now() + Duration::from_secs(86_400),
2727
//! )).expect("Failed to append block");
2828
//!
29-
//! new_biscuit.authorize(&authorizer!(
29+
//! authorizer!(
3030
//! r#"
3131
//! time({now});
3232
//! operation({operation});
@@ -42,7 +42,11 @@
4242
//! operation = "read",
4343
//! resource = "file1",
4444
//! user_id = "1234",
45-
//! )).expect("Failed to authorize biscuit");
45+
//! )
46+
//! .build(&new_biscuit)
47+
//! .expect("failed to build the authorizer")
48+
//! .authorize()
49+
//! .expect("Failed to authorize biscuit");
4650
//! ```
4751
4852
/// Create an `Authorizer` from a datalog string and optional parameters.
@@ -78,8 +82,8 @@ pub use biscuit_quote::authorizer;
7882
/// now = SystemTime::now()
7983
/// );
8084
///
81-
/// authorizer_merge!(
82-
/// &mut b,
85+
/// b = authorizer_merge!(
86+
/// b,
8387
/// r#"
8488
/// allow if true;
8589
/// "#
@@ -128,8 +132,8 @@ pub use biscuit_quote::biscuit;
128132
/// user_id = "1234"
129133
/// );
130134
///
131-
/// biscuit_merge!(
132-
/// &mut b,
135+
/// b = biscuit_merge!(
136+
/// b,
133137
/// r#"
134138
/// check if time($time), $time < {expiration}
135139
/// "#,
@@ -173,8 +177,8 @@ pub use biscuit_quote::block;
173177
/// user_id = "1234"
174178
/// );
175179
///
176-
/// block_merge!(
177-
/// &mut b,
180+
/// b = block_merge!(
181+
/// b,
178182
/// r#"
179183
/// check if user($id);
180184
/// "#

0 commit comments

Comments
 (0)