Reputation: 12063
Question:
For the different top-level firestore collections below, how to restrict access to all but one of the paths?
We are building a data schema in Firestore to support a chat app for teachers across multiple schools.
The top-level firestore collections include:
/siteAdminUsers
/schools
/schools/{schoolId}/teachers
/schools/{schoolId}/chats
Below is the security rules setup we are trying now - where we check for:
request.auth.token.chatFlatList
However, the read listener for /messages
is being blocked.
Error message:
FirebaseError: [code=permission-denied]: Missing or insufficient permissions
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
match /schools/{schoolId}/chats/{discussionId}/messages {
allow write: if false;
allow read: if request.auth != null
&& request.auth.token != null
&& request.auth.token.chatFlatList.val().contains($discussionId);
}
}
Details
We are using cloud functions for all data read/write, so for almost every case we can just block all client access.
The one exception is for the chat discussions, where we need to set a snapshot listener in the mobile client to know when there are new messages.
Sub-collection notes:
At a school, there are discussion sessions for school staff (teachers, admins, etc)
/schools/{schoolId}/chats/{discussionId}
Where each discussion-document contains:
/schools/{schoolId}/chats/{discussionId}/messages
User Claim code from Cloud Function
Looking at the cloud function logs, we have verified that the userClaim is being set.
return firebaseAdmin
.auth()
.setCustomUserClaims(
uid, {
chatFlatList: 'id1 id2 id3'
}
);
UPDATE #1
Tried the following variation where rules skip/omit the check on userClaim and auth.token.
However, still same permission error.
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
match /schools/{schoolId}/chats/{discussionId}/messages {
allow write: if false;
allow read: if request.auth != null;
}
}
}
Upvotes: 5
Views: 3145
Reputation: 527
This worked for me if I wanted to read and write all collection but not one collection named "backStage";
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{collection}/{document} {
allow read: if true
allow write: if (collection != "backStage");
}
}
}
Upvotes: 2
Reputation: 12063
Here's a solution (seems to be working), which includes the check on chatFlatList
user claim variable (from original question) for a substring :
match /schools/{schoolId}/chats/{discussionId}/messages {
allow write: if false;
allow read: if request.auth != null
&& request.auth.token.chatFlatList.matches(discussionId);
}
Figured this out thanks to:
Firebase storage rules based on custom parameters
$
notation to access the path var. I recall seeing this in a security rules example code example - maybe it's specific to database tiers?https://firebase.google.com/docs/reference/security/storage/#string
https://regex-golang.appspot.com/assets/html/index.html
Upvotes: 0
Reputation: 698
I think the issue here is that you are writing a rule on the collection called messages.
All match statements should point to documents, not collections. https://firebase.google.com/docs/firestore/security/rules-structure
You should try adding /{document=**} after your path to messages, something like:
match /schools/{schoolId}/chats/{discussionId}/messages/{document=**} {
allow write: if false;
allow read: if request.auth != null;
}
Upvotes: 4