diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bc451516..df39d659 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,7 @@ name: Release on: push: tags: - - '*' + - "*" permissions: contents: write @@ -12,43 +12,34 @@ env: CARGO_TERM_COLOR: always jobs: - build: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: macos-latest-large + target_arch: x86_64-apple-darwin + - os: macos-latest + target_arch: aarch64-apple-darwin + - os: ubuntu-latest + target_arch: x86_64-unknown-linux-gnu steps: - - name: Checkout sources - uses: actions/checkout@v2 - - name: Cache - uses: actions/cache@v2 - with: - path: | - ~/.cargo - target/ - key: ${{ runner.os }}-cargoc-${{ hashFiles('Cargo.toml') }} - restore-keys: | - ${{ runner.os }}-cargoc-${{ hashFiles('Cargo.toml') }} - ${{ runner.os }}-cargoc- - ${{ runner.os }}-${{ hashFiles('Cargo.toml') }} - ${{ runner.os }}- - - name: Install cargo-c - uses: actions-rs/cargo@v1 - with: - command: install - args: cargo-c - - name: Run cargo-c tests - uses: actions-rs/cargo@v1 - with: - command: ctest - args: --release - - name: Build - uses: actions-rs/cargo@v1 - with: - command: cinstall - args: --release --prefix=/usr --destdir=./build - - name: Compress - run: tar cvzf biscuit_c-${{github.ref_name}}-x86_64.tar.gz -C build/ . - - name: Release - uses: softprops/action-gh-release@v1 - with: - files: biscuit_c-${{github.ref_name}}-x86_64.tar.gz + - name: Checkout sources + uses: actions/checkout@v4 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + target: ${{ matrix.target_arch }} + - name: Install cargo-c + run: cargo install cargo-c --version 0.10.5+cargo-0.83.0 + - name: Run cargo-c tests + run: cargo ctest --release --features="capi" + if: matrix.os == 'ubuntu-latest' && matrix.target_arch == 'x86_64-unknown-linux-gnu' + - name: Build C-API + run: cargo cinstall --release --prefix=/usr --destdir=./build --features="capi" + - name: Compress + run: tar cvzf biscuit_capi-${{github.ref_name}}-${{matrix.target_arch}}.tar.gz -C build/ . + - name: Release + uses: softprops/action-gh-release@v1 + with: + files: biscuit_capi-${{github.ref_name}}-${{matrix.target_arch}}.tar.gz diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 70b60f11..dc7c2006 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -2,69 +2,50 @@ name: Rust on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main, v5 ] + branches: [main, v5] env: CARGO_TERM_COLOR: always jobs: build: - runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Cache - uses: actions/cache@v2 - with: - path: | - ~/.cargo - target/ - key: ${{ runner.os }}-${{ hashFiles('Cargo.toml') }} - restore-keys: | - ${{ runner.os }}-${{ hashFiles('Cargo.toml') }} - ${{ runner.os }}- - - name: Build - run: cargo build --verbose - - name: Run tests - run: cargo test --features="serde-error,bwk" --verbose - - name: Check samples - run: | - cd biscuit-auth - cargo run --release --example testcases --features serde-error -- ./samples --json > ./samples/samples.json - git diff --exit-code - + - uses: actions/checkout@v2 + - name: Cache + uses: actions/cache@v2 + with: + path: | + ~/.cargo + target/ + key: ${{ runner.os }}-${{ hashFiles('Cargo.toml') }} + restore-keys: | + ${{ runner.os }}-${{ hashFiles('Cargo.toml') }} + ${{ runner.os }}- + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --features="serde-error,bwk" --verbose + - name: Check samples + run: | + cd biscuit-auth + cargo run --release --example testcases --features serde-error -- ./samples --json > ./samples/samples.json + git diff --exit-code capi: runs-on: ubuntu-latest steps: - - name: Checkout sources - uses: actions/checkout@v2 - - name: Cache - uses: actions/cache@v2 - with: - path: | - ~/.cargo - target/ - key: ${{ runner.os }}-cargoc-${{ hashFiles('Cargo.toml') }} - restore-keys: | - ${{ runner.os }}-cargoc-${{ hashFiles('Cargo.toml') }} - ${{ runner.os }}-cargoc- - ${{ runner.os }}-${{ hashFiles('Cargo.toml') }} - ${{ runner.os }}- - - name: Install cargo-c - uses: actions-rs/cargo@v1 - with: - command: install - args: cargo-c --version 0.9.14+cargo-0.66 - - name: Run cargo-c tests - uses: actions-rs/cargo@v1 - with: - command: ctest - args: --release + - name: Checkout sources + uses: actions/checkout@v4 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + - name: Install cargo-c + run: cargo install cargo-c --version 0.10.5+cargo-0.83.0 + - name: Run cargo-c tests + run: cargo ctest --features="capi" --release coverage: name: Coverage @@ -94,4 +75,4 @@ jobs: uses: codecov/codecov-action@v4.0.1 with: token: ${{ secrets.CODECOV_TOKEN }} - slug: biscuit-auth/biscuit-rust \ No newline at end of file + slug: biscuit-auth/biscuit-rust diff --git a/Cargo.toml b/Cargo.toml index 28254783..9dcf7dc8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["biscuit-auth", "biscuit-quote", "biscuit-parser"] +members = ["biscuit-auth", "biscuit-quote", "biscuit-parser", "biscuit-capi"] diff --git a/README.md b/README.md index 2ccb3b26..ce65f1e8 100644 --- a/README.md +++ b/README.md @@ -172,19 +172,7 @@ biscuit implementations come with a default symbol table to avoid transmitting f # C bindings -This project can generate C bindings with [cargo-c](https://crates.io/crates/cargo-c). - -compile it with: - -``` -cargo cinstall --prefix=/usr --destdir=./build -``` - -Run C integration tests with: - -``` -cargo ctest -``` +You can find the C bindings and documentation in the [`biscuit-capi`](./biscuit-capi/README.md) crate. ## License diff --git a/biscuit-auth/Cargo.toml b/biscuit-auth/Cargo.toml index 322cb555..334c7d68 100644 --- a/biscuit-auth/Cargo.toml +++ b/biscuit-auth/Cargo.toml @@ -12,9 +12,7 @@ repository = "https://github.com/biscuit-auth/biscuit-rust" [features] default = ["regex-full", "datalog-macro"] -regex-full = [ "regex/perf", "regex/unicode"] -# used by cargo-c to signal the compilation of C bindings -capi = ["inline-c"] +regex-full = ["regex/perf", "regex/unicode"] wasm = ["wasm-bindgen", "getrandom/wasm-bindgen"] # used by biscuit-wasm to serialize errors to JSON serde-error = ["serde", "biscuit-parser/serde-error"] @@ -33,12 +31,11 @@ sha2 = "^0.9" prost = "0.10" prost-types = "0.10" regex = { version = "1.5", default-features = false, features = ["std"] } -nom = {version = "7", default-features = false, features = ["std"] } +nom = { version = "7", default-features = false, features = ["std"] } hex = "0.4" zeroize = { version = "1", default-features = false } thiserror = "1" rand = { version = "0.8" } -inline-c = { version = "0.1", optional = true } wasm-bindgen = { version = "0.2", optional = true } base64 = "0.13.0" ed25519-dalek = { version = "2.0.0", features = ["rand_core", "zeroize"] } @@ -48,7 +45,9 @@ time = { version = "0.3.7", features = ["formatting", "parsing"] } uuid = { version = "1", optional = true } biscuit-parser = { version = "0.1.2", path = "../biscuit-parser" } biscuit-quote = { version = "0.2.2", optional = true, path = "../biscuit-quote" } -chrono = { version = "0.4.26", optional = true, default-features = false, features = ["serde"] } +chrono = { version = "0.4.26", optional = true, default-features = false, features = [ + "serde", +] } [dev-dependencies] @@ -64,23 +63,6 @@ codspeed-bencher-compat = "2.6.0" #[build-dependencies] #prost-build = "0.10" -[package.metadata.capi.library] -# Used as the library name and defaults to the crate name. This might get -# prefixed with `lib` depending on the target platform. -name = "biscuit_auth" - -include = [ - "Cargo.toml", - "cbindgen.toml", - "build.rs", - "examples/*.rs", - "LICENSE", - "README.md", - "src/*.rs", - "src/*/*.rs", - "tests/*.rs" -] - [[example]] name = "testcases" required-features = ["serde-error"] diff --git a/biscuit-auth/src/datalog/symbol.rs b/biscuit-auth/src/datalog/symbol.rs index ab2e4df3..a0b41ac2 100644 --- a/biscuit-auth/src/datalog/symbol.rs +++ b/biscuit-auth/src/datalog/symbol.rs @@ -206,6 +206,7 @@ impl SymbolTable { } } } + pub fn print_fact(&self, f: &Fact) -> String { self.print_predicate(&f.predicate) } diff --git a/biscuit-auth/src/lib.rs b/biscuit-auth/src/lib.rs index d09ea33e..dc6bd64a 100644 --- a/biscuit-auth/src/lib.rs +++ b/biscuit-auth/src/lib.rs @@ -235,12 +235,6 @@ pub use token::Biscuit; pub use token::RootKeyProvider; pub use token::{ThirdPartyBlock, ThirdPartyRequest}; -#[cfg(cargo_c)] -mod capi; - -#[cfg(cargo_c)] -pub use capi::*; - #[cfg(feature = "bwk")] mod bwk; #[cfg(feature = "bwk")] diff --git a/biscuit-capi/Cargo.toml b/biscuit-capi/Cargo.toml new file mode 100644 index 00000000..14ab13ae --- /dev/null +++ b/biscuit-capi/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "biscuit-capi" +version = "5.0.0" # Should keep the same version as biscuit-auth +description = "C API for Biscuit" +authors = ["Geoffroy Couprie "] +edition = "2018" +license = "Apache-2.0" +documentation = "https://docs.rs/biscuit-auth" +homepage = "https://github.com/biscuit-auth/biscuit" +repository = "https://github.com/biscuit-auth/biscuit-rust" + +[dependencies] +biscuit-auth = { version = "5.0.0", path = "../biscuit-auth", features = [ + "datalog-macro", + "serde-error", +] } +inline-c = { version = "0.1", optional = true } +rand = "0.8" + +[features] +default = [] +capi = ["inline-c"] + +[package.metadata.capi.library] +# Used as the library name and defaults to the crate name. This might get +# prefixed with `lib` depending on the target platform. +name = "biscuit_auth" + +[package.metadata.capi.header] +name = "biscuit_auth" diff --git a/biscuit-capi/LICENSE b/biscuit-capi/LICENSE new file mode 100644 index 00000000..57bc88a1 --- /dev/null +++ b/biscuit-capi/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/biscuit-capi/README.md b/biscuit-capi/README.md new file mode 100644 index 00000000..06035e66 --- /dev/null +++ b/biscuit-capi/README.md @@ -0,0 +1,36 @@ +## Biscuit C API + +This crate provides a C API for the Biscuit token library. It is a wrapper around the Rust API, and is intended to be used by other languages that can interface with C. + +### Building from sources + +To build the C API, you need to have the Rust toolchain installed. You can then build the C API by running: + +```sh +cargo cinstall --release --features="capi" --prefix=/path/to/install --destdir=/path/to/destdir +``` +It will produce a shared library in the `--destdir` directory and also the C headers. +You can then link against the generated library in your C code. + +`cargo cinstall` is provided by the [`cargo-c`](https://github.com/lu-zero/cargo-c/) crate, which you can install with `cargo install cargo-c`. + +### Downloading pre-built binaries +In the [releases section](https://github.com/biscuit-auth/biscuit-rust/releases) of this repository, you can find pre-built binaries for the C API. +Currently, only Linux x86_64 and MacOS (x86 + arm) are provided. + +### Running the tests + +```sh +cargo ctest --features="capi" +``` + +## License + +Licensed under Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be licensed as above, without any additional terms or +conditions. \ No newline at end of file diff --git a/biscuit-auth/cbindgen.toml b/biscuit-capi/cbindgen.toml similarity index 100% rename from biscuit-auth/cbindgen.toml rename to biscuit-capi/cbindgen.toml diff --git a/biscuit-auth/src/capi.rs b/biscuit-capi/src/lib.rs similarity index 82% rename from biscuit-auth/src/capi.rs rename to biscuit-capi/src/lib.rs index 3a80cbba..23a0b290 100644 --- a/biscuit-auth/src/capi.rs +++ b/biscuit-capi/src/lib.rs @@ -1,3 +1,4 @@ +#![allow(clippy::missing_safety_doc)] use rand::prelude::*; use std::{ cell::RefCell, @@ -6,10 +7,10 @@ use std::{ os::raw::c_char, }; -use crate::datalog::SymbolTable; +use biscuit_auth::datalog::SymbolTable; enum Error { - Biscuit(crate::error::Token), + Biscuit(biscuit_auth::error::Token), InvalidArgument, } @@ -22,14 +23,14 @@ impl fmt::Display for Error { } } -impl From for Error { - fn from(error: crate::error::Token) -> Self { +impl From for Error { + fn from(error: biscuit_auth::error::Token) -> Self { Error::Biscuit(error) } } thread_local! { - static LAST_ERROR: RefCell> = RefCell::new(None); + static LAST_ERROR: RefCell> = const { RefCell::new(None) }; } fn update_last_error(err: Error) { @@ -41,7 +42,7 @@ fn update_last_error(err: Error) { #[no_mangle] pub extern "C" fn error_message() -> *const c_char { thread_local! { - static LAST: RefCell> = RefCell::new(None); + static LAST: RefCell> = const { RefCell::new(None) }; } LAST_ERROR.with(|prev| match *prev.borrow() { Some(ref err) => { @@ -105,7 +106,7 @@ pub extern "C" fn error_kind() -> ErrorKind { Some(ref err) => match err { Error::InvalidArgument => ErrorKind::InvalidArgument, Error::Biscuit(e) => { - use crate::error::*; + use biscuit_auth::error::*; match e { Token::InternalError => ErrorKind::InternalError, Token::Format(Format::Signature(Signature::InvalidFormat)) => { @@ -186,7 +187,7 @@ pub extern "C" fn error_kind() -> ErrorKind { #[no_mangle] pub extern "C" fn error_check_count() -> u64 { - use crate::error::*; + use biscuit_auth::error::*; LAST_ERROR.with(|prev| match *prev.borrow() { Some(Error::Biscuit(Token::FailedLogic(Logic::Unauthorized { ref checks, .. }))) | Some(Error::Biscuit(Token::FailedLogic(Logic::NoMatchingPolicy { ref checks }))) => { @@ -198,7 +199,7 @@ pub extern "C" fn error_check_count() -> u64 { #[no_mangle] pub extern "C" fn error_check_id(check_index: u64) -> u64 { - use crate::error::*; + use biscuit_auth::error::*; LAST_ERROR.with(|prev| match *prev.borrow() { Some(Error::Biscuit(Token::FailedLogic(Logic::Unauthorized { ref checks, .. }))) | Some(Error::Biscuit(Token::FailedLogic(Logic::NoMatchingPolicy { ref checks }))) => { @@ -219,7 +220,7 @@ pub extern "C" fn error_check_id(check_index: u64) -> u64 { #[no_mangle] pub extern "C" fn error_check_block_id(check_index: u64) -> u64 { - use crate::error::*; + use biscuit_auth::error::*; LAST_ERROR.with(|prev| match *prev.borrow() { Some(Error::Biscuit(Token::FailedLogic(Logic::Unauthorized { ref checks, .. }))) | Some(Error::Biscuit(Token::FailedLogic(Logic::NoMatchingPolicy { ref checks }))) => { @@ -240,9 +241,9 @@ pub extern "C" fn error_check_block_id(check_index: u64) -> u64 { /// the string is overwritten on each call #[no_mangle] pub extern "C" fn error_check_rule(check_index: u64) -> *const c_char { - use crate::error::*; + use biscuit_auth::error::*; thread_local! { - static CAVEAT_RULE: RefCell> = RefCell::new(None); + static CAVEAT_RULE: RefCell> = const { RefCell::new(None) }; } LAST_ERROR.with(|prev| match *prev.borrow() { @@ -271,7 +272,7 @@ pub extern "C" fn error_check_rule(check_index: u64) -> *const c_char { #[no_mangle] pub extern "C" fn error_check_is_authorizer(check_index: u64) -> bool { - use crate::error::*; + use biscuit_auth::error::*; LAST_ERROR.with(|prev| match *prev.borrow() { Some(Error::Biscuit(Token::FailedLogic(Logic::Unauthorized { ref checks, .. }))) | Some(Error::Biscuit(Token::FailedLogic(Logic::NoMatchingPolicy { ref checks }))) => { @@ -288,12 +289,12 @@ pub extern "C" fn error_check_is_authorizer(check_index: u64) -> bool { }) } -pub struct Biscuit(crate::token::Biscuit); -pub struct KeyPair(crate::crypto::KeyPair); -pub struct PublicKey(crate::crypto::PublicKey); -pub struct BiscuitBuilder(crate::token::builder::BiscuitBuilder); -pub struct BlockBuilder(crate::token::builder::BlockBuilder); -pub struct Authorizer(crate::token::authorizer::Authorizer); +pub struct Biscuit(biscuit_auth::Biscuit); +pub struct KeyPair(biscuit_auth::KeyPair); +pub struct PublicKey(biscuit_auth::PublicKey); +pub struct BiscuitBuilder(biscuit_auth::builder::BiscuitBuilder); +pub struct BlockBuilder(biscuit_auth::builder::BlockBuilder); +pub struct Authorizer(biscuit_auth::Authorizer); #[no_mangle] pub unsafe extern "C" fn key_pair_new<'a>( @@ -311,7 +312,7 @@ pub unsafe extern "C" fn key_pair_new<'a>( let mut rng: StdRng = SeedableRng::from_seed(seed); - Some(Box::new(KeyPair(crate::crypto::KeyPair::new_with_rng( + Some(Box::new(KeyPair(biscuit_auth::KeyPair::new_with_rng( &mut rng, )))) } @@ -346,12 +347,12 @@ pub unsafe extern "C" fn key_pair_serialize(kp: Option<&KeyPair>, buffer_ptr: *m pub unsafe extern "C" fn key_pair_deserialize(buffer_ptr: *mut u8) -> Option> { let input_slice = std::slice::from_raw_parts_mut(buffer_ptr, 32); - match crate::crypto::PrivateKey::from_bytes(input_slice).ok() { + match biscuit_auth::PrivateKey::from_bytes(input_slice).ok() { None => { update_last_error(Error::InvalidArgument); None } - Some(privkey) => Some(Box::new(KeyPair(crate::crypto::KeyPair::from(&privkey)))), + Some(privkey) => Some(Box::new(KeyPair(biscuit_auth::KeyPair::from(&privkey)))), } } @@ -381,7 +382,7 @@ pub unsafe extern "C" fn public_key_serialize( pub unsafe extern "C" fn public_key_deserialize(buffer_ptr: *mut u8) -> Option> { let input_slice = std::slice::from_raw_parts_mut(buffer_ptr, 32); - match crate::crypto::PublicKey::from_bytes(input_slice).ok() { + match biscuit_auth::PublicKey::from_bytes(input_slice).ok() { None => { update_last_error(Error::InvalidArgument); None @@ -395,7 +396,7 @@ pub unsafe extern "C" fn public_key_free(_kp: Option>) {} #[no_mangle] pub unsafe extern "C" fn biscuit_builder() -> Option> { - Some(Box::new(BiscuitBuilder(crate::token::Biscuit::builder()))) + Some(Box::new(BiscuitBuilder(biscuit_auth::Biscuit::builder()))) } #[no_mangle] @@ -569,7 +570,7 @@ pub unsafe extern "C" fn biscuit_from<'a>( } let root = root?; - crate::token::Biscuit::from(biscuit, root.0) + biscuit_auth::Biscuit::from(biscuit, root.0) .map(Biscuit) .map(Box::new) .ok() @@ -695,184 +696,7 @@ pub unsafe extern "C" fn biscuit_block_count(biscuit: Option<&Biscuit>) -> usize let biscuit = biscuit.unwrap(); - biscuit.0.blocks.len() + 1 -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_fact_count( - biscuit: Option<&Biscuit>, - block_index: u32, -) -> usize { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return 0; - } - - let biscuit = biscuit.unwrap(); - - let block = match biscuit.0.block(block_index as usize) { - Ok(block) => block, - Err(e) => { - update_last_error(e.into()); - return 0; - } - }; - - block.facts.len() -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_rule_count( - biscuit: Option<&Biscuit>, - block_index: u32, -) -> usize { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return 0; - } - - let biscuit = biscuit.unwrap(); - - let block = match biscuit.0.block(block_index as usize) { - Ok(block) => block, - Err(e) => { - update_last_error(e.into()); - return 0; - } - }; - - block.rules.len() -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_check_count( - biscuit: Option<&Biscuit>, - block_index: u32, -) -> usize { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return 0; - } - - let biscuit = biscuit.unwrap(); - - let block = match biscuit.0.block(block_index as usize) { - Ok(block) => block, - Err(e) => { - update_last_error(e.into()); - return 0; - } - }; - - block.checks.len() -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_fact( - biscuit: Option<&Biscuit>, - block_index: u32, - fact_index: u32, -) -> *mut c_char { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - - let biscuit = biscuit.unwrap(); - - let block = match biscuit.0.block(block_index as usize) { - Ok(block) => block, - Err(e) => { - update_last_error(e.into()); - return std::ptr::null_mut(); - } - }; - - match block.facts.get(fact_index as usize) { - None => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - Some(fact) => match CString::new(biscuit.0.symbols.print_fact(fact)) { - Ok(s) => s.into_raw(), - Err(_) => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - }, - } -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_rule( - biscuit: Option<&Biscuit>, - block_index: u32, - rule_index: u32, -) -> *mut c_char { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - - let biscuit = biscuit.unwrap(); - - let block = match biscuit.0.block(block_index as usize) { - Ok(block) => block, - Err(e) => { - update_last_error(e.into()); - return std::ptr::null_mut(); - } - }; - - match block.rules.get(rule_index as usize) { - None => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - Some(rule) => match CString::new(biscuit.0.symbols.print_rule(rule)) { - Ok(s) => s.into_raw(), - Err(_) => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - }, - } -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_check( - biscuit: Option<&Biscuit>, - block_index: u32, - check_index: u32, -) -> *mut c_char { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - - let biscuit = biscuit.unwrap(); - - let block = match biscuit.0.block(block_index as usize) { - Ok(block) => block, - Err(e) => { - update_last_error(e.into()); - return std::ptr::null_mut(); - } - }; - - match block.checks.get(check_index as usize) { - None => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - Some(check) => match CString::new(biscuit.0.symbols.print_check(check)) { - Ok(s) => s.into_raw(), - Err(_) => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - }, - } + biscuit.0.block_count() } #[no_mangle] @@ -887,27 +711,24 @@ pub unsafe extern "C" fn biscuit_block_context( let biscuit = biscuit.unwrap(); - let block = if block_index == 0 { - &biscuit.0.authority - } else { - match biscuit.0.blocks.get(block_index as usize - 1) { - Some(b) => b, - None => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - } - }; + let context = biscuit.0.context(); - match &block.context { + match context.get(block_index as usize) { None => { - return std::ptr::null_mut(); + update_last_error(Error::Biscuit(biscuit_auth::error::Token::Format( + biscuit_auth::error::Format::InvalidBlockId(block_index as usize), + ))); + + std::ptr::null_mut() } - Some(context) => match CString::new(context.clone()) { - Ok(s) => s.into_raw(), - Err(_) => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); + Some(context) => match context { + None => std::ptr::null_mut(), + Some(context) => { + let c = CString::new(context.clone()); + match c { + Err(_) => std::ptr::null_mut(), + Ok(context_cstring) => context_cstring.into_raw(), + } } }, } @@ -915,7 +736,7 @@ pub unsafe extern "C" fn biscuit_block_context( #[no_mangle] pub unsafe extern "C" fn create_block() -> Box { - Box::new(BlockBuilder(crate::token::builder::BlockBuilder::new())) + Box::new(BlockBuilder(biscuit_auth::builder::BlockBuilder::new())) } #[no_mangle] @@ -1243,3 +1064,31 @@ pub unsafe extern "C" fn biscuit_print(biscuit: Option<&Biscuit>) -> *const c_ch } } } + +#[no_mangle] +pub unsafe extern "C" fn biscuit_print_block_source( + biscuit: Option<&Biscuit>, + block_index: u32, +) -> *const c_char { + if biscuit.is_none() { + update_last_error(Error::InvalidArgument); + return std::ptr::null(); + } + let biscuit = biscuit.unwrap(); + + let block_source = match biscuit.0.print_block_source(block_index as usize) { + Ok(s) => s, + Err(e) => { + update_last_error(Error::Biscuit(e)); + return std::ptr::null(); + } + }; + + match CString::new(block_source) { + Ok(s) => s.into_raw(), + Err(_) => { + update_last_error(Error::InvalidArgument); + std::ptr::null() + } + } +} diff --git a/biscuit-auth/tests/capi.rs b/biscuit-capi/tests/capi.rs similarity index 84% rename from biscuit-auth/tests/capi.rs rename to biscuit-capi/tests/capi.rs index 9d0a1c61..af7f615a 100644 --- a/biscuit-auth/tests/capi.rs +++ b/biscuit-capi/tests/capi.rs @@ -8,6 +8,7 @@ mod capi { (assert_c! { #include #include + #include #include "biscuit_auth.h" int main() { @@ -51,19 +52,19 @@ mod capi { if(error_kind() == LogicUnauthorized) { uint64_t error_count = error_check_count(); - printf("failed checks (%ld):\n", error_count); + printf("failed checks (%" PRIu64 "):\n", error_count); for(uint64_t i = 0; i < error_count; i++) { if(error_check_is_authorizer(i)) { uint64_t check_id = error_check_id(i); const char* rule = error_check_rule(i); - printf(" Authorizer check %ld: %s\n", check_id, rule); + printf(" Authorizer check %" PRIu64 ": %s\n", check_id, rule); } else { uint64_t check_id = error_check_id(i); uint64_t block_id = error_check_block_id(i); const char* rule = error_check_rule(i); - printf(" Block %ld, check %ld: %s\n", block_id, check_id, rule); + printf(" Block %" PRIu64 ", check %" PRIu64 ": %s\n", block_id, check_id, rule); } } @@ -76,10 +77,19 @@ mod capi { string_free(world_print); uint64_t sz = biscuit_serialized_size(b2); - printf("serialized size: %ld\n", sz); + printf("serialized size: %" PRIu64 "\n", sz); uint8_t * buffer = malloc(sz); uint64_t written = biscuit_serialize(b2, buffer); - printf("wrote %ld bytes\n", written); + printf("wrote %" PRIu64 " bytes\n", written); + + const char *biscuit_source = biscuit_print_block_source(b2, 0); + printf("biscuit block 0 source: %s\n", biscuit_source); + + uintptr_t count = biscuit_block_count(b2); + printf("biscuit block count: %" PRIuPTR "\n", count); + + char *block_context_0 = biscuit_block_context(b2, 0); + printf("biscuit block 0 context: %s\n", block_context_0); free(buffer); authorizer_free(authorizer); @@ -126,6 +136,10 @@ allow if true; serialized size: 322 wrote 322 bytes +biscuit block 0 source: right("file1", "read"); + +biscuit block count: 2 +biscuit block 0 context: (null) "#, ); }