The firestore data represents a collection of artifacts. Each artifact has a usersIds
field (more about this later) and a parentId
field that is null
(if this is a root artifact) or hold an id
to another artifact in the same collection.
Root artifacts can be shared between multiple users. So, every document with a parentId
equal to null
has an array of id
values in its usersIds
field. This artifact and every other artifact subordinated to it in the collection can be viewed and edited by the users on that array.
Permissions are only configurable at the root level. Child artifacts cannot override what was set in their root.
For example:
{ id: 'a', parentId: null, usersIds: ['joe', 'jane'] }
{ id: 'a1', parentId: 'a' }
{ id: 'a11', parentId: 'a1' }
{ id: 'b', parentId: null, usersIds: ['mary'] }
{ id: 'b1', parentId: 'b' }
Based on the example above, joe can view a, a1, and a11, but not b or b1.
How can I write firestore rules that impose what was discussed before? Can I use a recursive function?
2
Answers
This is gonna be hard to do in security rules, as there is no way to loop over the necessary documents. It helps for this to keep in mind that security rules don’t filter the actual data, but instead merely ensure that a query only requests data that it is authorized to read.
And just like I don’t think that you can capture your requirements in security rules on the current data structure, I can’t even think of a query that would only request the correct documents. Firestore queries can only filter on data in the documents that they return, and in this case you’re actually trying to filter on data in "parent" artifacts – that may not be returns.
For this type of authorization model, you’ll typically need to denormalize the user IDs for everyone who has access to an artifact into that specific artifact. So you’d end up with this:
With this data structure, you query suddenly becomes quite simple, and your security rules can check the
usersIds
field of the document that is actually being considered – rather than needing to do one of more levels of parent lookups.you need to use the
exists
,get
and array functions. You can see the functions of working with lists here: https://firebase.google.com/docs/reference/rules/rules.ListYou can also write primitive functions such as (this is just an example of what you should do, it doesn’t work):
I also advise you to have a full usersIds in each document, because the rules do not support loops and you will have to paint N levels manually (see the example below). In addition, you will pay for reading each document that will be extracted at the rule level.
If you need a tree structure, you can place your own rules in one of the documents and use it in the rules. For example, use the path
/artifacts/a/b/${docId}
and place rules like{ "/artifacts/a/b": ["userId1", "userId2"]}
in the document, and then use request.path, request.auth.uid, string and array functions.