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?
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:
{ id: 'a', parentId: null, usersIds: ['joe', 'jane'] }
{ id: 'a1', parentId: 'a', usersIds: ['joe', 'jane'] }
{ id: 'a11', parentId: 'a1', usersIds: ['joe', 'jane'] }
{ id: 'b', parentId: null, usersIds: ['mary'] }
{ id: 'b1', parentId: 'b', usersIds: ['mary'] }
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.