biscuit-rust icon indicating copy to clipboard operation
biscuit-rust copied to clipboard

Hidden state in `Authorizer`

Open divarvel opened this issue 2 years ago • 1 comments

An Authorizer value wraps a datalog::World value, which contains facts and rules used for datalog evaluation. The Authorizer struct also contains an (optional) Vec<Block> representing a token (it is optional because an Authorizer can be created without a token. It also contains a BlockBuilder which contains facts, rules and checks added to the authorizer, as well as a Vec<Policy> containing policies added to the authorizer.

After calling authorize(), the World value contains facts from the token blocks, facts from the authorizer block builder and facts generated by rules.

The issue is that the World value does not always contain facts and rules from the authorizer builder:

  • when adding a token to an authorizer, all facts and rules are loaded in the World
  • when adding a fact or rule to an authorizer, it is added to the BlockBuilder but not the world
  • when calling authorize(), facts and rules from the BlockBuilder are copied to the world, and datalog generation is run
  • when calling query on Authorizer, datalog generation is automatically run on the current state of World (authorizer facts are not taken into account unless authorize() has been called first)

So, depending on whether authorize() and query() have been called, the authorizer world can be in various states:

  • no authorizer facts or rules, no generated facts at all
  • no authorizer facts or rules, but facts generated from the token contents
  • authorizer facts or rules, and facts generated from both the token contents and the authorizer
  • authorizer snapshots take a faithful copy of the inner World value, but only for facts. So taking a snapshot from an authorizer and then re-creating an authorizer from the snapshot will result in an authorizer with no authorizer rules in the datalog world

this makes relying on World contents tricky.

  • impl Display for Authorizer only uses World for facts and rules currently -> this needs to be fixed (#195)
  • Authorizer.query() only uses World, by design

Open questions:

  • what should query() return? would it be possible to avoid running datalog generation in query if we can ensure it's done elsewhere?
  • would it be possible to keep the World value in sync with the BlockBuilder. what would be the cost?
  • does it make sense to continue adding facts and rules to an Authorizer after having called authorize()?

This affects #192 and #193

divarvel avatar Oct 30 '23 16:10 divarvel

after thinking things through and experimenting, here's what I think:

  • the datalog world should always be up-to-date wrt facts and rules from the token and authorizer blocks (this might be a bit tricky to do because of the optional scope annotation for the whole authorizer block)
  • datalog generation should be available as an explicit method
  • datalog generation should stay explicit since it is a costly operation

divarvel avatar Oct 31 '23 13:10 divarvel

Fixed by https://github.com/biscuit-auth/biscuit-rust/pull/250

Geal avatar Nov 29 '24 09:11 Geal