diff --git a/Cargo.lock b/Cargo.lock index 0a095ba..d198762 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,8 +53,7 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "biscuit-auth" version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95490f2c91dc452247d00a2fb4779bcedb7693e669354fa1fe2a96679f4950cc" +source = "git+https://github.com/adamh-oai/biscuit-rust.git?branch=third-party#40b60a8fe2b60255ece2cad63b8c87dc420ed0fc" dependencies = [ "base64", "biscuit-parser", @@ -77,8 +76,7 @@ dependencies = [ [[package]] name = "biscuit-parser" version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9fd6da963e73f7e6db729c3bd76784863ba405b15acbbb5cb08ebc2adbd3bf" +source = "git+https://github.com/adamh-oai/biscuit-rust.git?branch=third-party#40b60a8fe2b60255ece2cad63b8c87dc420ed0fc" dependencies = [ "hex", "nom", @@ -102,22 +100,15 @@ dependencies = [ [[package]] name = "biscuit-quote" version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0071fe3634b644a8df1434e3e14841e480c4238059c66a94e479b7eff98c2bd3" +source = "git+https://github.com/adamh-oai/biscuit-rust.git?branch=third-party#40b60a8fe2b60255ece2cad63b8c87dc420ed0fc" dependencies = [ "biscuit-parser", - "proc-macro-error", + "proc-macro-error2", "proc-macro2", "quote", "syn 1.0.109", ] -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - [[package]] name = "block-buffer" version = "0.9.0" @@ -349,6 +340,12 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hex" version = "0.4.3" @@ -380,9 +377,9 @@ dependencies = [ [[package]] name = "indoc" -version = "1.0.9" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" [[package]] name = "itertools" @@ -414,16 +411,6 @@ version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.22" @@ -438,9 +425,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memoffset" -version = "0.8.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] @@ -488,29 +475,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", -] - [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -530,6 +494,12 @@ dependencies = [ "spki", ] +[[package]] +name = "portable-atomic" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" + [[package]] name = "powerfmt" version = "0.2.0" @@ -546,27 +516,25 @@ dependencies = [ ] [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "proc-macro-error-attr2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ - "proc-macro-error-attr", "proc-macro2", "quote", - "syn 1.0.109", - "version_check", ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "proc-macro-error2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ + "proc-macro-error-attr2", "proc-macro2", "quote", - "version_check", + "syn 2.0.85", ] [[package]] @@ -613,16 +581,17 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.18.3" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b1ac5b3731ba34fdaa9785f8d74d17448cd18f30cf19e0c7e7b1fdb5272109" +checksum = "f402062616ab18202ae8319da13fa4279883a2b8a9d9f83f20dbade813ce1884" dependencies = [ "cfg-if", "chrono", "indoc", "libc", "memoffset", - "parking_lot", + "once_cell", + "portable-atomic", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", @@ -631,9 +600,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.18.3" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cb946f5ac61bb61a5014924910d936ebd2b23b705f7a4a3c40b05c720b079a3" +checksum = "b14b5775b5ff446dd1056212d778012cbe8a0fbffd368029fd9e25b514479c38" dependencies = [ "once_cell", "target-lexicon", @@ -641,9 +610,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.18.3" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd4d7c5337821916ea2a1d21d1092e8443cf34879e53a0ac653fbb98f44ff65c" +checksum = "9ab5bcf04a2cdcbb50c7d6105de943f543f9ed92af55818fd17b660390fc8636" dependencies = [ "libc", "pyo3-build-config", @@ -651,25 +620,27 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.18.3" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d39c55dab3fc5a4b25bbd1ac10a2da452c4aca13bb450f22818a002e29648d" +checksum = "0fd24d897903a9e6d80b968368a34e1525aeb719d568dba8b3d4bfa5dc67d453" dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn 1.0.109", + "syn 2.0.85", ] [[package]] name = "pyo3-macros-backend" -version = "0.18.3" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97daff08a4c48320587b5224cc98d609e3c27b6d437315bd40b605c98eeb5918" +checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe" dependencies = [ + "heck", "proc-macro2", + "pyo3-build-config", "quote", - "syn 1.0.109", + "syn 2.0.85", ] [[package]] @@ -711,15 +682,6 @@ dependencies = [ "getrandom 0.2.15", ] -[[package]] -name = "redox_syscall" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" -dependencies = [ - "bitflags", -] - [[package]] name = "regex" version = "1.11.1" @@ -758,12 +720,6 @@ dependencies = [ "semver", ] -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "semver" version = "1.0.23" @@ -829,12 +785,6 @@ dependencies = [ "rand_core", ] -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - [[package]] name = "spki" version = "0.7.3" @@ -944,9 +894,9 @@ checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unindent" -version = "0.1.11" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" +checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" [[package]] name = "version_check" diff --git a/Cargo.toml b/Cargo.toml index 2e7d6d1..71e56a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,8 +8,10 @@ name = "biscuit_auth" crate-type = ["cdylib"] [dependencies] -biscuit-auth = { version = "5.0.0", features = ["pem"] } -pyo3 = { version = "0.18.3", features = ["extension-module", "chrono"]} +biscuit-auth = { git = "https://github.com/adamh-oai/biscuit-rust.git", branch = "third-party", features = [ + "pem", +] } #biscuit-auth = { version = "5.0.0", features = ["pem"] } +pyo3 = { version = "0.22.6", features = ["extension-module", "chrono"] } hex = "0.4" base64 = "0.13.0" chrono = "0.4" diff --git a/src/lib.rs b/src/lib.rs index 72effac..9c5340c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,8 @@ // There seem to be false positives with pyo3 #![allow(clippy::borrow_deref_ref)] use ::biscuit_auth::RootKeyProvider; +use ::biscuit_auth::ThirdPartyBlock; +use ::biscuit_auth::ThirdPartyRequest; use ::biscuit_auth::UnverifiedBiscuit; use chrono::DateTime; use chrono::Duration; @@ -53,19 +55,18 @@ struct PyKeyProvider { impl RootKeyProvider for PyKeyProvider { fn choose(&self, kid: Option) -> Result { Python::with_gil(|py| { - if self.py_value.as_ref(py).is_callable() { - let result = self - .py_value - .call1(py, (kid,)) + let bound = self.py_value.bind(py); + if bound.is_callable() { + let result = bound + .call1((kid,)) .map_err(|_| error::Format::UnknownPublicKey)?; let py_pk: PyPublicKey = result - .extract(py) + .extract() .map_err(|_| error::Format::UnknownPublicKey)?; Ok(py_pk.0) } else { - let py_pk: PyPublicKey = self - .py_value - .extract(py) + let py_pk: PyPublicKey = bound + .extract() .map_err(|_| error::Format::UnknownPublicKey)?; Ok(py_pk.0) } @@ -95,6 +96,7 @@ impl PyBiscuitBuilder { /// :param scope_parameters: public keys for the public key parameters in the datalog snippet /// :type scope_parameters: dict, optional #[new] + #[pyo3(signature = (source=None, parameters=None, scope_parameters=None))] fn new( source: Option, parameters: Option>, @@ -131,6 +133,7 @@ impl PyBiscuitBuilder { /// :type parameters: dict, optional /// :param scope_parameters: public keys for the public key parameters in the datalog snippet /// :type scope_parameters: dict, optional + #[pyo3(signature = (source, parameters=None, scope_parameters=None))] pub fn add_code( &mut self, source: &str, @@ -215,6 +218,22 @@ impl PyBiscuitBuilder { } } +#[pyclass(name="ThirdPartyBlock")] +pub struct PyThirdPartyBlock(ThirdPartyBlock); + + +#[pyclass(name="ThirdPartyRequest")] +pub struct PyThirdPartyRequest(ThirdPartyRequest); + +#[pymethods] +impl PyThirdPartyRequest { + pub fn create_block(&self, private_key: &PyPrivateKey, response: &PyBlockBuilder) -> PyResult { + self.0.clone().create_block(&private_key.0, response.0.clone()).map(PyThirdPartyBlock).map_err(|e| BiscuitBuildError::new_err(e.to_string())) + } +} + + + /// Representation of a biscuit token that has been parsed and cryptographically verified. #[pyclass(name = "Biscuit")] pub struct PyBiscuit(Biscuit); @@ -241,7 +260,7 @@ impl PyBiscuit { /// :return: the parsed and verified biscuit /// :rtype: Biscuit #[classmethod] - pub fn from_bytes(_: &PyType, data: &[u8], root: PyObject) -> PyResult { + pub fn from_bytes(_: &Bound, data: &[u8], root: PyObject) -> PyResult { match Biscuit::from(data, PyKeyProvider { py_value: root }) { Ok(biscuit) => Ok(PyBiscuit(biscuit)), Err(error) => Err(BiscuitValidationError::new_err(error.to_string())), @@ -259,7 +278,7 @@ impl PyBiscuit { /// :return: the parsed and verified biscuit /// :rtype: Biscuit #[classmethod] - pub fn from_base64(_: &PyType, data: &str, root: PyObject) -> PyResult { + pub fn from_base64(_: &Bound, data: &str, root: PyObject) -> PyResult { match Biscuit::from_base64(data, PyKeyProvider { py_value: root }) { Ok(biscuit) => Ok(PyBiscuit(biscuit)), Err(error) => Err(BiscuitValidationError::new_err(error.to_string())), @@ -328,6 +347,18 @@ impl PyBiscuit { .collect() } + pub fn third_party_request(&self) -> PyResult { + self.0.third_party_request().map_err(|e| BiscuitBuildError::new_err(e.to_string())).map(PyThirdPartyRequest) + } + + pub fn append_third_party(&self, public_key: &PyPublicKey, block: &PyThirdPartyBlock) -> PyResult { + self.0.append_third_party(public_key.0, block.0.clone()).map_err(|e| BiscuitBuildError::new_err(e.to_string())).map(PyBiscuit) + } + + pub fn external_public_keys(&self) -> Vec> { + self.0.external_public_keys().into_iter().map(|e| e.map(PyPublicKey)).collect() + } + fn __repr__(&self) -> String { self.0.print() } @@ -366,6 +397,7 @@ impl PyAuthorizer { /// :param scope_parameters: public keys for the public key parameters in the datalog snippet /// :type scope_parameters: dict, optional #[new] + #[pyo3(signature = (source=None, parameters=None, scope_parameters=None))] pub fn new( source: Option, parameters: Option>, @@ -386,6 +418,7 @@ impl PyAuthorizer { /// :type parameters: dict, optional /// :param scope_parameters: public keys for the public key parameters in the datalog snippet /// :type scope_parameters: dict, optional + #[pyo3(signature = (source, parameters=None, scope_parameters=None))] pub fn add_code( &mut self, source: &str, @@ -571,7 +604,7 @@ impl PyAuthorizer { /// :return: the authorizer /// :rtype: Authorizer #[classmethod] - pub fn from_base64_snapshot(_: &PyType, input: &str) -> PyResult { + pub fn from_base64_snapshot(_: &Bound, input: &str) -> PyResult { Ok(PyAuthorizer( Authorizer::from_base64_snapshot(input) .map_err(|error| BiscuitValidationError::new_err(error.to_string()))?, @@ -585,7 +618,7 @@ impl PyAuthorizer { /// :return: the authorizer /// :rtype: Authorizer #[classmethod] - pub fn from_raw_snapshot(_: &PyType, input: &[u8]) -> PyResult { + pub fn from_raw_snapshot(_: &Bound, input: &[u8]) -> PyResult { Ok(PyAuthorizer(Authorizer::from_raw_snapshot(input).map_err( |error| BiscuitValidationError::new_err(error.to_string()), )?)) @@ -619,6 +652,7 @@ impl PyBlockBuilder { /// :param scope_parameters: public keys for the public key parameters in the datalog snippet /// :type scope_parameters: dict, optional #[new] + #[pyo3(signature = (source=None, parameters=None, scope_parameters=None))] fn new( source: Option, parameters: Option>, @@ -680,6 +714,7 @@ impl PyBlockBuilder { /// :type parameters: dict, optional /// :param scope_parameters: public keys for the public key parameters in the datalog snippet /// :type scope_parameters: dict, optional + #[pyo3(signature = (source, parameters=None, scope_parameters=None))] pub fn add_code( &mut self, source: &str, @@ -734,7 +769,7 @@ impl PyKeyPair { /// :return: the corresponding keypair /// :rtype: KeyPair #[classmethod] - pub fn from_private_key(_: &PyType, private_key: PyPrivateKey) -> Self { + pub fn from_private_key(_: &Bound, private_key: PyPrivateKey) -> Self { PyKeyPair(KeyPair::from(&private_key.0)) } @@ -745,7 +780,7 @@ impl PyKeyPair { /// :return: the corresponding keypair /// :rtype: KeyPair #[classmethod] - pub fn from_private_key_der(_: &PyType, der: &[u8]) -> PyResult { + pub fn from_private_key_der(_: &Bound, der: &[u8]) -> PyResult { let kp = KeyPair::from_private_key_der(der) .map_err(|e: error::Format| pyo3::exceptions::PyValueError::new_err(e.to_string()))?; Ok(PyKeyPair(kp)) @@ -758,7 +793,7 @@ impl PyKeyPair { /// :return: the corresponding keypair /// :rtype: KeyPair #[classmethod] - pub fn from_private_key_pem(_: &PyType, pem: &str) -> PyResult { + pub fn from_private_key_pem(_: &Bound, pem: &str) -> PyResult { let kp = KeyPair::from_private_key_pem(pem) .map_err(|e: error::Format| pyo3::exceptions::PyValueError::new_err(e.to_string()))?; Ok(PyKeyPair(kp)) @@ -813,7 +848,7 @@ impl PyPublicKey { /// :return: the public key /// :rtype: PublicKey #[classmethod] - pub fn from_bytes(_: &PyType, data: &[u8]) -> PyResult { + pub fn from_bytes(_: &Bound, data: &[u8]) -> PyResult { match PublicKey::from_bytes(data) { Ok(key) => Ok(PyPublicKey(key)), Err(error) => Err(PyValueError::new_err(error.to_string())), @@ -827,7 +862,7 @@ impl PyPublicKey { /// :return: the public key /// :rtype: PublicKey #[classmethod] - pub fn from_hex(_: &PyType, data: &str) -> PyResult { + pub fn from_hex(_: &Bound, data: &str) -> PyResult { let data = match hex::decode(data) { Ok(data) => data, Err(error) => return Err(PyValueError::new_err(error.to_string())), @@ -869,7 +904,7 @@ impl PyPrivateKey { /// :return: the private key /// :rtype: PrivateKey #[classmethod] - pub fn from_bytes(_: &PyType, data: &[u8]) -> PyResult { + pub fn from_bytes(_: &Bound, data: &[u8]) -> PyResult { match PrivateKey::from_bytes(data) { Ok(key) => Ok(PyPrivateKey(key)), Err(error) => Err(PyValueError::new_err(error.to_string())), @@ -883,7 +918,7 @@ impl PyPrivateKey { /// :return: the private key /// :rtype: PrivateKey #[classmethod] - pub fn from_hex(_: &PyType, data: &str) -> PyResult { + pub fn from_hex(_: &Bound, data: &str) -> PyResult { let data = match hex::decode(data) { Ok(data) => data, Err(error) => return Err(PyValueError::new_err(error.to_string())), @@ -993,6 +1028,7 @@ pub struct PyFact(builder::Fact); impl PyFact { /// Build a datalog fact from the provided source and optional parameter values #[new] + #[pyo3(signature = (source, parameters=None))] pub fn new(source: &str, parameters: Option>) -> PyResult { let mut fact: builder::Fact = source .try_into() @@ -1054,6 +1090,7 @@ pub struct PyRule(builder::Rule); impl PyRule { /// Build a rule from the source and optional parameter values #[new] + #[pyo3(signature = (source, parameters=None, scope_parameters=None))] pub fn new( source: &str, parameters: Option>, @@ -1097,6 +1134,7 @@ pub struct PyCheck(builder::Check); #[pymethods] impl PyCheck { /// Build a check from the source and optional parameter values + #[pyo3(signature = (source, parameters=None, scope_parameters=None))] #[new] pub fn new( source: &str, @@ -1144,6 +1182,7 @@ pub struct PyPolicy(builder::Policy); impl PyPolicy { /// Build a check from the source and optional parameter values #[new] + #[pyo3(signature = (source, parameters=None, scope_parameters=None))] pub fn new( source: &str, parameters: Option>, @@ -1190,7 +1229,7 @@ impl PyUnverifiedBiscuit { /// :return: the parsed, unverified biscuit /// :rtype: UnverifiedBiscuit #[classmethod] - pub fn from_base64(_: &PyType, data: &str) -> PyResult { + pub fn from_base64(_: &Bound, data: &str) -> PyResult { match UnverifiedBiscuit::from_base64(data) { Ok(biscuit) => Ok(PyUnverifiedBiscuit(biscuit)), Err(error) => Err(BiscuitValidationError::new_err(error.to_string())), @@ -1264,7 +1303,7 @@ impl PyUnverifiedBiscuit { /// Main module for the biscuit_auth lib #[pymodule] -fn biscuit_auth(py: Python, m: &PyModule) -> PyResult<()> { +fn biscuit_auth(py: Python, m: &Bound) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; @@ -1278,17 +1317,17 @@ fn biscuit_auth(py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; - m.add("DataLogError", py.get_type::())?; - m.add("AuthorizationError", py.get_type::())?; - m.add("BiscuitBuildError", py.get_type::())?; - m.add("BiscuitBlockError", py.get_type::())?; + m.add("DataLogError", py.get_type_bound::())?; + m.add("AuthorizationError", py.get_type_bound::())?; + m.add("BiscuitBuildError", py.get_type_bound::())?; + m.add("BiscuitBlockError", py.get_type_bound::())?; m.add( "BiscuitValidationError", - py.get_type::(), + py.get_type_bound::(), )?; m.add( "BiscuitSerializationError", - py.get_type::(), + py.get_type_bound::(), )?; Ok(())