diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index fac2c22c..48802226 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -33,7 +33,7 @@ jobs: - name: Check samples run: | cd biscuit-auth - cargo run --release --example testcases --features serde-error -- ./samples > ./samples/README.md + cargo run --release --example testcases --features serde-error -- ./samples --json > ./samples/samples.json git diff --exit-code diff --git a/biscuit-auth/CHANGELOG.md b/biscuit-auth/CHANGELOG.md index e9895a35..8d0792e7 100644 --- a/biscuit-auth/CHANGELOG.md +++ b/biscuit-auth/CHANGELOG.md @@ -1,5 +1,17 @@ # not released +# `4.1.1` + +- remove PKCS8 file loading functions (#208) + +# `4.1.0` (yanked) + +**This release was yanked because PKCS8 file loading functions, activated by the `pem` feature, could not compile. Those functions are removed in #208. PKCS8 parsing from buffers is still available** + +- fix: typo in documentation (#190) (Rémi Duraffort) +- fix: include all authorizer facts and rules when using `Display` (#195) (Clément Delafargue) +- Add optional support for PEM/DER parsing (#204) (Baran Yildirim) + # `4.0.0` - macros for individual statements: `fact!`, `check!`, `policy!` (#175, #176) (Clément Delafargue) diff --git a/biscuit-auth/Cargo.toml b/biscuit-auth/Cargo.toml index 919127fb..e05e0c82 100644 --- a/biscuit-auth/Cargo.toml +++ b/biscuit-auth/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "biscuit-auth" -version = "4.0.0" +version = "4.1.1" description = "an authorization token with decentralized verification and offline attenuation" authors = ["Geoffroy Couprie "] edition = "2018" @@ -24,6 +24,8 @@ datalog-macro = ["biscuit-quote"] bwk = ["chrono", "serde"] docsrs = [] uuid = ["dep:uuid"] +# used to expose pem/der loaders for keypairs +pem = ["ed25519-dalek/pem"] [dependencies] rand_core = "^0.6" diff --git a/biscuit-auth/examples/testcases.rs b/biscuit-auth/examples/testcases.rs index 09cfff64..8f499201 100644 --- a/biscuit-auth/examples/testcases.rs +++ b/biscuit-auth/examples/testcases.rs @@ -250,10 +250,28 @@ impl TestResult { #[derive(Debug, Serialize)] struct AuthorizerWorld { - pub facts: BTreeSet<(String, BTreeSet>)>, - pub rules: BTreeSet<(String, Option)>, - pub checks: BTreeSet, - pub policies: BTreeSet, + pub facts: Vec, + pub rules: Vec, + pub checks: Vec, + pub policies: Vec, +} + +#[derive(Debug, Serialize, PartialEq, Eq, PartialOrd, Ord)] +struct AuthorizerFactSet { + origin: BTreeSet>, + facts: Vec, +} + +#[derive(Debug, Serialize, PartialEq, Eq, PartialOrd, Ord)] +struct AuthorizerRuleSet { + origin: Option, + rules: Vec, +} + +#[derive(Debug, Serialize, PartialEq, Eq, PartialOrd, Ord)] +struct AuthorizerCheckSet { + origin: Option, + checks: Vec, } #[derive(Debug, Serialize)] @@ -299,7 +317,7 @@ fn validate_token(root: &KeyPair, data: &[u8], authorizer_code: &str) -> Validat let res = authorizer.authorize(); //println!("authorizer world:\n{}", authorizer.print_world()); - let (_, _, mut checks, mut policies) = authorizer.dump(); + let (_, _, _, policies) = authorizer.dump(); let snapshot = authorizer.snapshot().unwrap(); let symbols = SymbolTable::from_symbols_and_public_keys( @@ -313,23 +331,65 @@ fn validate_token(root: &KeyPair, data: &[u8], authorizer_code: &str) -> Validat ) .unwrap(); - let mut facts = BTreeSet::new(); - let mut rules = BTreeSet::new(); + let mut authorizer_facts = Vec::new(); + let mut authorizer_rules = Vec::new(); + let mut authorizer_checks = Vec::new(); for (i, block) in snapshot.world.blocks.iter().enumerate() { - let mut origin = BTreeSet::new(); - origin.insert(i); + let mut rules: Vec = Vec::new(); for rule in block.rules_v2.iter() { let r = convert::proto_rule_to_token_rule(&rule, snapshot.world.version.unwrap()).unwrap(); - rules.insert((symbols.print_rule(&r.0), Some(i))); + rules.push(symbols.print_rule(&r.0)); + } + if !rules.is_empty() { + rules.sort(); + authorizer_rules.push(AuthorizerRuleSet { + origin: Some(i), + rules, + }); + } + + let mut checks = Vec::new(); + for check in block.checks_v2.iter() { + let c = convert::proto_check_to_token_check(&check, snapshot.world.version.unwrap()) + .unwrap(); + checks.push(symbols.print_check(&c)); + } + if !checks.is_empty() { + checks.sort(); + authorizer_checks.push(AuthorizerCheckSet { + origin: Some(i), + checks, + }); } } - let mut authorizer_origin = BTreeSet::new(); - authorizer_origin.insert(usize::MAX); + let mut rules: Vec = Vec::new(); for rule in snapshot.world.authorizer_block.rules_v2 { let r = convert::proto_rule_to_token_rule(&rule, snapshot.world.version.unwrap()).unwrap(); - rules.insert((symbols.print_rule(&r.0), None)); + + rules.push(symbols.print_rule(&r.0)); + } + if !rules.is_empty() { + rules.sort(); + authorizer_rules.push(AuthorizerRuleSet { + origin: Some(usize::MAX), + rules, + }); + } + + let mut checks = Vec::new(); + for check in snapshot.world.authorizer_block.checks_v2 { + let c = + convert::proto_check_to_token_check(&check, snapshot.world.version.unwrap()).unwrap(); + checks.push(symbols.print_check(&c)); + } + if !checks.is_empty() { + checks.sort(); + authorizer_checks.push(AuthorizerCheckSet { + origin: Some(usize::MAX), + checks, + }); } for factset in snapshot.world.generated_facts { @@ -343,18 +403,25 @@ fn validate_token(root: &KeyPair, data: &[u8], authorizer_code: &str) -> Validat }; } + let mut facts = Vec::new(); + for fact in factset.facts { let f = convert::proto_fact_to_token_fact(&fact).unwrap(); - facts.insert((symbols.print_fact(&f), origin.clone())); + facts.push(symbols.print_fact(&f)); + } + if !facts.is_empty() { + facts.sort(); + authorizer_facts.push(AuthorizerFactSet { origin, facts }); } } + authorizer_facts.sort(); Validation { world: Some(AuthorizerWorld { - facts, - rules, - checks: checks.drain(..).map(|c| c.to_string()).collect(), - policies: policies.drain(..).map(|p| p.to_string()).collect(), + facts: authorizer_facts, + rules: authorizer_rules, + checks: authorizer_checks, + policies: policies.into_iter().map(|p| p.to_string()).collect(), }), result: match res { Ok(i) => AuthorizerResult::Ok(i), diff --git a/biscuit-auth/samples/README.md b/biscuit-auth/samples/README.md index 0f915a7b..e123c7c6 100644 --- a/biscuit-auth/samples/README.md +++ b/biscuit-auth/samples/README.md @@ -44,45 +44,42 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "resource(\"file1\")", - { + facts: [ + AuthorizerFactSet { + origin: { None, }, - ), - ( - "right(\"file1\", \"read\")", - { + facts: [ + "resource(\"file1\")", + ], + }, + AuthorizerFactSet { + origin: { Some( 0, ), }, - ), - ( - "right(\"file1\", \"write\")", - { - Some( - 0, - ), - }, - ), - ( - "right(\"file2\", \"read\")", - { - Some( - 0, - ), - }, - ), -} - rules: {} - checks: { - "check if resource($0), operation(\"read\"), right($0, \"read\")", -} - policies: { + facts: [ + "right(\"file1\", \"read\")", + "right(\"file1\", \"write\")", + "right(\"file2\", \"read\")", + ], + }, +] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 1, + ), + checks: [ + "check if resource($0), operation(\"read\"), right($0, \"read\")", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -298,58 +295,61 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "operation(\"read\")", - { + facts: [ + AuthorizerFactSet { + origin: { None, }, - ), - ( - "owner(\"alice\", \"file1\")", - { + facts: [ + "operation(\"read\")", + "resource(\"file2\")", + ], + }, + AuthorizerFactSet { + origin: { Some( 0, ), }, - ), - ( - "owner(\"alice\", \"file2\")", - { + facts: [ + "owner(\"alice\", \"file1\")", + "user_id(\"alice\")", + ], + }, + AuthorizerFactSet { + origin: { Some( 2, ), }, - ), - ( - "resource(\"file2\")", - { - None, - }, - ), - ( - "user_id(\"alice\")", - { - Some( - 0, - ), - }, - ), -} - rules: { - ( - "right($0, \"read\") <- resource($0), user_id($1), owner($1, $0)", - Some( + facts: [ + "owner(\"alice\", \"file2\")", + ], + }, +] + rules: [ + AuthorizerRuleSet { + origin: Some( 1, ), - ), -} - checks: { - "check if resource($0), operation(\"read\"), right($0, \"read\")", -} - policies: { + rules: [ + "right($0, \"read\") <- resource($0), user_id($1), owner($1, $0)", + ], + }, +] + checks: [ + AuthorizerCheckSet { + origin: Some( + 1, + ), + checks: [ + "check if resource($0), operation(\"read\"), right($0, \"read\")", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -406,43 +406,51 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "operation(\"read\")", - { + facts: [ + AuthorizerFactSet { + origin: { None, }, - ), - ( - "resource(\"file2\")", - { - None, - }, - ), - ( - "right(\"file1\", \"read\")", - { + facts: [ + "operation(\"read\")", + "resource(\"file2\")", + ], + }, + AuthorizerFactSet { + origin: { Some( 0, ), }, - ), - ( - "right(\"file2\", \"read\")", - { + facts: [ + "right(\"file1\", \"read\")", + ], + }, + AuthorizerFactSet { + origin: { Some( 2, ), }, - ), -} - rules: {} - checks: { - "check if resource($0), operation(\"read\"), right($0, \"read\")", -} - policies: { + facts: [ + "right(\"file2\", \"read\")", + ], + }, +] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 1, + ), + checks: [ + "check if resource($0), operation(\"read\"), right($0, \"read\")", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -490,34 +498,33 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "operation(\"read\")", - { + facts: [ + AuthorizerFactSet { + origin: { None, }, - ), - ( - "resource(\"file1\")", - { - None, - }, - ), - ( - "time(2020-12-21T09:23:12Z)", - { - None, - }, - ), -} - rules: {} - checks: { - "check if resource(\"file1\")", - "check if time($time), $time <= 2018-12-20T00:00:00Z", -} - policies: { + facts: [ + "operation(\"read\")", + "resource(\"file1\")", + "time(2020-12-21T09:23:12Z)", + ], + }, +] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 1, + ), + checks: [ + "check if resource(\"file1\")", + "check if time($time), $time <= 2018-12-20T00:00:00Z", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -566,43 +573,51 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "operation(\"read\")", - { + facts: [ + AuthorizerFactSet { + origin: { None, }, - ), - ( - "resource(\"file2\")", - { - None, - }, - ), - ( - "right(\"file1\", \"read\")", - { + facts: [ + "operation(\"read\")", + "resource(\"file2\")", + ], + }, + AuthorizerFactSet { + origin: { Some( 0, ), }, - ), - ( - "right(\"file2\", \"read\")", - { + facts: [ + "right(\"file1\", \"read\")", + ], + }, + AuthorizerFactSet { + origin: { Some( 1, ), }, - ), -} - rules: {} - checks: { - "check if right($0, $1), resource($0), operation($1)", -} - policies: { + facts: [ + "right(\"file2\", \"read\")", + ], + }, +] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 18446744073709551615, + ), + checks: [ + "check if right($0, $1), resource($0), operation($1)", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -641,35 +656,41 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "operation(\"read\")", - { - None, - }, - ), - ( - "resource(\"file2\")", - { + facts: [ + AuthorizerFactSet { + origin: { None, }, - ), - ( - "right(\"file1\", \"read\")", - { + facts: [ + "operation(\"read\")", + "resource(\"file2\")", + ], + }, + AuthorizerFactSet { + origin: { Some( 0, ), }, - ), -} - rules: {} - checks: { - "check if right($0, $1), resource($0), operation($1)", -} - policies: { + facts: [ + "right(\"file1\", \"read\")", + ], + }, +] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 18446744073709551615, + ), + checks: [ + "check if right($0, $1), resource($0), operation($1)", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -706,27 +727,31 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "operation(\"read\")", - { + facts: [ + AuthorizerFactSet { + origin: { None, }, - ), - ( - "resource(\"file1\")", - { - None, - }, - ), -} - rules: {} - checks: { - "check if resource(\"file1\")", -} - policies: { + facts: [ + "operation(\"read\")", + "resource(\"file1\")", + ], + }, +] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 0, + ), + checks: [ + "check if resource(\"file1\")", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -747,27 +772,31 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "operation(\"read\")", - { - None, - }, - ), - ( - "resource(\"file2\")", - { + facts: [ + AuthorizerFactSet { + origin: { None, }, - ), -} - rules: {} - checks: { - "check if resource(\"file1\")", -} - policies: { + facts: [ + "operation(\"read\")", + "resource(\"file2\")", + ], + }, +] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 0, + ), + checks: [ + "check if resource(\"file1\")", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -817,65 +846,63 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "resource(\"file1\")", - { + facts: [ + AuthorizerFactSet { + origin: { None, }, - ), - ( - "right(\"file1\", \"read\")", - { + facts: [ + "resource(\"file1\")", + "time(2020-12-21T09:23:12Z)", + ], + }, + AuthorizerFactSet { + origin: { + None, Some( - 0, + 1, ), }, - ), - ( - "right(\"file2\", \"read\")", - { + facts: [ + "valid_date(\"file1\")", + ], + }, + AuthorizerFactSet { + origin: { Some( 0, ), }, - ), - ( - "time(2020-12-21T09:23:12Z)", - { - None, - }, - ), - ( - "valid_date(\"file1\")", - { - None, - Some( - 1, - ), - }, - ), -} - rules: { - ( - "valid_date(\"file1\") <- time($0), resource(\"file1\"), $0 <= 2030-12-31T12:59:59Z", - Some( + facts: [ + "right(\"file1\", \"read\")", + "right(\"file2\", \"read\")", + ], + }, +] + rules: [ + AuthorizerRuleSet { + origin: Some( 1, ), - ), - ( - "valid_date($1) <- time($0), resource($1), $0 <= 1999-12-31T12:59:59Z, ![\"file1\"].contains($1)", - Some( + rules: [ + "valid_date(\"file1\") <- time($0), resource(\"file1\"), $0 <= 2030-12-31T12:59:59Z", + "valid_date($1) <- time($0), resource($1), $0 <= 1999-12-31T12:59:59Z, ![\"file1\"].contains($1)", + ], + }, +] + checks: [ + AuthorizerCheckSet { + origin: Some( 1, ), - ), -} - checks: { - "check if valid_date($0), resource($0)", -} - policies: { + checks: [ + "check if valid_date($0), resource($0)", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -897,56 +924,52 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "resource(\"file2\")", - { + facts: [ + AuthorizerFactSet { + origin: { None, }, - ), - ( - "right(\"file1\", \"read\")", - { - Some( - 0, - ), - }, - ), - ( - "right(\"file2\", \"read\")", - { + facts: [ + "resource(\"file2\")", + "time(2020-12-21T09:23:12Z)", + ], + }, + AuthorizerFactSet { + origin: { Some( 0, ), }, - ), - ( - "time(2020-12-21T09:23:12Z)", - { - None, - }, - ), -} - rules: { - ( - "valid_date(\"file1\") <- time($0), resource(\"file1\"), $0 <= 2030-12-31T12:59:59Z", - Some( + facts: [ + "right(\"file1\", \"read\")", + "right(\"file2\", \"read\")", + ], + }, +] + rules: [ + AuthorizerRuleSet { + origin: Some( 1, ), - ), - ( - "valid_date($1) <- time($0), resource($1), $0 <= 1999-12-31T12:59:59Z, ![\"file1\"].contains($1)", - Some( + rules: [ + "valid_date(\"file1\") <- time($0), resource(\"file1\"), $0 <= 2030-12-31T12:59:59Z", + "valid_date($1) <- time($0), resource($1), $0 <= 1999-12-31T12:59:59Z, ![\"file1\"].contains($1)", + ], + }, +] + checks: [ + AuthorizerCheckSet { + origin: Some( 1, ), - ), -} - checks: { - "check if valid_date($0), resource($0)", -} - policies: { + checks: [ + "check if valid_date($0), resource($0)", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -982,21 +1005,30 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "resource(\"file1\")", - { + facts: [ + AuthorizerFactSet { + origin: { None, }, - ), -} - rules: {} - checks: { - "check if resource($0), $0.matches(\"file[0-9]+.txt\")", -} - policies: { + facts: [ + "resource(\"file1\")", + ], + }, +] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 0, + ), + checks: [ + "check if resource($0), $0.matches(\"file[0-9]+.txt\")", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -1016,21 +1048,30 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "resource(\"file123.txt\")", - { + facts: [ + AuthorizerFactSet { + origin: { None, }, - ), -} - rules: {} - checks: { - "check if resource($0), $0.matches(\"file[0-9]+.txt\")", -} - policies: { + facts: [ + "resource(\"file123.txt\")", + ], + }, +] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 0, + ), + checks: [ + "check if resource($0), $0.matches(\"file[0-9]+.txt\")", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -1066,23 +1107,32 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "must_be_present(\"hello\")", - { + facts: [ + AuthorizerFactSet { + origin: { Some( 0, ), }, - ), -} - rules: {} - checks: { - "check if must_be_present($0) or must_be_present($0)", -} - policies: { + facts: [ + "must_be_present(\"hello\")", + ], + }, +] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 18446744073709551615, + ), + checks: [ + "check if must_be_present($0) or must_be_present($0)", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -1126,23 +1176,32 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "query(\"test\")", - { + facts: [ + AuthorizerFactSet { + origin: { Some( 1, ), }, - ), -} - rules: {} - checks: { - "check if resource(\"hello\")", -} - policies: { + facts: [ + "query(\"test\")", + ], + }, +] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 0, + ), + checks: [ + "check if resource(\"hello\")", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -1214,51 +1273,59 @@ revocation ids: authorizer world: ``` World { - facts: {} - rules: {} - checks: { - "check if !false", - "check if !false && true", - "check if \"aaabde\" == \"aaa\" + \"b\" + \"de\"", - "check if \"aaabde\".contains(\"abd\")", - "check if \"aaabde\".matches(\"a*c?.e\")", - "check if \"abcD12\" == \"abcD12\"", - "check if \"hello world\".starts_with(\"hello\") && \"hello world\".ends_with(\"world\")", - "check if (true || false) && true", - "check if 1 + 2 * 3 - 4 / 2 == 5", - "check if 1 < 2", - "check if 1 <= 1", - "check if 1 <= 2", - "check if 2 > 1", - "check if 2 >= 1", - "check if 2 >= 2", - "check if 2019-12-04T09:46:41Z < 2020-12-04T09:46:41Z", - "check if 2019-12-04T09:46:41Z <= 2020-12-04T09:46:41Z", - "check if 2020-12-04T09:46:41Z == 2020-12-04T09:46:41Z", - "check if 2020-12-04T09:46:41Z > 2019-12-04T09:46:41Z", - "check if 2020-12-04T09:46:41Z >= 2019-12-04T09:46:41Z", - "check if 2020-12-04T09:46:41Z >= 2020-12-04T09:46:41Z", - "check if 3 == 3", - "check if [\"abc\", \"def\"].contains(\"abc\")", - "check if [1, 2, 3].intersection([1, 2]).contains(1)", - "check if [1, 2, 3].intersection([1, 2]).length() == 2", - "check if [1, 2] == [1, 2]", - "check if [1, 2].contains(2)", - "check if [1, 2].contains([2])", - "check if [1, 2].intersection([2, 3]) == [2]", - "check if [1, 2].union([2, 3]) == [1, 2, 3]", - "check if [2019-12-04T09:46:41Z, 2020-12-04T09:46:41Z].contains(2020-12-04T09:46:41Z)", - "check if [false, true].contains(true)", - "check if [hex:12ab, hex:34de].contains(hex:34de)", - "check if false == false", - "check if false || true", - "check if hex:12ab == hex:12ab", - "check if true", - "check if true == true", -} - policies: { + facts: [] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 0, + ), + checks: [ + "check if !false", + "check if !false && true", + "check if \"aaabde\" == \"aaa\" + \"b\" + \"de\"", + "check if \"aaabde\".contains(\"abd\")", + "check if \"aaabde\".matches(\"a*c?.e\")", + "check if \"abcD12\" == \"abcD12\"", + "check if \"hello world\".starts_with(\"hello\") && \"hello world\".ends_with(\"world\")", + "check if (true || false) && true", + "check if 1 + 2 * 3 - 4 / 2 == 5", + "check if 1 < 2", + "check if 1 <= 1", + "check if 1 <= 2", + "check if 2 > 1", + "check if 2 >= 1", + "check if 2 >= 2", + "check if 2019-12-04T09:46:41Z < 2020-12-04T09:46:41Z", + "check if 2019-12-04T09:46:41Z <= 2020-12-04T09:46:41Z", + "check if 2020-12-04T09:46:41Z == 2020-12-04T09:46:41Z", + "check if 2020-12-04T09:46:41Z > 2019-12-04T09:46:41Z", + "check if 2020-12-04T09:46:41Z >= 2019-12-04T09:46:41Z", + "check if 2020-12-04T09:46:41Z >= 2020-12-04T09:46:41Z", + "check if 2020-12-04T09:46:41Z >= 2020-12-04T09:46:41Z", + "check if 3 == 3", + "check if [\"abc\", \"def\"].contains(\"abc\")", + "check if [1, 2, 3].intersection([1, 2]).contains(1)", + "check if [1, 2, 3].intersection([1, 2]).length() == 2", + "check if [1, 2] == [1, 2]", + "check if [1, 2].contains(2)", + "check if [1, 2].contains([2])", + "check if [1, 2].intersection([2, 3]) == [2]", + "check if [1, 2].union([2, 3]) == [1, 2, 3]", + "check if [2019-12-04T09:46:41Z, 2020-12-04T09:46:41Z].contains(2020-12-04T09:46:41Z)", + "check if [false, true].contains(true)", + "check if [hex:12ab, hex:34de].contains(hex:34de)", + "check if false == false", + "check if false || true", + "check if hex:12ab == hex:12ab", + "check if true", + "check if true == true", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -1332,37 +1399,50 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "operation(\"read\")", - { + facts: [ + AuthorizerFactSet { + origin: { + None, + }, + facts: [ + "operation(\"write\")", + ], + }, + AuthorizerFactSet { + origin: { None, Some( 1, ), }, - ), - ( - "operation(\"write\")", - { - None, - }, - ), -} - rules: { - ( - "operation(\"read\") <- operation($any)", - Some( + facts: [ + "operation(\"read\")", + ], + }, +] + rules: [ + AuthorizerRuleSet { + origin: Some( 1, ), - ), -} - checks: { - "check if operation(\"read\")", -} - policies: { + rules: [ + "operation(\"read\") <- operation($any)", + ], + }, +] + checks: [ + AuthorizerCheckSet { + origin: Some( + 0, + ), + checks: [ + "check if operation(\"read\")", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -1411,51 +1491,43 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "operation(\"read\")", - { + facts: [ + AuthorizerFactSet { + origin: { None, }, - ), - ( - "resource(\"file1\")", - { - None, - }, - ), - ( - "right(\"file1\", \"read\")", - { - Some( - 0, - ), - }, - ), - ( - "right(\"file1\", \"write\")", - { + facts: [ + "operation(\"read\")", + "resource(\"file1\")", + ], + }, + AuthorizerFactSet { + origin: { Some( 0, ), }, - ), - ( - "right(\"file2\", \"read\")", - { - Some( - 0, - ), - }, - ), -} - rules: {} - checks: { - "check if resource($0), operation(\"read\"), right($0, \"read\")", -} - policies: { + facts: [ + "right(\"file1\", \"read\")", + "right(\"file1\", \"write\")", + "right(\"file2\", \"read\")", + ], + }, +] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 1, + ), + checks: [ + "check if resource($0), operation(\"read\"), right($0, \"read\")", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -1491,23 +1563,32 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "ns::fact_123(\"hello é\t😁\")", - { + facts: [ + AuthorizerFactSet { + origin: { Some( 0, ), }, - ), -} - rules: {} - checks: { - "check if ns::fact_123(\"hello é\t😁\")", -} - policies: { + facts: [ + "ns::fact_123(\"hello é\t😁\")", + ], + }, +] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 18446744073709551615, + ), + checks: [ + "check if ns::fact_123(\"hello é\t😁\")", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -1570,239 +1651,59 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "admin(13)", - { - Some( - 0, - ), - }, - ), - ( - "client(18)", - { - Some( - 0, - ), - }, - ), - ( - "client_ip(19)", - { + facts: [ + AuthorizerFactSet { + origin: { Some( 0, ), }, - ), - ( - "cluster(23)", - { - Some( - 0, - ), - }, - ), - ( - "domain(20)", - { - Some( - 0, - ), - }, - ), - ( - "email(14)", - { - Some( - 0, - ), - }, - ), - ( - "group(15)", - { - Some( - 0, - ), - }, - ), - ( - "hostname(25)", - { - Some( - 0, - ), - }, - ), - ( - "ip_address(17)", - { - Some( - 0, - ), - }, - ), - ( - "member(16)", - { - Some( - 0, - ), - }, - ), - ( - "namespace(9)", - { - Some( - 0, - ), - }, - ), - ( - "node(24)", - { - Some( - 0, - ), - }, - ), - ( - "nonce(26)", - { - Some( - 0, - ), - }, - ), - ( - "operation(3)", - { - Some( - 0, - ), - }, - ), - ( - "owner(7)", - { - Some( - 0, - ), - }, - ), - ( - "path(21)", - { - Some( - 0, - ), - }, - ), - ( - "query(27)", - { - Some( - 0, - ), - }, - ), - ( - "read(0)", - { - Some( - 0, - ), - }, - ), - ( - "resource(2)", - { - Some( - 0, - ), - }, - ), - ( - "right(4)", - { - Some( - 0, - ), - }, - ), - ( - "role(6)", - { - Some( - 0, - ), - }, - ), - ( - "service(12)", - { - Some( - 0, - ), - }, - ), - ( - "team(11)", - { - Some( - 0, - ), - }, - ), - ( - "tenant(8)", - { - Some( - 0, - ), - }, - ), - ( - "time(5)", - { - Some( - 0, - ), - }, - ), - ( - "user(10)", - { - Some( - 0, - ), - }, - ), - ( - "version(22)", - { - Some( - 0, - ), - }, - ), - ( - "write(1)", - { - Some( - 0, - ), - }, - ), -} - rules: {} - checks: { - "check if read(0), write(1), resource(2), operation(3), right(4), time(5), role(6), owner(7), tenant(8), namespace(9), user(10), team(11), service(12), admin(13), email(14), group(15), member(16), ip_address(17), client(18), client_ip(19), domain(20), path(21), version(22), cluster(23), node(24), hostname(25), nonce(26), query(27)", -} - policies: { + facts: [ + "admin(13)", + "client(18)", + "client_ip(19)", + "cluster(23)", + "domain(20)", + "email(14)", + "group(15)", + "hostname(25)", + "ip_address(17)", + "member(16)", + "namespace(9)", + "node(24)", + "nonce(26)", + "operation(3)", + "owner(7)", + "path(21)", + "query(27)", + "read(0)", + "resource(2)", + "right(4)", + "role(6)", + "service(12)", + "team(11)", + "tenant(8)", + "time(5)", + "user(10)", + "version(22)", + "write(1)", + ], + }, +] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 18446744073709551615, + ), + checks: [ + "check if read(0), write(1), resource(2), operation(3), right(4), time(5), role(6), owner(7), tenant(8), namespace(9), user(10), team(11), service(12), admin(13), email(14), group(15), member(16), ip_address(17), client(18), client_ip(19), domain(20), path(21), version(22), cluster(23), node(24), hostname(25), nonce(26), query(27)", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -1857,32 +1758,43 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "authority_fact(1)", - { + facts: [ + AuthorizerFactSet { + origin: { Some( 0, ), }, - ), - ( - "block1_fact(1)", - { + facts: [ + "authority_fact(1)", + ], + }, + AuthorizerFactSet { + origin: { Some( 1, ), }, - ), -} - rules: {} - checks: { - "check if authority_fact($var)", - "check if block1_fact($var)", -} - policies: { + facts: [ + "block1_fact(1)", + ], + }, +] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 2, + ), + checks: [ + "check if authority_fact($var)", + "check if block1_fact($var)", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -1930,32 +1842,50 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "group(\"admin\")", - { + facts: [ + AuthorizerFactSet { + origin: { Some( - 1, + 0, ), }, - ), - ( - "right(\"read\")", - { + facts: [ + "right(\"read\")", + ], + }, + AuthorizerFactSet { + origin: { Some( - 0, + 1, ), }, - ), -} - rules: {} - checks: { - "check if group(\"admin\") trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", - "check if right(\"read\")", -} - policies: { + facts: [ + "group(\"admin\")", + ], + }, +] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 0, + ), + checks: [ + "check if group(\"admin\") trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", + ], + }, + AuthorizerCheckSet { + origin: Some( + 1, + ), + checks: [ + "check if right(\"read\")", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -1993,35 +1923,41 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "allowed_operations([\"A\", \"B\"])", - { + facts: [ + AuthorizerFactSet { + origin: { + None, + }, + facts: [ + "operation(\"A\")", + "operation(\"B\")", + ], + }, + AuthorizerFactSet { + origin: { Some( 0, ), }, - ), - ( - "operation(\"A\")", - { - None, - }, - ), - ( - "operation(\"B\")", - { - None, - }, - ), -} - rules: {} - checks: { - "check all operation($op), allowed_operations($allowed), $allowed.contains($op)", -} - policies: { + facts: [ + "allowed_operations([\"A\", \"B\"])", + ], + }, +] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 0, + ), + checks: [ + "check all operation($op), allowed_operations($allowed), $allowed.contains($op)", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -2042,35 +1978,41 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "allowed_operations([\"A\", \"B\"])", - { + facts: [ + AuthorizerFactSet { + origin: { + None, + }, + facts: [ + "operation(\"A\")", + "operation(\"invalid\")", + ], + }, + AuthorizerFactSet { + origin: { Some( 0, ), }, - ), - ( - "operation(\"A\")", - { - None, - }, - ), - ( - "operation(\"invalid\")", - { - None, - }, - ), -} - rules: {} - checks: { - "check all operation($op), allowed_operations($allowed), $allowed.contains($op)", -} - policies: { + facts: [ + "allowed_operations([\"A\", \"B\"])", + ], + }, +] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 0, + ), + checks: [ + "check all operation($op), allowed_operations($allowed), $allowed.contains($op)", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -2165,26 +2107,29 @@ revocation ids: authorizer world: ``` World { - facts: { - ( - "query(0)", - { + facts: [ + AuthorizerFactSet { + origin: { Some( 0, ), }, - ), - ( - "query(1)", - { + facts: [ + "query(0)", + ], + }, + AuthorizerFactSet { + origin: { Some( 1, ), }, - ), - ( - "query(1, 2)", - { + facts: [ + "query(1)", + ], + }, + AuthorizerFactSet { + origin: { Some( 1, ), @@ -2192,54 +2137,111 @@ World { 2, ), }, - ), - ( - "query(2)", - { + facts: [ + "query(1, 2)", + ], + }, + AuthorizerFactSet { + origin: { Some( 2, ), }, - ), - ( - "query(3)", - { + facts: [ + "query(2)", + ], + }, + AuthorizerFactSet { + origin: { Some( 3, ), }, - ), - ( - "query(4)", - { + facts: [ + "query(3)", + ], + }, + AuthorizerFactSet { + origin: { Some( 4, ), }, - ), -} - rules: { - ( - "query(1, 2) <- query(1), query(2) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", - Some( + facts: [ + "query(4)", + ], + }, +] + rules: [ + AuthorizerRuleSet { + origin: Some( 1, ), - ), -} - checks: { - "check if query(1) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", - "check if query(1, 2) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189, ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", - "check if query(2) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", - "check if query(2), query(3) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", - "check if query(4) trusting ed25519/f98da8c1cf907856431bfc3dc87531e0eaadba90f919edc232405b85877ef136", - "check if true trusting previous, ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", -} - policies: { - "allow if true", - "deny if query(0) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", - "deny if query(1, 2)", + rules: [ + "query(1, 2) <- query(1), query(2) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", + ], + }, +] + checks: [ + AuthorizerCheckSet { + origin: Some( + 0, + ), + checks: [ + "check if true trusting previous, ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", + ], + }, + AuthorizerCheckSet { + origin: Some( + 1, + ), + checks: [ + "check if query(1) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", + "check if query(2), query(3) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", + ], + }, + AuthorizerCheckSet { + origin: Some( + 2, + ), + checks: [ + "check if query(1) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", + "check if query(2), query(3) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", + ], + }, + AuthorizerCheckSet { + origin: Some( + 3, + ), + checks: [ + "check if query(1) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", + "check if query(2), query(3) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", + ], + }, + AuthorizerCheckSet { + origin: Some( + 4, + ), + checks: [ + "check if query(2) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", + "check if query(4) trusting ed25519/f98da8c1cf907856431bfc3dc87531e0eaadba90f919edc232405b85877ef136", + ], + }, + AuthorizerCheckSet { + origin: Some( + 18446744073709551615, + ), + checks: [ + "check if query(1, 2) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189, ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", + ], + }, +] + policies: [ "deny if query(3)", -} + "deny if query(1, 2)", + "deny if query(0) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", + "allow if true", +] } ``` @@ -2275,16 +2277,23 @@ revocation ids: authorizer world: ``` World { - facts: {} - rules: {} - checks: { - "check if true || -9223372036854775808 - 1 != 0", - "check if true || 10000000000 * 10000000000 != 0", - "check if true || 9223372036854775807 + 1 != 0", -} - policies: { + facts: [] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 0, + ), + checks: [ + "check if true || -9223372036854775808 - 1 != 0", + "check if true || 10000000000 * 10000000000 != 0", + "check if true || 9223372036854775807 + 1 != 0", + ], + }, +] + policies: [ "allow if true", -} +] } ``` @@ -2323,19 +2332,26 @@ revocation ids: authorizer world: ``` World { - facts: {} - rules: {} - checks: { - "check if \"abcD12x\" != \"abcD12\"", - "check if 1 != 3", - "check if 1 | 2 ^ 3 == 0", - "check if 2022-12-04T09:46:41Z != 2020-12-04T09:46:41Z", - "check if [1, 4] != [1, 2]", - "check if hex:12abcd != hex:12ab", -} - policies: { + facts: [] + rules: [] + checks: [ + AuthorizerCheckSet { + origin: Some( + 0, + ), + checks: [ + "check if \"abcD12x\" != \"abcD12\"", + "check if 1 != 3", + "check if 1 | 2 ^ 3 == 0", + "check if 2022-12-04T09:46:41Z != 2020-12-04T09:46:41Z", + "check if [1, 4] != [1, 2]", + "check if hex:12abcd != hex:12ab", + ], + }, +] + policies: [ "allow if true", -} +] } ``` diff --git a/biscuit-auth/samples/samples.json b/biscuit-auth/samples/samples.json index 6e9a4bb8..432c45d2 100644 --- a/biscuit-auth/samples/samples.json +++ b/biscuit-auth/samples/samples.json @@ -28,34 +28,33 @@ "": { "world": { "facts": [ - [ - "resource(\"file1\")", - [ + { + "origin": [ null + ], + "facts": [ + "resource(\"file1\")" ] - ], - [ - "right(\"file1\", \"read\")", - [ + }, + { + "origin": [ 0 + ], + "facts": [ + "right(\"file1\", \"read\")", + "right(\"file1\", \"write\")", + "right(\"file2\", \"read\")" ] - ], - [ - "right(\"file1\", \"write\")", - [ - 0 - ] - ], - [ - "right(\"file2\", \"read\")", - [ - 0 - ] - ] + } ], "rules": [], "checks": [ - "check if resource($0), operation(\"read\"), right($0, \"read\")" + { + "origin": 1, + "checks": [ + "check if resource($0), operation(\"read\"), right($0, \"read\")" + ] + } ], "policies": [ "allow if true" @@ -323,45 +322,48 @@ "": { "world": { "facts": [ - [ - "operation(\"read\")", - [ + { + "origin": [ null - ] - ], - [ - "owner(\"alice\", \"file1\")", - [ + ], + "facts": [ + "operation(\"read\")", + "resource(\"file2\")" + ] + }, + { + "origin": [ 0 - ] - ], - [ - "owner(\"alice\", \"file2\")", - [ + ], + "facts": [ + "owner(\"alice\", \"file1\")", + "user_id(\"alice\")" + ] + }, + { + "origin": [ 2 + ], + "facts": [ + "owner(\"alice\", \"file2\")" ] - ], - [ - "resource(\"file2\")", - [ - null - ] - ], - [ - "user_id(\"alice\")", - [ - 0 - ] - ] + } ], "rules": [ - [ - "right($0, \"read\") <- resource($0), user_id($1), owner($1, $0)", - 1 - ] + { + "origin": 1, + "rules": [ + "right($0, \"read\") <- resource($0), user_id($1), owner($1, $0)" + ] + } ], "checks": [ - "check if resource($0), operation(\"read\"), right($0, \"read\")" + { + "origin": 1, + "checks": [ + "check if resource($0), operation(\"read\"), right($0, \"read\")" + ] + } ], "policies": [ "allow if true" @@ -429,34 +431,40 @@ "": { "world": { "facts": [ - [ - "operation(\"read\")", - [ + { + "origin": [ null - ] - ], - [ - "resource(\"file2\")", - [ - null - ] - ], - [ - "right(\"file1\", \"read\")", - [ + ], + "facts": [ + "operation(\"read\")", + "resource(\"file2\")" + ] + }, + { + "origin": [ 0 + ], + "facts": [ + "right(\"file1\", \"read\")" ] - ], - [ - "right(\"file2\", \"read\")", - [ + }, + { + "origin": [ 2 + ], + "facts": [ + "right(\"file2\", \"read\")" ] - ] + } ], "rules": [], "checks": [ - "check if resource($0), operation(\"read\"), right($0, \"read\")" + { + "origin": 1, + "checks": [ + "check if resource($0), operation(\"read\"), right($0, \"read\")" + ] + } ], "policies": [ "allow if true" @@ -514,29 +522,26 @@ "": { "world": { "facts": [ - [ - "operation(\"read\")", - [ + { + "origin": [ null + ], + "facts": [ + "operation(\"read\")", + "resource(\"file1\")", + "time(2020-12-21T09:23:12Z)" ] - ], - [ - "resource(\"file1\")", - [ - null - ] - ], - [ - "time(2020-12-21T09:23:12Z)", - [ - null - ] - ] + } ], "rules": [], "checks": [ - "check if resource(\"file1\")", - "check if time($time), $time <= 2018-12-20T00:00:00Z" + { + "origin": 1, + "checks": [ + "check if resource(\"file1\")", + "check if time($time), $time <= 2018-12-20T00:00:00Z" + ] + } ], "policies": [ "allow if true" @@ -595,34 +600,40 @@ "": { "world": { "facts": [ - [ - "operation(\"read\")", - [ + { + "origin": [ null - ] - ], - [ - "resource(\"file2\")", - [ - null - ] - ], - [ - "right(\"file1\", \"read\")", - [ + ], + "facts": [ + "operation(\"read\")", + "resource(\"file2\")" + ] + }, + { + "origin": [ 0 + ], + "facts": [ + "right(\"file1\", \"read\")" ] - ], - [ - "right(\"file2\", \"read\")", - [ + }, + { + "origin": [ 1 + ], + "facts": [ + "right(\"file2\", \"read\")" ] - ] + } ], "rules": [], "checks": [ - "check if right($0, $1), resource($0), operation($1)" + { + "origin": 18446744073709551615, + "checks": [ + "check if right($0, $1), resource($0), operation($1)" + ] + } ], "policies": [ "allow if true" @@ -672,28 +683,32 @@ "": { "world": { "facts": [ - [ - "operation(\"read\")", - [ + { + "origin": [ null - ] - ], - [ - "resource(\"file2\")", - [ - null - ] - ], - [ - "right(\"file1\", \"read\")", - [ + ], + "facts": [ + "operation(\"read\")", + "resource(\"file2\")" + ] + }, + { + "origin": [ 0 + ], + "facts": [ + "right(\"file1\", \"read\")" ] - ] + } ], "rules": [], "checks": [ - "check if right($0, $1), resource($0), operation($1)" + { + "origin": 18446744073709551615, + "checks": [ + "check if right($0, $1), resource($0), operation($1)" + ] + } ], "policies": [ "allow if true" @@ -742,22 +757,24 @@ "file1": { "world": { "facts": [ - [ - "operation(\"read\")", - [ + { + "origin": [ null + ], + "facts": [ + "operation(\"read\")", + "resource(\"file1\")" ] - ], - [ - "resource(\"file1\")", - [ - null - ] - ] + } ], "rules": [], "checks": [ - "check if resource(\"file1\")" + { + "origin": 0, + "checks": [ + "check if resource(\"file1\")" + ] + } ], "policies": [ "allow if true" @@ -774,22 +791,24 @@ "file2": { "world": { "facts": [ - [ - "operation(\"read\")", - [ - null - ] - ], - [ - "resource(\"file2\")", - [ + { + "origin": [ null + ], + "facts": [ + "operation(\"read\")", + "resource(\"file2\")" ] - ] + } ], "rules": [], "checks": [ - "check if resource(\"file1\")" + { + "origin": 0, + "checks": [ + "check if resource(\"file1\")" + ] + } ], "policies": [ "allow if true" @@ -850,50 +869,50 @@ "file1": { "world": { "facts": [ - [ - "resource(\"file1\")", - [ + { + "origin": [ null - ] - ], - [ - "right(\"file1\", \"read\")", - [ - 0 - ] - ], - [ - "right(\"file2\", \"read\")", - [ - 0 - ] - ], - [ - "time(2020-12-21T09:23:12Z)", - [ - null - ] - ], - [ - "valid_date(\"file1\")", - [ + ], + "facts": [ + "resource(\"file1\")", + "time(2020-12-21T09:23:12Z)" + ] + }, + { + "origin": [ null, 1 + ], + "facts": [ + "valid_date(\"file1\")" ] - ] + }, + { + "origin": [ + 0 + ], + "facts": [ + "right(\"file1\", \"read\")", + "right(\"file2\", \"read\")" + ] + } ], "rules": [ - [ - "valid_date(\"file1\") <- time($0), resource(\"file1\"), $0 <= 2030-12-31T12:59:59Z", - 1 - ], - [ - "valid_date($1) <- time($0), resource($1), $0 <= 1999-12-31T12:59:59Z, ![\"file1\"].contains($1)", - 1 - ] + { + "origin": 1, + "rules": [ + "valid_date(\"file1\") <- time($0), resource(\"file1\"), $0 <= 2030-12-31T12:59:59Z", + "valid_date($1) <- time($0), resource($1), $0 <= 1999-12-31T12:59:59Z, ![\"file1\"].contains($1)" + ] + } ], "checks": [ - "check if valid_date($0), resource($0)" + { + "origin": 1, + "checks": [ + "check if valid_date($0), resource($0)" + ] + } ], "policies": [ "allow if true" @@ -911,43 +930,41 @@ "file2": { "world": { "facts": [ - [ - "resource(\"file2\")", - [ + { + "origin": [ null - ] - ], - [ - "right(\"file1\", \"read\")", - [ - 0 - ] - ], - [ - "right(\"file2\", \"read\")", - [ + ], + "facts": [ + "resource(\"file2\")", + "time(2020-12-21T09:23:12Z)" + ] + }, + { + "origin": [ 0 + ], + "facts": [ + "right(\"file1\", \"read\")", + "right(\"file2\", \"read\")" ] - ], - [ - "time(2020-12-21T09:23:12Z)", - [ - null - ] - ] + } ], "rules": [ - [ - "valid_date(\"file1\") <- time($0), resource(\"file1\"), $0 <= 2030-12-31T12:59:59Z", - 1 - ], - [ - "valid_date($1) <- time($0), resource($1), $0 <= 1999-12-31T12:59:59Z, ![\"file1\"].contains($1)", - 1 - ] + { + "origin": 1, + "rules": [ + "valid_date(\"file1\") <- time($0), resource(\"file1\"), $0 <= 2030-12-31T12:59:59Z", + "valid_date($1) <- time($0), resource($1), $0 <= 1999-12-31T12:59:59Z, ![\"file1\"].contains($1)" + ] + } ], "checks": [ - "check if valid_date($0), resource($0)" + { + "origin": 1, + "checks": [ + "check if valid_date($0), resource($0)" + ] + } ], "policies": [ "allow if true" @@ -999,16 +1016,23 @@ "file1": { "world": { "facts": [ - [ - "resource(\"file1\")", - [ + { + "origin": [ null + ], + "facts": [ + "resource(\"file1\")" ] - ] + } ], "rules": [], "checks": [ - "check if resource($0), $0.matches(\"file[0-9]+.txt\")" + { + "origin": 0, + "checks": [ + "check if resource($0), $0.matches(\"file[0-9]+.txt\")" + ] + } ], "policies": [ "allow if true" @@ -1042,16 +1066,23 @@ "file123": { "world": { "facts": [ - [ - "resource(\"file123.txt\")", - [ + { + "origin": [ null + ], + "facts": [ + "resource(\"file123.txt\")" ] - ] + } ], "rules": [], "checks": [ - "check if resource($0), $0.matches(\"file[0-9]+.txt\")" + { + "origin": 0, + "checks": [ + "check if resource($0), $0.matches(\"file[0-9]+.txt\")" + ] + } ], "policies": [ "allow if true" @@ -1085,16 +1116,23 @@ "": { "world": { "facts": [ - [ - "must_be_present(\"hello\")", - [ + { + "origin": [ 0 + ], + "facts": [ + "must_be_present(\"hello\")" ] - ] + } ], "rules": [], "checks": [ - "check if must_be_present($0) or must_be_present($0)" + { + "origin": 18446744073709551615, + "checks": [ + "check if must_be_present($0) or must_be_present($0)" + ] + } ], "policies": [ "allow if true" @@ -1135,16 +1173,23 @@ "": { "world": { "facts": [ - [ - "query(\"test\")", - [ + { + "origin": [ 1 + ], + "facts": [ + "query(\"test\")" ] - ] + } ], "rules": [], "checks": [ - "check if resource(\"hello\")" + { + "origin": 0, + "checks": [ + "check if resource(\"hello\")" + ] + } ], "policies": [ "allow if true" @@ -1208,44 +1253,50 @@ "facts": [], "rules": [], "checks": [ - "check if !false", - "check if !false && true", - "check if \"aaabde\" == \"aaa\" + \"b\" + \"de\"", - "check if \"aaabde\".contains(\"abd\")", - "check if \"aaabde\".matches(\"a*c?.e\")", - "check if \"abcD12\" == \"abcD12\"", - "check if \"hello world\".starts_with(\"hello\") && \"hello world\".ends_with(\"world\")", - "check if (true || false) && true", - "check if 1 + 2 * 3 - 4 / 2 == 5", - "check if 1 < 2", - "check if 1 <= 1", - "check if 1 <= 2", - "check if 2 > 1", - "check if 2 >= 1", - "check if 2 >= 2", - "check if 2019-12-04T09:46:41Z < 2020-12-04T09:46:41Z", - "check if 2019-12-04T09:46:41Z <= 2020-12-04T09:46:41Z", - "check if 2020-12-04T09:46:41Z == 2020-12-04T09:46:41Z", - "check if 2020-12-04T09:46:41Z > 2019-12-04T09:46:41Z", - "check if 2020-12-04T09:46:41Z >= 2019-12-04T09:46:41Z", - "check if 2020-12-04T09:46:41Z >= 2020-12-04T09:46:41Z", - "check if 3 == 3", - "check if [\"abc\", \"def\"].contains(\"abc\")", - "check if [1, 2, 3].intersection([1, 2]).contains(1)", - "check if [1, 2, 3].intersection([1, 2]).length() == 2", - "check if [1, 2] == [1, 2]", - "check if [1, 2].contains(2)", - "check if [1, 2].contains([2])", - "check if [1, 2].intersection([2, 3]) == [2]", - "check if [1, 2].union([2, 3]) == [1, 2, 3]", - "check if [2019-12-04T09:46:41Z, 2020-12-04T09:46:41Z].contains(2020-12-04T09:46:41Z)", - "check if [false, true].contains(true)", - "check if [hex:12ab, hex:34de].contains(hex:34de)", - "check if false == false", - "check if false || true", - "check if hex:12ab == hex:12ab", - "check if true", - "check if true == true" + { + "origin": 0, + "checks": [ + "check if !false", + "check if !false && true", + "check if \"aaabde\" == \"aaa\" + \"b\" + \"de\"", + "check if \"aaabde\".contains(\"abd\")", + "check if \"aaabde\".matches(\"a*c?.e\")", + "check if \"abcD12\" == \"abcD12\"", + "check if \"hello world\".starts_with(\"hello\") && \"hello world\".ends_with(\"world\")", + "check if (true || false) && true", + "check if 1 + 2 * 3 - 4 / 2 == 5", + "check if 1 < 2", + "check if 1 <= 1", + "check if 1 <= 2", + "check if 2 > 1", + "check if 2 >= 1", + "check if 2 >= 2", + "check if 2019-12-04T09:46:41Z < 2020-12-04T09:46:41Z", + "check if 2019-12-04T09:46:41Z <= 2020-12-04T09:46:41Z", + "check if 2020-12-04T09:46:41Z == 2020-12-04T09:46:41Z", + "check if 2020-12-04T09:46:41Z > 2019-12-04T09:46:41Z", + "check if 2020-12-04T09:46:41Z >= 2019-12-04T09:46:41Z", + "check if 2020-12-04T09:46:41Z >= 2020-12-04T09:46:41Z", + "check if 2020-12-04T09:46:41Z >= 2020-12-04T09:46:41Z", + "check if 3 == 3", + "check if [\"abc\", \"def\"].contains(\"abc\")", + "check if [1, 2, 3].intersection([1, 2]).contains(1)", + "check if [1, 2, 3].intersection([1, 2]).length() == 2", + "check if [1, 2] == [1, 2]", + "check if [1, 2].contains(2)", + "check if [1, 2].contains([2])", + "check if [1, 2].intersection([2, 3]) == [2]", + "check if [1, 2].union([2, 3]) == [1, 2, 3]", + "check if [2019-12-04T09:46:41Z, 2020-12-04T09:46:41Z].contains(2020-12-04T09:46:41Z)", + "check if [false, true].contains(true)", + "check if [hex:12ab, hex:34de].contains(hex:34de)", + "check if false == false", + "check if false || true", + "check if hex:12ab == hex:12ab", + "check if true", + "check if true == true" + ] + } ], "policies": [ "allow if true" @@ -1326,28 +1377,39 @@ "": { "world": { "facts": [ - [ - "operation(\"read\")", - [ + { + "origin": [ + null + ], + "facts": [ + "operation(\"write\")" + ] + }, + { + "origin": [ null, 1 + ], + "facts": [ + "operation(\"read\")" ] - ], - [ - "operation(\"write\")", - [ - null - ] - ] + } ], "rules": [ - [ - "operation(\"read\") <- operation($any)", - 1 - ] + { + "origin": 1, + "rules": [ + "operation(\"read\") <- operation($any)" + ] + } ], "checks": [ - "check if operation(\"read\")" + { + "origin": 0, + "checks": [ + "check if operation(\"read\")" + ] + } ], "policies": [ "allow if true" @@ -1407,40 +1469,34 @@ "": { "world": { "facts": [ - [ - "operation(\"read\")", - [ - null - ] - ], - [ - "resource(\"file1\")", - [ + { + "origin": [ null - ] - ], - [ - "right(\"file1\", \"read\")", - [ - 0 - ] - ], - [ - "right(\"file1\", \"write\")", - [ + ], + "facts": [ + "operation(\"read\")", + "resource(\"file1\")" + ] + }, + { + "origin": [ 0 + ], + "facts": [ + "right(\"file1\", \"read\")", + "right(\"file1\", \"write\")", + "right(\"file2\", \"read\")" ] - ], - [ - "right(\"file2\", \"read\")", - [ - 0 - ] - ] + } ], "rules": [], "checks": [ - "check if resource($0), operation(\"read\"), right($0, \"read\")" + { + "origin": 1, + "checks": [ + "check if resource($0), operation(\"read\"), right($0, \"read\")" + ] + } ], "policies": [ "allow if true" @@ -1475,16 +1531,23 @@ "": { "world": { "facts": [ - [ - "ns::fact_123(\"hello é\t😁\")", - [ + { + "origin": [ 0 + ], + "facts": [ + "ns::fact_123(\"hello é\t😁\")" ] - ] + } ], "rules": [], "checks": [ - "check if ns::fact_123(\"hello é\t😁\")" + { + "origin": 18446744073709551615, + "checks": [ + "check if ns::fact_123(\"hello é\t😁\")" + ] + } ], "policies": [ "allow if true" @@ -1515,178 +1578,50 @@ "": { "world": { "facts": [ - [ - "admin(13)", - [ - 0 - ] - ], - [ - "client(18)", - [ - 0 - ] - ], - [ - "client_ip(19)", - [ - 0 - ] - ], - [ - "cluster(23)", - [ - 0 - ] - ], - [ - "domain(20)", - [ - 0 - ] - ], - [ - "email(14)", - [ - 0 - ] - ], - [ - "group(15)", - [ - 0 - ] - ], - [ - "hostname(25)", - [ - 0 - ] - ], - [ - "ip_address(17)", - [ - 0 - ] - ], - [ - "member(16)", - [ - 0 - ] - ], - [ - "namespace(9)", - [ - 0 - ] - ], - [ - "node(24)", - [ - 0 - ] - ], - [ - "nonce(26)", - [ - 0 - ] - ], - [ - "operation(3)", - [ - 0 - ] - ], - [ - "owner(7)", - [ - 0 - ] - ], - [ - "path(21)", - [ - 0 - ] - ], - [ - "query(27)", - [ - 0 - ] - ], - [ - "read(0)", - [ - 0 - ] - ], - [ - "resource(2)", - [ - 0 - ] - ], - [ - "right(4)", - [ - 0 - ] - ], - [ - "role(6)", - [ - 0 - ] - ], - [ - "service(12)", - [ - 0 - ] - ], - [ - "team(11)", - [ + { + "origin": [ 0 + ], + "facts": [ + "admin(13)", + "client(18)", + "client_ip(19)", + "cluster(23)", + "domain(20)", + "email(14)", + "group(15)", + "hostname(25)", + "ip_address(17)", + "member(16)", + "namespace(9)", + "node(24)", + "nonce(26)", + "operation(3)", + "owner(7)", + "path(21)", + "query(27)", + "read(0)", + "resource(2)", + "right(4)", + "role(6)", + "service(12)", + "team(11)", + "tenant(8)", + "time(5)", + "user(10)", + "version(22)", + "write(1)" ] - ], - [ - "tenant(8)", - [ - 0 - ] - ], - [ - "time(5)", - [ - 0 - ] - ], - [ - "user(10)", - [ - 0 - ] - ], - [ - "version(22)", - [ - 0 - ] - ], - [ - "write(1)", - [ - 0 - ] - ] + } ], "rules": [], "checks": [ - "check if read(0), write(1), resource(2), operation(3), right(4), time(5), role(6), owner(7), tenant(8), namespace(9), user(10), team(11), service(12), admin(13), email(14), group(15), member(16), ip_address(17), client(18), client_ip(19), domain(20), path(21), version(22), cluster(23), node(24), hostname(25), nonce(26), query(27)" + { + "origin": 18446744073709551615, + "checks": [ + "check if read(0), write(1), resource(2), operation(3), right(4), time(5), role(6), owner(7), tenant(8), namespace(9), user(10), team(11), service(12), admin(13), email(14), group(15), member(16), ip_address(17), client(18), client_ip(19), domain(20), path(21), version(22), cluster(23), node(24), hostname(25), nonce(26), query(27)" + ] + } ], "policies": [ "allow if true" @@ -1735,23 +1670,32 @@ "": { "world": { "facts": [ - [ - "authority_fact(1)", - [ + { + "origin": [ 0 + ], + "facts": [ + "authority_fact(1)" ] - ], - [ - "block1_fact(1)", - [ + }, + { + "origin": [ 1 + ], + "facts": [ + "block1_fact(1)" ] - ] + } ], "rules": [], "checks": [ - "check if authority_fact($var)", - "check if block1_fact($var)" + { + "origin": 2, + "checks": [ + "check if authority_fact($var)", + "check if block1_fact($var)" + ] + } ], "policies": [ "allow if true" @@ -1809,23 +1753,37 @@ "": { "world": { "facts": [ - [ - "group(\"admin\")", - [ - 1 - ] - ], - [ - "right(\"read\")", - [ + { + "origin": [ 0 + ], + "facts": [ + "right(\"read\")" + ] + }, + { + "origin": [ + 1 + ], + "facts": [ + "group(\"admin\")" ] - ] + } ], "rules": [], "checks": [ - "check if group(\"admin\") trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", - "check if right(\"read\")" + { + "origin": 0, + "checks": [ + "check if group(\"admin\") trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189" + ] + }, + { + "origin": 1, + "checks": [ + "check if right(\"read\")" + ] + } ], "policies": [ "allow if true" @@ -1863,28 +1821,32 @@ "A, B": { "world": { "facts": [ - [ - "allowed_operations([\"A\", \"B\"])", - [ - 0 - ] - ], - [ - "operation(\"A\")", - [ - null - ] - ], - [ - "operation(\"B\")", - [ + { + "origin": [ null + ], + "facts": [ + "operation(\"A\")", + "operation(\"B\")" + ] + }, + { + "origin": [ + 0 + ], + "facts": [ + "allowed_operations([\"A\", \"B\"])" ] - ] + } ], "rules": [], "checks": [ - "check all operation($op), allowed_operations($allowed), $allowed.contains($op)" + { + "origin": 0, + "checks": [ + "check all operation($op), allowed_operations($allowed), $allowed.contains($op)" + ] + } ], "policies": [ "allow if true" @@ -1901,28 +1863,32 @@ "A, invalid": { "world": { "facts": [ - [ - "allowed_operations([\"A\", \"B\"])", - [ - 0 - ] - ], - [ - "operation(\"A\")", - [ - null - ] - ], - [ - "operation(\"invalid\")", - [ + { + "origin": [ null + ], + "facts": [ + "operation(\"A\")", + "operation(\"invalid\")" + ] + }, + { + "origin": [ + 0 + ], + "facts": [ + "allowed_operations([\"A\", \"B\"])" ] - ] + } ], "rules": [], "checks": [ - "check all operation($op), allowed_operations($allowed), $allowed.contains($op)" + { + "origin": 0, + "checks": [ + "check all operation($op), allowed_operations($allowed), $allowed.contains($op)" + ] + } ], "policies": [ "allow if true" @@ -2000,63 +1966,111 @@ "": { "world": { "facts": [ - [ - "query(0)", - [ + { + "origin": [ 0 + ], + "facts": [ + "query(0)" ] - ], - [ - "query(1)", - [ + }, + { + "origin": [ 1 + ], + "facts": [ + "query(1)" ] - ], - [ - "query(1, 2)", - [ + }, + { + "origin": [ 1, 2 + ], + "facts": [ + "query(1, 2)" ] - ], - [ - "query(2)", - [ + }, + { + "origin": [ 2 + ], + "facts": [ + "query(2)" ] - ], - [ - "query(3)", - [ + }, + { + "origin": [ 3 + ], + "facts": [ + "query(3)" ] - ], - [ - "query(4)", - [ + }, + { + "origin": [ 4 + ], + "facts": [ + "query(4)" ] - ] + } ], "rules": [ - [ - "query(1, 2) <- query(1), query(2) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", - 1 - ] + { + "origin": 1, + "rules": [ + "query(1, 2) <- query(1), query(2) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463" + ] + } ], "checks": [ - "check if query(1) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", - "check if query(1, 2) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189, ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", - "check if query(2) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", - "check if query(2), query(3) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", - "check if query(4) trusting ed25519/f98da8c1cf907856431bfc3dc87531e0eaadba90f919edc232405b85877ef136", - "check if true trusting previous, ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189" + { + "origin": 0, + "checks": [ + "check if true trusting previous, ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189" + ] + }, + { + "origin": 1, + "checks": [ + "check if query(1) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", + "check if query(2), query(3) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463" + ] + }, + { + "origin": 2, + "checks": [ + "check if query(1) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", + "check if query(2), query(3) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463" + ] + }, + { + "origin": 3, + "checks": [ + "check if query(1) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", + "check if query(2), query(3) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463" + ] + }, + { + "origin": 4, + "checks": [ + "check if query(2) trusting ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463", + "check if query(4) trusting ed25519/f98da8c1cf907856431bfc3dc87531e0eaadba90f919edc232405b85877ef136" + ] + }, + { + "origin": 18446744073709551615, + "checks": [ + "check if query(1, 2) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189, ed25519/a060270db7e9c9f06e8f9cc33a64e99f6596af12cb01c4b638df8afc7b642463" + ] + } ], "policies": [ - "allow if true", - "deny if query(0) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", + "deny if query(3)", "deny if query(1, 2)", - "deny if query(3)" + "deny if query(0) trusting ed25519/acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189", + "allow if true" ] }, "result": { @@ -2090,9 +2104,14 @@ "facts": [], "rules": [], "checks": [ - "check if true || -9223372036854775808 - 1 != 0", - "check if true || 10000000000 * 10000000000 != 0", - "check if true || 9223372036854775807 + 1 != 0" + { + "origin": 0, + "checks": [ + "check if true || -9223372036854775808 - 1 != 0", + "check if true || 10000000000 * 10000000000 != 0", + "check if true || 9223372036854775807 + 1 != 0" + ] + } ], "policies": [ "allow if true" @@ -2130,12 +2149,17 @@ "facts": [], "rules": [], "checks": [ - "check if \"abcD12x\" != \"abcD12\"", - "check if 1 != 3", - "check if 1 | 2 ^ 3 == 0", - "check if 2022-12-04T09:46:41Z != 2020-12-04T09:46:41Z", - "check if [1, 4] != [1, 2]", - "check if hex:12abcd != hex:12ab" + { + "origin": 0, + "checks": [ + "check if \"abcD12x\" != \"abcD12\"", + "check if 1 != 3", + "check if 1 | 2 ^ 3 == 0", + "check if 2022-12-04T09:46:41Z != 2020-12-04T09:46:41Z", + "check if [1, 4] != [1, 2]", + "check if hex:12abcd != hex:12ab" + ] + } ], "policies": [ "allow if true" diff --git a/biscuit-auth/src/crypto/ed25519.rs b/biscuit-auth/src/crypto/ed25519.rs index d6a56504..0686c3de 100644 --- a/biscuit-auth/src/crypto/ed25519.rs +++ b/biscuit-auth/src/crypto/ed25519.rs @@ -20,7 +20,7 @@ use zeroize::Zeroize; /// pair of cryptographic keys used to sign a token's block #[derive(Debug)] pub struct KeyPair { - kp: ed25519_dalek::SigningKey, + pub(super) kp: ed25519_dalek::SigningKey, } impl KeyPair { diff --git a/biscuit-auth/src/crypto/mod.rs b/biscuit-auth/src/crypto/mod.rs index 52aa5cdc..05473dbd 100644 --- a/biscuit-auth/src/crypto/mod.rs +++ b/biscuit-auth/src/crypto/mod.rs @@ -12,6 +12,9 @@ use crate::format::schema; use super::error; mod ed25519; mod p256; + +#[cfg(feature = "pem")] +use ed25519_dalek::pkcs8::DecodePrivateKey; use nom::Finish; use rand_core::{CryptoRng, RngCore}; use std::{fmt::Display, hash::Hash, str::FromStr}; @@ -65,6 +68,20 @@ impl KeyPair { } } + #[cfg(feature = "pem")] + pub fn from_private_key_der(bytes: &[u8]) -> Result { + let kp = ed25519_dalek::SigningKey::from_pkcs8_der(bytes) + .map_err(|e| error::Format::InvalidKey(e.to_string()))?; + Ok(KeyPair::Ed25519(ed25519::KeyPair { kp })) + } + + #[cfg(feature = "pem")] + pub fn from_private_key_pem(str: &str) -> Result { + let kp = ed25519_dalek::SigningKey::from_pkcs8_pem(str) + .map_err(|e| error::Format::InvalidKey(e.to_string()))?; + Ok(KeyPair::Ed25519(ed25519::KeyPair { kp })) + } + pub fn private(&self) -> PrivateKey { match self { KeyPair::Ed25519(key) => PrivateKey::Ed25519(key.private()), diff --git a/biscuit-auth/src/token/authorizer.rs b/biscuit-auth/src/token/authorizer.rs index 6738839a..bed6eb6e 100644 --- a/biscuit-auth/src/token/authorizer.rs +++ b/biscuit-auth/src/token/authorizer.rs @@ -24,7 +24,6 @@ use std::{ }; mod snapshot; -pub use snapshot::*; /// used to check authorization policies on a token /// @@ -513,7 +512,7 @@ impl Authorizer { /// let biscuit = builder.build(&keypair).unwrap(); /// /// let mut authorizer = biscuit.authorizer().unwrap(); - /// let res: Vec<(String, i64)> = authorizer.query("data($name, $id) <- user($name, $id)").unwrap(); + /// let res: Vec<(String, i64)> = authorizer.query_all("data($name, $id) <- user($name, $id)").unwrap(); /// # assert_eq!(res.len(), 1); /// # assert_eq!(res[0].0, "John Doe"); /// # assert_eq!(res[0].1, 42); @@ -1019,103 +1018,153 @@ impl Authorizer { impl std::fmt::Display for Authorizer { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if !self.world.facts.is_empty() { - write!(f, "// Facts:\n")?; - } - + let mut has_facts = false; let mut all_facts = BTreeMap::new(); for (origin, factset) in &self.world.facts.inner { - let mut facts = Vec::new(); + let mut facts = HashSet::new(); for fact in factset { - facts.push(self.symbols.print_fact(&fact)); + facts.insert(self.symbols.print_fact(fact)); } - facts.sort(); + has_facts = has_facts || !facts.is_empty(); 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:")?; + } + for (origin, factset) in &all_facts { - write!(f, "// origin: {origin}\n")?; + let mut facts = factset.iter().collect::>(); + facts.sort(); - for fact in factset { - write!(f, "{};\n", fact)?; + if !facts.is_empty() { + writeln!(f, "// origin: {origin}")?; } - } - if !self.world.facts.is_empty() { - write!(f, "\n")?; + for fact in facts { + writeln!(f, "{};", fact)?; + } } - if !self.world.rules.inner.is_empty() { - write!(f, "// Rules:\n")?; + if has_facts { + writeln!(f)?; } - let mut rules_map: BTreeMap> = BTreeMap::new(); + let mut has_rules = false; + let mut rules_map: BTreeMap> = BTreeMap::new(); for ruleset in self.world.rules.inner.values() { + has_rules = has_rules || !ruleset.is_empty(); for (origin, rule) in ruleset { rules_map .entry(*origin) .or_default() - .push(self.symbols.print_rule(&rule)); + .insert(self.symbols.print_rule(rule)); } } + + 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:")?; + } + for (origin, rule_list) in &rules_map { - if *origin == usize::MAX { - write!(f, "// origin: authorizer\n")?; - } else { - write!(f, "// origin: {origin}\n")?; + if !rule_list.is_empty() { + if *origin == usize::MAX { + writeln!(f, "// origin: authorizer")?; + } else { + writeln!(f, "// origin: {origin}")?; + } } - let mut sorted_rule_list = rule_list.clone(); + let mut sorted_rule_list = rule_list.iter().collect::>(); sorted_rule_list.sort(); for rule in sorted_rule_list { - write!(f, "{};\n", rule)?; + writeln!(f, "{};", rule)?; } } - if !self.world.rules.inner.is_empty() { - write!(f, "\n")?; + if has_rules { + writeln!(f)?; } - if !self.authorizer_block_builder.checks.is_empty() - || self - .blocks - .iter() - .flat_map(|blocks| blocks.iter()) - .any(|block| !block.checks.is_empty()) - { - write!(f, "// Checks:\n")?; + let mut has_checks = false; + let mut checks_map: BTreeMap> = Default::default(); + + if let Some(blocks) = &self.blocks { + for (i, block) in blocks.iter().enumerate() { + let entry = checks_map.entry(i).or_default(); + has_checks = has_checks || !&block.checks.is_empty(); + for check in &block.checks { + entry.push(self.symbols.print_check(check)); + } + } } - if !self.authorizer_block_builder.checks.is_empty() { - write!(f, "// origin: authorizer\n")?; + let authorizer_entry = checks_map.entry(usize::MAX).or_default(); - for check in &self.authorizer_block_builder.checks { - write!(f, "{check};\n")?; - } + has_checks = has_checks || !&self.authorizer_block_builder.checks.is_empty(); + for check in &self.authorizer_block_builder.checks { + authorizer_entry.push(check.to_string()); } - if let Some(blocks) = &self.blocks { - for (i, block) in blocks.iter().enumerate() { - if !block.checks.is_empty() { - write!(f, "// origin: {i}\n")?; + if has_checks { + writeln!(f, "// Checks:")?; + } - for check in &block.checks { - write!(f, "{};\n", self.symbols.print_check(check))?; - } + for (origin, checks) in checks_map { + if !checks.is_empty() { + if origin == usize::MAX { + writeln!(f, "// origin: authorizer")?; + } else { + writeln!(f, "// origin: {origin}")?; } } + + for check in checks { + writeln!(f, "{};", &check)?; + } } - if !self.authorizer_block_builder.checks.is_empty() { - write!(f, "\n")?; + if has_checks { + writeln!(f)?; } if !self.policies.is_empty() { - write!(f, "// Policies:\n")?; + writeln!(f, "// Policies:")?; } for policy in self.policies.iter() { - write!(f, "{policy};\n")?; + writeln!(f, "{policy};")?; } Ok(()) @@ -1302,7 +1351,10 @@ impl AuthorizerExt for Authorizer { mod tests { use std::time::Duration; - use crate::{builder::BlockBuilder, KeyPair}; + use crate::{ + builder::{BiscuitBuilder, BlockBuilder}, + KeyPair, + }; use super::*; @@ -1599,4 +1651,80 @@ mod tests { .unwrap(); assert_eq!(block_facts_query_all_explicit.len(), 0); } + + #[test] + fn authorizer_display_before_and_after_authorization() { + let root = KeyPair::new(); + + let mut token_builder = BiscuitBuilder::new(); + token_builder + .add_code( + r#" + authority_fact(true); + authority_rule($v) <- authority_fact($v); + check if authority_fact(true), authority_rule(true); + "#, + ) + .unwrap(); + let token = token_builder.build(&root).unwrap(); + + let mut authorizer = token.authorizer().unwrap(); + authorizer + .add_code( + r#" + authorizer_fact(true); + authorizer_rule($v) <- authorizer_fact($v); + check if authorizer_fact(true), authorizer_rule(true); + allow if true; + "#, + ) + .unwrap(); + let output_before_authorization = authorizer.to_string(); + + assert!( + output_before_authorization.contains("authorizer_fact(true)"), + "Authorizer.to_string() displays authorizer facts even before running authorize()" + ); + + authorizer.authorize().unwrap(); + + let output_after_authorization = authorizer.to_string(); + assert!( + output_after_authorization.contains("authorizer_rule(true)"), + "Authorizer.to_string() displays generated facts after running authorize()" + ); + + assert_eq!( + r#"// Facts: +// origin: 0 +authority_fact(true); +authority_rule(true); +// origin: authorizer +authorizer_fact(true); +authorizer_rule(true); + +// Rules: +// origin: 0 +authority_rule($v) <- authority_fact($v); +// origin: authorizer +authorizer_rule($v) <- authorizer_fact($v); + +// Checks: +// origin: 0 +check if authority_fact(true), authority_rule(true); +// origin: authorizer +check if authorizer_fact(true), authorizer_rule(true); + +// Policies: +allow if true; +"#, + output_after_authorization + ); + } + + #[test] + fn empty_authorizer_display() { + let authorizer = Authorizer::new(); + assert_eq!("", authorizer.to_string()) + } } diff --git a/biscuit-auth/tests/capi.rs b/biscuit-auth/tests/capi.rs index d9a44264..9d0a1c61 100644 --- a/biscuit-auth/tests/capi.rs +++ b/biscuit-auth/tests/capi.rs @@ -116,10 +116,10 @@ right("file1", "read"); hello("world"); // Checks: -// origin: authorizer -check if right("efgh"); // origin: 1 check if operation("read"); +// origin: authorizer +check if right("efgh"); // Policies: allow if true;