diff --git a/Cargo.lock b/Cargo.lock index 0c14b9c..aa187f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,6 +97,7 @@ dependencies = [ "chrono", "hex", "pyo3", + "time", ] [[package]] @@ -455,6 +456,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" version = "0.2.17" @@ -883,12 +890,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.30" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", + "num-conv", "powerfmt", "serde", "time-core", @@ -903,10 +911,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.15" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] diff --git a/Cargo.toml b/Cargo.toml index a782157..5a56e05 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "biscuit-python" version = "0.2.1" edition = "2021" +rust-version = "1.79" [lib] name = "biscuit_auth" @@ -13,3 +14,4 @@ pyo3 = { version = "0.18.3", features = ["extension-module", "chrono"]} hex = "0.4" base64 = "0.13.0" chrono = "0.4" +time = "0.3.36" diff --git a/biscuit_test.py b/biscuit_test.py index 5181735..79a5358 100644 --- a/biscuit_test.py +++ b/biscuit_test.py @@ -431,4 +431,17 @@ def test_keypair_from_private_key_pem(): private_key_pem = "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIASZaU0NoF3KxABSZj5x1QwVOUZfiSbf6SAzz3qq1T1l\n-----END PRIVATE KEY-----" private_key_hex = "0499694d0da05dcac40052663e71d50c1539465f8926dfe92033cf7aaad53d65" kp = KeyPair.from_private_key_pem(pem=private_key_pem) - assert kp.private_key.to_hex() == private_key_hex \ No newline at end of file + assert kp.private_key.to_hex() == private_key_hex + + +def test_append_third_party_block(): + root_kp = KeyPair() + external_kp = KeyPair() + + builder = BiscuitBuilder("user({user});", {'user': "123"}) + biscuit = builder.build(root_kp.private_key) + + third_party_block = BlockBuilder("external_fact({fact});", {'fact': "456"}) + new_biscuit = biscuit.append_third_party_block(external_kp, third_party_block) + + assert new_biscuit.block_external_key(1).to_hex() == external_kp.public_key.to_hex() diff --git a/src/lib.rs b/src/lib.rs index b57df23..d8825fa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,6 +43,13 @@ create_exception!( pyo3::exceptions::PyException ); +impl BiscuitBuildError { + /// Helper function to build `BiscuitBuildError` from other errors. + pub fn from_err(error: impl ToString) -> PyErr { + Self::new_err(error.to_string()) + } +} + struct PyKeyProvider { py_value: PyObject, } @@ -315,6 +322,31 @@ impl PyBiscuit { .map(PyBiscuit) } + /// Create a new `Biscuit` by appending a third party attenuation block + /// + /// :param kp: keypair used to sign the block + /// :type kp: KeyPair + /// :param block: a builder for the new block + /// :type block: BlockBuilder + /// :return: the attenuated biscuit + /// :rtype: Biscuit + pub fn append_third_party_block( + &self, + kp: &PyKeyPair, + block: &PyBlockBuilder, + ) -> PyResult { + let third_party_block = self + .0 + .third_party_request() + .and_then(|req| req.create_block(&kp.private_key().0, block.0.clone())) + .map_err(BiscuitBuildError::from_err)?; + + self.0 + .append_third_party(kp.public_key().0, third_party_block) + .map_err(BiscuitBuildError::from_err) + .map(PyBiscuit) + } + /// The revocation ids of the token, encoded as hexadecimal strings #[getter] pub fn revocation_ids(&self) -> Vec { @@ -325,6 +357,21 @@ impl PyBiscuit { .collect() } + /// Get the external key of a block if it exists + /// + /// :param index: the block index + /// :type index: int + /// :return: the public key if it exists + /// :rtype: str | None + pub fn block_external_key(&self, index: usize) -> PyResult> { + let opt_key = self + .0 + .block_external_key(index) + .map_err(|e| BiscuitBlockError::new_err(e.to_string()))?; + + Ok(opt_key.map(PyPublicKey)) + } + fn __repr__(&self) -> String { self.0.print() }