diff --git a/CHANGELOG.md b/CHANGELOG.md index ca5ba13..63605f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 0.2.1 + +- support for `UnverifiedBiscuit` +- support for snapshots + # 0.2.0 - support for root key id (#5) diff --git a/biscuit_test.py b/biscuit_test.py index fb63106..16e1622 100644 --- a/biscuit_test.py +++ b/biscuit_test.py @@ -265,6 +265,56 @@ def test_complete_lifecycle(): assert facts[0].name == "u" assert facts[0].terms == ["1234"] +def test_snapshot(): + private_key = PrivateKey.from_hex("473b5189232f3f597b5c2f3f9b0d5e28b1ee4e7cce67ec6b7fbf5984157a6b97") + root = KeyPair.from_private_key(private_key) + + biscuit_builder = BiscuitBuilder("user({id})", { 'id': "1234" }) + + for right in ["read", "write"]: + biscuit_builder.add_fact(Fact("fact({right})", { 'right': right})) + + token = biscuit_builder.build(private_key).append(BlockBuilder('check if user($u)')).to_base64() + + parsedToken = Biscuit.from_base64(token, root.public_key) + + authorizer = Authorizer("allow if user({id})", { 'id': "1234" }) + + print(authorizer) + authorizer.add_token(parsedToken) + + snapshot = authorizer.base64_snapshot() + parsed = Authorizer.from_base64_snapshot(snapshot) + assert repr(authorizer) == repr(parsed) + + policy = parsed.authorize() + + assert policy == 0 + + rule = Rule("u($id) <- user($id), $id == {id}", { 'id': "1234"}) + facts = parsed.query(rule) + + assert len(facts) == 1 + assert facts[0].name == "u" + assert facts[0].terms == ["1234"] + + # raw_snapshot() returns a list of bytes, not a `bytes` value directly + return + raw_snapshot = authorizer.raw_snapshot() + parsed_from_raw = Authorizer.from_raw_snapshot(raw_snapshot) + assert repr(authorizer) == repr(parsed_from_raw) + + raw_policy = raw_parsed.authorize() + + assert raw_policy == 0 + + rule = Rule("u($id) <- user($id), $id == {id}", { 'id': "1234"}) + raw_facts = raw_parsed.query(rule) + + assert len(raw_facts) == 1 + assert raw_facts[0].name == "u" + assert raw_facts[0].terms == ["1234"] + def test_public_keys(): # Happy path (hex to bytes and back) public_key_from_hex = PublicKey.from_hex("acdd6d5b53bfee478bf689f8e012fe7988bf755e3d7c5152947abc149bc20189") diff --git a/docs/basic-use.rst b/docs/basic-use.rst index 936f824..bb12f1b 100644 --- a/docs/basic-use.rst +++ b/docs/basic-use.rst @@ -113,3 +113,9 @@ Query an authorizer 'user' >>> facts[0].terms ['1234'] + +Save and load snapshots +----------------------- + +>>> snapshot = authorizer.base64_snapshot() +>>> parsed = Authorizer.from_base64_snapshot(snapshot) diff --git a/src/lib.rs b/src/lib.rs index 388870b..24ce12b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -507,6 +507,53 @@ impl PyAuthorizer { .collect()) } + /// Take a snapshot of the authorizer and return it, base64-encoded + /// + /// :return: a snapshot as a base64-encoded string + /// :rtype: str + pub fn base64_snapshot(&self) -> PyResult { + self.0 + .to_base64_snapshot() + .map_err(|error| BiscuitSerializationError::new_err(error.to_string())) + } + + /// Take a snapshot of the authorizer and return it, as raw bytes + /// + /// :return: a snapshot as raw bytes + /// :rtype: bytes + pub fn raw_snapshot(&self) -> PyResult> { + self.0 + .to_raw_snapshot() + .map_err(|error| BiscuitSerializationError::new_err(error.to_string())) + } + + /// Build an authorizer from a base64-encoded snapshot + /// + /// :param input: base64-encoded snapshot + /// :type input: str + /// :return: the authorizer + /// :rtype: Authorizer + #[classmethod] + pub fn from_base64_snapshot(_: &PyType, input: &str) -> PyResult { + Ok(PyAuthorizer( + Authorizer::from_base64_snapshot(input) + .map_err(|error| BiscuitValidationError::new_err(error.to_string()))?, + )) + } + + /// Build an authorizer from a snapshot's raw bytes + /// + /// :param input: raw snapshot bytes + /// :type input: bytes + /// :return: the authorizer + /// :rtype: Authorizer + #[classmethod] + pub fn from_raw_snapshot(_: &PyType, input: &[u8]) -> PyResult { + Ok(PyAuthorizer(Authorizer::from_raw_snapshot(input).map_err( + |error| BiscuitValidationError::new_err(error.to_string()), + )?)) + } + fn __repr__(&self) -> String { self.0.to_string() }