diff --git a/biscuit-auth/examples/testcases.rs b/biscuit-auth/examples/testcases.rs index 37f09be5..ad8b6dd8 100644 --- a/biscuit-auth/examples/testcases.rs +++ b/biscuit-auth/examples/testcases.rs @@ -141,6 +141,8 @@ fn main() { add_test_result(&mut results, expressions_v4(&target, &root, test)); + add_test_result(&mut results, reject_if(&target, &root, test)); + if json { let s = serde_json::to_string_pretty(&TestCases { root_private_key: hex::encode(root.private().to_bytes()), @@ -1953,6 +1955,37 @@ fn expressions_v4(target: &str, root: &KeyPair, test: bool) -> TestResult { } } +fn reject_if(target: &str, root: &KeyPair, test: bool) -> TestResult { + let mut rng: StdRng = SeedableRng::seed_from_u64(1234); + let title = "test reject if".to_string(); + let filename = "test029_reject_if".to_string(); + let token; + + let biscuit = biscuit!(r#"reject if test($test), $test"#) + .build_with_rng(&root, SymbolTable::default(), &mut rng) + .unwrap(); + token = print_blocks(&biscuit); + + let data = write_or_load_testcase(target, &filename, root, &biscuit, test); + + let mut validations = BTreeMap::new(); + validations.insert( + "".to_string(), + validate_token(root, &data[..], "test(false); allow if true"), + ); + validations.insert( + "rejection".to_string(), + validate_token(root, &data[..], "test(true); allow if true"), + ); + + TestResult { + title, + filename, + token, + validations, + } +} + fn print_blocks(token: &Biscuit) -> Vec { let mut v = Vec::new(); diff --git a/biscuit-auth/samples/README.md b/biscuit-auth/samples/README.md index 0e61691a..bec8cedf 100644 --- a/biscuit-auth/samples/README.md +++ b/biscuit-auth/samples/README.md @@ -2361,3 +2361,87 @@ World { result: `Ok(0)` + +------------------------------ + +## test reject if: test029_reject_if.bc +### token + +authority: +symbols: ["test"] + +public keys: [] + +``` +reject if test($test), $test; +``` + +### validation + +authorizer code: +``` +test(false); + +allow if true; +``` + +revocation ids: +- `2060031eb9968b492123440fa9cbc781f18be812961e765a34a8702d3eee0ed54910710efbb41b3141f60748a815012fe0e703a5b5604f4262d1ac7e79766b07` + +authorizer world: +``` +World { + facts: { + ( + "test(false)", + { + None, + }, + ), +} + rules: {} + checks: { + "reject if test($test), $test", +} + policies: { + "allow if true", +} +} +``` + +result: `Ok(0)` +### validation for "rejection" + +authorizer code: +``` +test(true); + +allow if true; +``` + +revocation ids: +- `2060031eb9968b492123440fa9cbc781f18be812961e765a34a8702d3eee0ed54910710efbb41b3141f60748a815012fe0e703a5b5604f4262d1ac7e79766b07` + +authorizer world: +``` +World { + facts: { + ( + "test(true)", + { + None, + }, + ), +} + rules: {} + checks: { + "reject if test($test), $test", +} + policies: { + "allow if true", +} +} +``` + +result: `Err(FailedLogic(Unauthorized { policy: Allow(0), checks: [Block(FailedBlockCheck { block_id: 0, check_id: 0, rule: "reject if test($test), $test" })] }))` + diff --git a/biscuit-auth/samples/samples.json b/biscuit-auth/samples/samples.json index acae28a8..28730f32 100644 --- a/biscuit-auth/samples/samples.json +++ b/biscuit-auth/samples/samples.json @@ -2177,6 +2177,91 @@ ] } } + }, + { + "title": "test reject if", + "filename": "test029_reject_if.bc", + "token": [ + { + "symbols": [ + "test" + ], + "public_keys": [], + "external_key": null, + "code": "reject if test($test), $test;\n" + } + ], + "validations": { + "": { + "world": { + "facts": [ + [ + "test(false)", + [ + null + ] + ] + ], + "rules": [], + "checks": [ + "reject if test($test), $test" + ], + "policies": [ + "allow if true" + ] + }, + "result": { + "Ok": 0 + }, + "authorizer_code": "test(false);\n\nallow if true;\n", + "revocation_ids": [ + "2060031eb9968b492123440fa9cbc781f18be812961e765a34a8702d3eee0ed54910710efbb41b3141f60748a815012fe0e703a5b5604f4262d1ac7e79766b07" + ] + }, + "rejection": { + "world": { + "facts": [ + [ + "test(true)", + [ + null + ] + ] + ], + "rules": [], + "checks": [ + "reject if test($test), $test" + ], + "policies": [ + "allow if true" + ] + }, + "result": { + "Err": { + "FailedLogic": { + "Unauthorized": { + "policy": { + "Allow": 0 + }, + "checks": [ + { + "Block": { + "block_id": 0, + "check_id": 0, + "rule": "reject if test($test), $test" + } + } + ] + } + } + } + }, + "authorizer_code": "test(true);\n\nallow if true;\n", + "revocation_ids": [ + "2060031eb9968b492123440fa9cbc781f18be812961e765a34a8702d3eee0ed54910710efbb41b3141f60748a815012fe0e703a5b5604f4262d1ac7e79766b07" + ] + } + } } ] } diff --git a/biscuit-auth/samples/test029_reject_if.bc b/biscuit-auth/samples/test029_reject_if.bc new file mode 100644 index 00000000..9d6c4d36 Binary files /dev/null and b/biscuit-auth/samples/test029_reject_if.bc differ diff --git a/biscuit-auth/src/datalog/mod.rs b/biscuit-auth/src/datalog/mod.rs index 8970c183..355e8029 100644 --- a/biscuit-auth/src/datalog/mod.rs +++ b/biscuit-auth/src/datalog/mod.rs @@ -165,14 +165,14 @@ impl Rule { _ => continue, }; } - + origin.insert(rule_origin); Some(Ok((origin, Fact { predicate: p }))) } else {None} }, Err(e) => Some(Err(e)) } - + }) } @@ -190,7 +190,7 @@ impl Rule { match next { None => Ok(false), Some(Ok(_)) => Ok(true), - Some(Err(e)) => Err(Execution::Expression(e)) + Some(Err(e)) => Err(Execution::Expression(e)), } } @@ -214,8 +214,10 @@ impl Rule { Ok(Term::Bool(false)) => { //println!("expr returned {:?}", res); return Ok(false); - }, - Ok(_) => return Err(error::Execution::Expression(error::Expression::InvalidType)), + } + Ok(_) => { + return Err(error::Execution::Expression(error::Expression::InvalidType)) + } Err(e) => { return Err(error::Execution::Expression(e)); } @@ -604,11 +606,10 @@ impl World { for (origin, rule) in rules { for res in rule.apply(it.clone(), *origin, symbols) { match res { - Ok((origin,fact)) => { + Ok((origin, fact)) => { new_facts.insert(&origin, fact); - - }, - Err(e) => { + } + Err(e) => { return Err(Execution::Expression(e)); } } @@ -625,7 +626,9 @@ impl World { index += 1; if index == limits.max_iterations { - break Err(Execution::RunLimit( crate::error::RunLimit::TooManyIterations)); + break Err(Execution::RunLimit( + crate::error::RunLimit::TooManyIterations, + )); } if self.facts.len() >= limits.max_facts as usize { @@ -678,11 +681,10 @@ impl World { //new_facts.extend(rule.apply(it, origin, symbols)); for res in rule.apply(it.clone(), origin, symbols) { match res { - Ok((origin,fact)) => { + Ok((origin, fact)) => { new_facts.insert(&origin, fact); - - }, - Err(e) => { + } + Err(e) => { return Err(Execution::Expression(e)); } } @@ -840,11 +842,14 @@ pub struct SchemaVersion { contains_scopes: bool, contains_v4: bool, contains_check_all: bool, + contains_reject_if: bool, } impl SchemaVersion { pub fn version(&self) -> u32 { - if self.contains_scopes || self.contains_v4 || self.contains_check_all { + if self.contains_reject_if { + 5 + } else if self.contains_scopes || self.contains_v4 || self.contains_check_all { 4 } else { MIN_SCHEMA_VERSION @@ -863,11 +868,15 @@ impl SchemaVersion { )) } else if self.contains_check_all { Err(error::Format::DeserializationError( - "v3 blocks must not have use all".to_string(), + "v3 blocks must not have check all".to_string(), )) } else { Ok(()) } + } else if version < 5 && self.contains_reject_if { + Err(error::Format::DeserializationError( + "v5 blocks must not have reject if".to_string(), + )) } else { Ok(()) } @@ -887,7 +896,16 @@ pub fn get_schema_version( .iter() .any(|c: &Check| c.queries.iter().any(|q| !q.scopes.is_empty())); - let contains_check_all = checks.iter().any(|c: &Check| c.kind == CheckKind::All); + let mut contains_check_all = false; + let mut contains_reject_if = false; + + for c in checks.iter() { + if c.kind == CheckKind::All { + contains_check_all = true; + } else if c.kind == CheckKind::Reject { + contains_reject_if = true; + } + } let contains_v4 = rules.iter().any(|rule| contains_v4_op(&rule.expressions)) || checks.iter().any(|check| { @@ -901,6 +919,7 @@ pub fn get_schema_version( contains_scopes, contains_v4, contains_check_all, + contains_reject_if, } } @@ -986,19 +1005,21 @@ mod tests { w.run(&syms).unwrap(); println!("parents:"); - let res = w.query_rule( - rule::( - parent, - &[var(&mut syms, "parent"), var(&mut syms, "child")], - &[pred( + let res = w + .query_rule( + rule::( parent, &[var(&mut syms, "parent"), var(&mut syms, "child")], - )], - ), - 0, - &[0].iter().collect(), - &syms, - ).unwrap(); + &[pred( + parent, + &[var(&mut syms, "parent"), var(&mut syms, "child")], + )], + ), + 0, + &[0].iter().collect(), + &syms, + ) + .unwrap(); for (origin, fact) in res.iterator(&[0].iter().collect()) { println!("\t{:?}\t{}", origin, syms.print_fact(fact)); @@ -1035,19 +1056,21 @@ mod tests { ); w.add_fact(&[0].iter().collect(), fact(parent, &[&c, &e])); w.run(&syms).unwrap(); - let res = w.query_rule( - rule::( - grandparent, - &[var(&mut syms, "grandparent"), var(&mut syms, "grandchild")], - &[pred( + let res = w + .query_rule( + rule::( grandparent, &[var(&mut syms, "grandparent"), var(&mut syms, "grandchild")], - )], - ), - 0, - &[0].iter().collect(), - &syms, - ).unwrap(); + &[pred( + grandparent, + &[var(&mut syms, "grandparent"), var(&mut syms, "grandchild")], + )], + ), + 0, + &[0].iter().collect(), + &syms, + ) + .unwrap(); println!("grandparents after inserting parent(C, E): {:?}", res); let res = res @@ -1101,26 +1124,28 @@ mod tests { w.add_fact(&[0].iter().collect(), fact(t2, &[&int(1), &bbb, &int(0)])); w.add_fact(&[0].iter().collect(), fact(t2, &[&int(2), &ccc, &int(1)])); - let res = w.query_rule( - rule( - join, - &[var(&mut syms, "left"), var(&mut syms, "right")], - &[ - pred(t1, &[var(&mut syms, "id"), var(&mut syms, "left")]), - pred( - t2, - &[ - var(&mut syms, "t2_id"), - var(&mut syms, "right"), - var(&mut syms, "id"), - ], - ), - ], - ), - 0, - &[0].iter().collect(), - &syms, - ).unwrap(); + let res = w + .query_rule( + rule( + join, + &[var(&mut syms, "left"), var(&mut syms, "right")], + &[ + pred(t1, &[var(&mut syms, "id"), var(&mut syms, "left")]), + pred( + t2, + &[ + var(&mut syms, "t2_id"), + var(&mut syms, "right"), + var(&mut syms, "id"), + ], + ), + ], + ), + 0, + &[0].iter().collect(), + &syms, + ) + .unwrap(); for (_, fact) in res.iter_all() { println!("\t{}", syms.print_fact(fact)); @@ -1141,33 +1166,35 @@ mod tests { assert_eq!(res2, compared); // test constraints - let res = w.query_rule( - expressed_rule( - join, - &[var(&mut syms, "left"), var(&mut syms, "right")], - &[ - pred(t1, &[var(&mut syms, "id"), var(&mut syms, "left")]), - pred( - t2, - &[ - var(&mut syms, "t2_id"), - var(&mut syms, "right"), - var(&mut syms, "id"), - ], - ), - ], - &[Expression { - ops: vec![ - Op::Value(var(&mut syms, "id")), - Op::Value(Term::Integer(1)), - Op::Binary(Binary::LessThan), + let res = w + .query_rule( + expressed_rule( + join, + &[var(&mut syms, "left"), var(&mut syms, "right")], + &[ + pred(t1, &[var(&mut syms, "id"), var(&mut syms, "left")]), + pred( + t2, + &[ + var(&mut syms, "t2_id"), + var(&mut syms, "right"), + var(&mut syms, "id"), + ], + ), ], - }], - ), - 0, - &[0].iter().collect(), - &syms, - ).unwrap(); + &[Expression { + ops: vec![ + Op::Value(var(&mut syms, "id")), + Op::Value(Term::Integer(1)), + Op::Binary(Binary::LessThan), + ], + }], + ), + 0, + &[0].iter().collect(), + &syms, + ) + .unwrap(); for (_, fact) in res.iter_all() { println!("\t{}", syms.print_fact(fact)); @@ -1252,7 +1279,8 @@ mod tests { 0, &[0].iter().collect(), &syms, - ).unwrap() + ) + .unwrap() .iter_all() .map(|(_, fact)| fact.clone()) .collect() @@ -1403,35 +1431,37 @@ mod tests { w.add_fact(&[0].iter().collect(), fact(x, &[&abc, &int(0), &test])); w.add_fact(&[0].iter().collect(), fact(x, &[&def, &int(2), &hello])); - let res = w.query_rule( - expressed_rule( - int_set, - &[var(&mut syms, "sym"), var(&mut syms, "str")], - &[pred( - x, - &[ - var(&mut syms, "sym"), - var(&mut syms, "int"), - var(&mut syms, "str"), - ], - )], - &[Expression { - ops: vec![ - Op::Value(Term::Set( - [Term::Integer(0), Term::Integer(1)] - .iter() - .cloned() - .collect(), - )), - Op::Value(var(&mut syms, "int")), - Op::Binary(Binary::Contains), - ], - }], - ), - 0, - &[0].iter().collect(), - &syms, - ).unwrap(); + let res = w + .query_rule( + expressed_rule( + int_set, + &[var(&mut syms, "sym"), var(&mut syms, "str")], + &[pred( + x, + &[ + var(&mut syms, "sym"), + var(&mut syms, "int"), + var(&mut syms, "str"), + ], + )], + &[Expression { + ops: vec![ + Op::Value(Term::Set( + [Term::Integer(0), Term::Integer(1)] + .iter() + .cloned() + .collect(), + )), + Op::Value(var(&mut syms, "int")), + Op::Binary(Binary::Contains), + ], + }], + ), + 0, + &[0].iter().collect(), + &syms, + ) + .unwrap(); for (_, fact) in res.iter_all() { println!("\t{}", syms.print_fact(fact)); @@ -1450,37 +1480,39 @@ mod tests { let abc_sym_id = syms.add("abc"); let ghi_sym_id = syms.add("ghi"); - let res = w.query_rule( - expressed_rule( - symbol_set, - &[ - var(&mut syms, "symbol"), - var(&mut syms, "int"), - var(&mut syms, "str"), - ], - &[pred( - x, + let res = w + .query_rule( + expressed_rule( + symbol_set, &[ var(&mut syms, "symbol"), var(&mut syms, "int"), var(&mut syms, "str"), ], - )], - &[Expression { - ops: vec![ - Op::Value(Term::Set( - [abc_sym_id, ghi_sym_id].iter().cloned().collect(), - )), - Op::Value(var(&mut syms, "symbol")), - Op::Binary(Binary::Contains), - Op::Unary(Unary::Negate), - ], - }], - ), - 0, - &[0].iter().collect(), - &syms, - ).unwrap(); + &[pred( + x, + &[ + var(&mut syms, "symbol"), + var(&mut syms, "int"), + var(&mut syms, "str"), + ], + )], + &[Expression { + ops: vec![ + Op::Value(Term::Set( + [abc_sym_id, ghi_sym_id].iter().cloned().collect(), + )), + Op::Value(var(&mut syms, "symbol")), + Op::Binary(Binary::Contains), + Op::Unary(Unary::Negate), + ], + }], + ), + 0, + &[0].iter().collect(), + &syms, + ) + .unwrap(); for (_, fact) in res.iter_all() { println!("\t{}", syms.print_fact(fact)); @@ -1496,34 +1528,36 @@ mod tests { .collect::>(); assert_eq!(res2, compared); - let res = w.query_rule( - expressed_rule( - string_set, - &[ - var(&mut syms, "sym"), - var(&mut syms, "int"), - var(&mut syms, "str"), - ], - &[pred( - x, + let res = w + .query_rule( + expressed_rule( + string_set, &[ var(&mut syms, "sym"), var(&mut syms, "int"), var(&mut syms, "str"), ], - )], - &[Expression { - ops: vec![ - Op::Value(Term::Set([test.clone(), aaa].iter().cloned().collect())), - Op::Value(var(&mut syms, "str")), - Op::Binary(Binary::Contains), - ], - }], - ), - 0, - &[0].iter().collect(), - &syms, - ).unwrap(); + &[pred( + x, + &[ + var(&mut syms, "sym"), + var(&mut syms, "int"), + var(&mut syms, "str"), + ], + )], + &[Expression { + ops: vec![ + Op::Value(Term::Set([test.clone(), aaa].iter().cloned().collect())), + Op::Value(var(&mut syms, "str")), + Op::Binary(Binary::Contains), + ], + }], + ), + 0, + &[0].iter().collect(), + &syms, + ) + .unwrap(); for (_, fact) in res.iter_all() { println!("\t{}", syms.print_fact(fact)); @@ -1561,12 +1595,14 @@ mod tests { w.add_fact(&[0].iter().collect(), fact(right, &[&file2, &read])); w.add_fact(&[0].iter().collect(), fact(right, &[&file1, &write])); - let res = w.query_rule( - rule(check1, &[&file1], &[pred(resource, &[&file1])]), - 0, - &[0].iter().collect(), - &syms, - ).unwrap(); + let res = w + .query_rule( + rule(check1, &[&file1], &[pred(resource, &[&file1])]), + 0, + &[0].iter().collect(), + &syms, + ) + .unwrap(); for (_, fact) in res.iter_all() { println!("\t{}", syms.print_fact(fact)); @@ -1574,20 +1610,22 @@ mod tests { assert!(res.len() == 0); - let res = w.query_rule( - rule( - check2, - &[Term::Variable(0)], - &[ - pred(resource, &[&Term::Variable(0)]), - pred(operation, &[&read]), - pred(right, &[&Term::Variable(0), &read]), - ], - ), - 0, - &[0].iter().collect(), - &syms, - ).unwrap(); + let res = w + .query_rule( + rule( + check2, + &[Term::Variable(0)], + &[ + pred(resource, &[&Term::Variable(0)]), + pred(operation, &[&read]), + pred(right, &[&Term::Variable(0), &read]), + ], + ), + 0, + &[0].iter().collect(), + &syms, + ) + .unwrap(); for (_, fact) in res.iter_all() { println!("\t{}", syms.print_fact(fact)); diff --git a/biscuit-auth/src/datalog/symbol.rs b/biscuit-auth/src/datalog/symbol.rs index df5b398d..5f1a616d 100644 --- a/biscuit-auth/src/datalog/symbol.rs +++ b/biscuit-auth/src/datalog/symbol.rs @@ -282,10 +282,11 @@ impl SymbolTable { .collect::>(); format!( - "check {} {}", + "{} {}", match c.kind { - crate::builder::CheckKind::One => "if", - crate::builder::CheckKind::All => "all", + crate::builder::CheckKind::One => "check if", + crate::builder::CheckKind::All => "check all", + crate::builder::CheckKind::Reject => "reject if", }, queries.join(" or ") ) diff --git a/biscuit-auth/src/format/convert.rs b/biscuit-auth/src/format/convert.rs index a049ca93..d7ab9797 100644 --- a/biscuit-auth/src/format/convert.rs +++ b/biscuit-auth/src/format/convert.rs @@ -71,10 +71,19 @@ pub fn proto_block_to_token_block( rules.push(v2::proto_rule_to_token_rule(rule, version)?.0); } - if version == MIN_SCHEMA_VERSION && input.checks_v2.iter().any(|c| c.kind.is_some()) { - return Err(error::Format::DeserializationError( - "deserialization error: v3 blocks must not contain a check kind".to_string(), - )); + if version < MAX_SCHEMA_VERSION { + for c in input.checks_v2.iter() { + if version == MIN_SCHEMA_VERSION && c.kind.is_some() { + return Err(error::Format::DeserializationError( + "deserialization error: v3 blocks must not contain a check kind".to_string(), + )); + } else if c.kind == Some(schema::check_v2::Kind::Reject as i32) { + return Err(error::Format::DeserializationError( + "deserialization error: v4 blocks must not contain reject if checks" + .to_string(), + )); + } + } } for check in input.checks_v2.iter() { @@ -326,6 +335,7 @@ pub mod v2 { kind: match input.kind { crate::token::builder::CheckKind::One => None, crate::token::builder::CheckKind::All => Some(Kind::All as i32), + crate::token::builder::CheckKind::Reject => Some(Kind::Reject as i32), }, } } @@ -343,6 +353,7 @@ pub mod v2 { let kind = match input.kind { None | Some(0) => crate::token::builder::CheckKind::One, Some(1) => crate::token::builder::CheckKind::All, + Some(2) => crate::token::builder::CheckKind::Reject, _ => { return Err(error::Format::DeserializationError( "deserialization error: invalid check kind".to_string(), diff --git a/biscuit-auth/src/format/schema.proto b/biscuit-auth/src/format/schema.proto index bc6cb295..351ff3ad 100644 --- a/biscuit-auth/src/format/schema.proto +++ b/biscuit-auth/src/format/schema.proto @@ -80,6 +80,7 @@ message CheckV2 { enum Kind { One = 0; All = 1; + Reject = 2; } } diff --git a/biscuit-auth/src/format/schema.rs b/biscuit-auth/src/format/schema.rs index 869858f9..19d7e851 100644 --- a/biscuit-auth/src/format/schema.rs +++ b/biscuit-auth/src/format/schema.rs @@ -127,6 +127,7 @@ pub mod check_v2 { pub enum Kind { One = 0, All = 1, + Reject = 2, } } #[derive(Clone, PartialEq, ::prost::Message)] diff --git a/biscuit-auth/src/token/authorizer.rs b/biscuit-auth/src/token/authorizer.rs index bed6eb6e..2ed0da73 100644 --- a/biscuit-auth/src/token/authorizer.rs +++ b/biscuit-auth/src/token/authorizer.rs @@ -759,6 +759,12 @@ impl Authorizer { self.world .query_match_all(query, &rule_trusted_origins, &self.symbols)? } + CheckKind::Reject => !self.world.query_match( + query, + usize::MAX, + &rule_trusted_origins, + &self.symbols, + )?, }; let now = Instant::now(); @@ -812,6 +818,12 @@ impl Authorizer { &rule_trusted_origins, &self.symbols, )?, + CheckKind::Reject => !self.world.query_match( + query.clone(), + 0, + &rule_trusted_origins, + &self.symbols, + )?, }; let now = Instant::now(); @@ -905,6 +917,12 @@ impl Authorizer { &rule_trusted_origins, &self.symbols, )?, + CheckKind::Reject => !self.world.query_match( + query.clone(), + i + 1, + &rule_trusted_origins, + &self.symbols, + )?, }; let now = Instant::now(); diff --git a/biscuit-auth/src/token/builder.rs b/biscuit-auth/src/token/builder.rs index 3335bd1c..9bd19f79 100644 --- a/biscuit-auth/src/token/builder.rs +++ b/biscuit-auth/src/token/builder.rs @@ -1435,6 +1435,7 @@ pub struct Check { pub enum CheckKind { One, All, + Reject, } impl Check { @@ -1584,6 +1585,7 @@ impl fmt::Display for Check { match self.kind { CheckKind::One => write!(f, "check if ")?, CheckKind::All => write!(f, "check all ")?, + CheckKind::Reject => write!(f, "reject if ")?, }; if !self.queries.is_empty() { @@ -1612,6 +1614,7 @@ impl From for Check { kind: match c.kind { biscuit_parser::builder::CheckKind::One => CheckKind::One, biscuit_parser::builder::CheckKind::All => CheckKind::All, + biscuit_parser::builder::CheckKind::Reject => CheckKind::Reject, }, } } diff --git a/biscuit-auth/src/token/third_party.rs b/biscuit-auth/src/token/third_party.rs index dd3ffa84..70378fd0 100644 --- a/biscuit-auth/src/token/third_party.rs +++ b/biscuit-auth/src/token/third_party.rs @@ -1,3 +1,5 @@ +use std::cmp::max; + use ed25519_dalek::Signer; use prost::Message; @@ -124,7 +126,7 @@ impl ThirdPartyRequest { let mut symbols = SymbolTable::new(); symbols.public_keys = self.public_keys.clone(); let mut block = block_builder.build(symbols); - block.version = super::THIRD_PARTY_BLOCK_VERSION; + block.version = max(super::THIRD_PARTY_BLOCK_VERSION, block.version); let mut v = Vec::new(); token_block_to_proto_block(&block) diff --git a/biscuit-parser/src/builder.rs b/biscuit-parser/src/builder.rs index 77897cc7..c5efe188 100644 --- a/biscuit-parser/src/builder.rs +++ b/biscuit-parser/src/builder.rs @@ -55,7 +55,7 @@ impl ToTokens for Term { Term::Set(v) => { quote! {{ use std::iter::FromIterator; - ::biscuit_auth::builder::Term::Set(::std::collections::BTreeSet::from_iter(<[::biscuit_auth::builder::Term]>::into_vec(Box::new([ #(#v),*])))) + ::biscuit_auth::builder::Term::Set(::std::collections::BTreeSet::from_iter(<[::biscuit_auth::builder::Term]>::into_vec(Box::new([ #(#v),*])))) }} } }) @@ -393,6 +393,7 @@ pub struct Check { pub enum CheckKind { One, All, + Reject, } #[cfg(feature = "datalog-macro")] @@ -419,6 +420,9 @@ impl ToTokens for CheckKind { CheckKind::All => quote! { ::biscuit_auth::builder::CheckKind::All }, + CheckKind::Reject => quote! { + ::biscuit_auth::builder::CheckKind::Reject + }, }); } } diff --git a/biscuit-parser/src/parser.rs b/biscuit-parser/src/parser.rs index 392e46b3..66da7cea 100644 --- a/biscuit-parser/src/parser.rs +++ b/biscuit-parser/src/parser.rs @@ -70,6 +70,7 @@ fn check_inner(i: &str) -> IResult<&str, builder::Check, Error> { let (i, kind) = alt(( map(tag_no_case("check if"), |_| CheckKind::One), map(tag_no_case("check all"), |_| CheckKind::All), + map(tag_no_case("reject if"), |_| CheckKind::Reject), ))(i)?; let (i, queries) = cut(check_body)(i)?;