Reputation: 57502
Let's say I have a restaurants
collection with menus
subcollection. The restaurant permission is set inside the restaurant document and it looks like this:
Restaurant
{
"roles": {
"user123": "Owner"
}
}
Anyone who has access to the restaurant also has access to any of its subcollections, including the menus
subcollection. Here is the security rule for this:
Security Rule
match /restaurants/{restaurantId}/{document=**} {
allow read: if resource.data.roles[request.auth.uid] > '' ;
}
Querying for the list of restaurants can be accomplished like this and it works well:
Query for restaurants
firebase
.firestore()
.collection('restaurants')
.where(`roles.${uid}`, '>', '');
Now how do I query the menus
subcollection? I understand that Firestore needs to infer the permission based on the query without looking at the underlying data. Logically this should be possible, as anyone who has access to the restaurant
also has access to the menus
subcollection. But how do I compose this menus
subcollection query so that Firestore can infer this? The most logical query I can come up with is something like this:
Query for menus subcollection (doesn't work)
firebase
.firestore()
.doc(`restaurants/${restaurantId}`) // user has access to this restaurant
.collection('menus');
...but that doesn't work with permission being denied.
Upvotes: 2
Views: 1273
Reputation: 57502
Well it turns out that I need to create 2 security rules at the restaurant
level to achieve what I want:
match /restaurants/{restaurantId} {
allow read: if resource.data.roles[request.auth.uid] > ''
}
match /restaurant/{restaurantId}/{document=**} {
allow read: if get(/databases/$(database)/documents/restaurants/$(restaurantId)).data.roles[request.auth.uid] > '';
}
The first rule is necessary in order to query for restaurants that the user has permission to. The second query alone won't work, although I'm not sure why. This is the query for the first rule:
firebase
.firestore()
.collection('restaurants')
.where(`roles.${uid}`, '>', '');
The second rule is necessary for all subcollection queries. Here's an example:
firebase
.firestore()
.collection(`restaurants/${restaurantId}/menus`);
The fact that the second rule allows the subcollection query is very interesting, as Firestore needs to read the restaurant
document in order to permit this query. This contradicts its claims that it doesn't look at the underlying data for queries, but it looks like it does if it needs to read just one document within a path.
Upvotes: 7