I have the following rule in my Cloud Firestore database:
service cloud.firestore {
match /databases/{database}/documents {
match /performances/{performanceId} {
allow read, update, delete: if request.auth.uid == resource.data.owner;
allow create: if request.auth.uid != null;
}
}
}
The idea is you can read and write to a performance if you own it, or create a performance if you are logged in.
This query works fine:
db.collection("performances").whereEqualTo(FieldPath.of("owner"), user.getUid())
However if I want to get at the contents of the 'scenes' subcollection I get an error: "com.google.firebase.firestore.FirebaseFirestoreException: PERMISSION_DENIED: Missing or insufficient permissions." That's with the following query:
db.collection("performances")
.document(performanceID)
.collection("scenes");
I assume I need to restrict the query to something like the following, but this wont work as the output of whereEqualTo is a Query and not a CollectionReference so I can't access the 'document':
db.collection("performances")
.whereEqualTo(FieldPath.of("owner"), user.getUid())
.document(performanceID)
.collection("scenes");
So, does anyone know how I should access a subcollection if the main collection has security rules?
Update 1 (because the code doesnt format in the comment below)
I think I may have come up with a solution. I didnt realise that my security rule would deny reads from subcollections by default, so changing it to allow all reads and writes to scenes within a performance makes it work fine:
service cloud.firestore {
match /databases/{database}/documents {
match /performances/{performanceId} {
allow read, update, delete: if request.auth.uid == resource.data.owner;
allow create: if request.auth.uid != null;
match /scenes/{sceneId} {
allow read, write: if true
}
}
}
}
First, please note that rules don't cascade, so your solution actually opens up all the documents in all scenes subcollections to the world, not just the owner of the parent document.
You'll need to check the permissions on the parent document using the get() method in Rules.
service cloud.firestore {
match /databases/{database}/documents {
match /performances/{performanceId} {
allow read, update, delete: if request.auth.uid == resource.data.owner;
allow create: if request.auth.uid != null;
function parentDoc() {
return get(/databases/$(database)/documents/performances/$(performanceId)).data;
}
match /scenes/{sceneId} {
allow read, write: if parentDoc().owner = request.auth.uid;
}
}
}
}
Here in the subcollection rules we use the previously captured path segments to find the parent document we need to check.
Lastly, you'll probably want to tighten up your create rule as well. Currently it allows someone to create a document owned by someone else (or no-one). I doubt you want that. By also checking that the requester's id is in the incoming document you can prevent potential bugs that allow creating documents that the user cannot then read:
allow create: if request.auth.uid != null && request.auth.uid == request.resource.data.owner;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With