I'm trying to write a rule in firebase security console with get() but I can't get resource data anyhow... I want documents and their subcollections to be readable to user, if user uid is in document field, array or map.
My collection structure:
/boards/(boardId)/...much more
Fields in (boardId) document:
name: "Board name"
ownerId: "MYID12345"
guestsId(array): ["MYID12345"]
guestsMap(map): [MYID12345: true]
Security rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /boards/{boardId=**} {
// rules here
}
}
}
What I have tried so far:
allow read: if get(/databases/$(database)/documents/boards/$(boardId)).data.guestsMap[request.auth.uid] == true;
allow read: if request.auth.uid in get(/databases/$(database)/documents/boards/$(boardId)).data.guestsMap;
allow read: if request.auth.uid in get(/databases/$(database)/documents/boards/$(boardId)).data.guestsMap[true];
allow read: if get(/databases/$(database)/documents/boards/$(boardId)).request.data.guestsId[request.auth.uid];
allow read: if request.auth.uid in get(/databases/$(database)/documents/boards/$(boardId)).data.guestsId;
allow read: if request.auth.uid in get(/databases/$(database)/documents/boards/$(boardId)).request.data.guestsId;
allow read: if get(/databases/$(database)/documents/boards/$(boardId)).data.ownerId == request.auth.uid;
allow read: if get(/databases/$(database)/documents/boards/$(boardId)).data.ownerId == "MYID12345";
allow read: if get(/databases/$(database)/documents/boards/$(boardId)).resource.data.ownerId == request.auth.uid;
allow read: if get(/databases/$(database)/documents/boards/$(boardId)).request.data.ownerId == request.auth.uid;
None of these worked, always getting:
ERROR FirebaseError: Missing or insufficient permissions.
allow read: if true, makes the application run normally.
I'm sticking to the documentation but it's not working for me...
#update
match /boards/{boardId=**} {
allow read: if resource.data.ownerId == request.auth.uid;
}
Also can't use it like this, because then, every subcollection is looking for ownerId field in it's documents, and ownerId or friend list array are only in board document.
#update
I tried to do like so, but it didn't help:
match /boards/{boardId} {
allow read, write: if request.auth.uid in resource.data.guestsId || request.auth.uid == resource.data.ownerId;
allow create: if exists(/databases/$(database)/documents/users/$(request.auth.uid));
function passResource() {
return request.auth.uid in resource.data.guestsId || request.auth.uid == resource.data.ownerId;
}
match /categoryList/{categoryId} {
allow read, write: if passResource();
}
...
}
What am I missing here?
Okay I found a working solution:
match /databases/{database}/documents {
match /boards/{boardId} {
allow read: if request.auth.uid in resource.data.guestsId || request.auth.uid == resource.data.ownerId;
allow write: if request.auth.uid == resource.data.ownerId;
allow create: if exists(/databases/$(database)/documents/users/$(request.auth.uid));
function isAllowed() {
return request.auth.uid in get(/databases/$(database)/documents/boards/$(boardId)).data.guestsId || request.auth.uid == get(/databases/$(database)/documents/boards/$(boardId)).data.ownerId;
}
match /categoryList/{category} {
allow read, write: if isAllowed();
match /taskList/{task} {
allow read, write: if isAllowed();
}
}
...
}
Also funny it didn't want to work like this:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /boards/{boardId=**} {
allow read, write: if request.auth.uid in get(/databases/$(database)/documents/boards/$(boardId)).data.guestsId || request.auth.uid == get(/databases/$(database)/documents/boards/$(boardId)).data.ownerId;
allow create: if exists(/databases/$(database)/documents/users/$(request.auth.uid));
}
}
because:
Error: simulator.rules line [5], column [49]. Property guestsId is undefined on object.
Related
I want all authenticated users to read the collection but only the user with the uid that is specified in the field named uid should be able to write.
service cloud.firestore {
match /taken/{doc}{
allow read: if request.auth.uid != null;
allow read,write: if request.auth.uid == doc.id;
}
}
}
However, the above code does not allow write access even if the query has the right uid.
Try this:
service cloud.firestore {
match /taken/{doc}{
allow read: if request.auth != null;
allow create: if true; // resource.data is not defined on create, hence the separate case
allow update, delete: if request.auth.uid == resource.data.uid;
}
}
}
And you can even force the uid field to be set on create, depending on your logic:
service cloud.firestore {
match /taken/{doc}{
allow read: if request.auth != null;
allow create: if request.auth.uid == request.resource.data.uid; // Forces uid to be set to the user's uid
allow update, delete: if request.auth.uid == resource.data.uid;
}
}
}
You can read data of the document being access using resource object as explained in the documentation.
service cloud.firestore {
match /taken/{doc}{
allow read: if request.auth != null;
allow write: if request.auth.uid == resource.data.id;
}
}
}
The above rules allow any authenticated users to read the documents in 'taken' collection but only user with that UID in the documentation to write it.
I am getting the error:
FirebaseError: Missing or insufficient permissions when trying to view a document in a subcollection. I'm trying to get at the documents 'character' in subcollection 'settings'.
systemsCol()
.doc(localStorage.getItem("systemId"))
.collection('settings')
.doc('character')
How can i set all items and subdocuments for each 'system' document?
Here are my permissions
service cloud.firestore {
function authed (auth, data){
return auth.uid == data.owner.uid || auth.token.email in data.collaborators;
}
match /databases/{database}/documents {
match /systems {
allow read, write:
if request.auth.uid != null;
match /{systemId} {
allow list, create:
if request.auth.uid != null;
allow get, update, delete:
if authed(request.auth, resource.data);
}
match /{systemId}/{document=**} {
allow read, write:
if authed(request.auth, resource.data);
}
}
}
}
Also tried
service cloud.firestore {
function authed (auth, data){
return auth.uid == data.owner.uid || auth.token.email in data.collaborators;
}
match /databases/{database}/documents {
match /systems {
allow read, write:
if request.auth.uid != null;
match /{systemId} {
allow list, create:
if request.auth.uid != null;
allow get, update, delete:
if authed(request.auth, resource.data);
match /{document=**} {
allow read, write:
if authed(request.auth, get(/systems/{systemId}).data);
}
}
}
}
}
I have a "game" collection on firestore with a "levels" sub-collection. I'm trying to set-up the security rules so that you can only access game or level you created.
All documents (games or levels) have an authorId field with the uid of the user that created them. I have try this rule but still got an Missing or insufficient permissions error:
service cloud.firestore {
match /databases/{database}/documents {
match /games/{document=**} {
allow read, write: if document.data.authorId == request.auth.uid;
}
}
}
What am I missing?
I have tried the following rules too with no success:
service cloud.firestore {
match /databases/{database}/documents {
match /games/{game}/levels/{level} {
allow read, write: if level.data.authorId == request.auth.uid;
}
}
}
service cloud.firestore {
match /games/{game} {
allow read, write: if game.data.authorId == request.auth.uid;
match /levels/{level} {
allow read, write: if level.data.authorId == request.auth.uid;
}
}
}
According to the reference documentation, resource is the object that contains the document data that the user is trying to write. You use its data property to get a hold of its field values.
service cloud.firestore {
match /databases/{database}/documents {
match /games/{document=**} {
allow read, write: if resource.data.authorId == request.auth.uid;
}
}
}
match /databases/{database}/documents {
match /users/{uid} {
allow read, write: if request.auth.uid == uid;
}
match /data/{uid}/temperature
/{document=**}{
allow read, write: if request.auth.uid == uid;
}
match /data/{uid}/blood_pressure
/{document=**}{
allow read, write: if request.auth.uid == uid;
}
}
}
I did this to access subcollections "blood_pressure" and "temperature" for only authenticated users. It works fine for me.
I am using Cloud Firestore and I can't seem to get the "IN" operator to work with the security rules. I have tried using array and map but neither work. Of course when I set it to allow read, write; it works fine. What am I doing wrong?
Rules:
service cloud.firestore {
match /databases/{database}/documents {
match /rooms/{roomId=**} {
allow read, write: if request.auth.uid in resource.data.users;
allow read, write: if request.auth.uid in resource.data.users2;
allow create: if request.auth != null;
}
match /user-rooms/{userId} {
allow read, write: if userId == request.auth.uid;
}
match /users/{userId} {
allow read, write: if request.auth.uid == userId;
allow get, create: if request.auth != null;
}
}
}
Client:
db.collection("rooms")
.document(self.room.getRoomId())
.collection("messages")
.addSnapshotListener { .....
//Room is: d6l946swspNSouANzVdZ
//Username is: eX8gkxJNDREv
data will return it's direct children not it's sub-children(users and users2) so you should use get and exists instead of in
match /rooms/{roomId=**} {
allow read, write: if request.auth.uid in get(/databases/$(database)/documents/rooms/$(roomId)/users/$(request.auth.uid)).data;
allow read, write: if exists(/databases/$(database)/documents/rooms/$(roomId)/users2/$(request.auth.uid));
allow create: if request.auth != null;
}
checkout the doc
You're trying to access a variable named "users" inside resource.data which doesn't exist. The resource variable contains data from the object that is currently being written to the database.
What you're probably trying to do is check if this users exist in the fields users and users2, which can be achieved with the rules:
match /rooms/{roomId=**}{
allow read, write: if (exists(/databases/$(database)/documents/rooms/$(roomId)/users2/$(request.auth.uid)) ||
request.auth.uid in get(/databases/$(database)/documents/rooms/$(roomId)).data.users);
allow create: if request.auth!=null;
}
I'm implementing a recipe book in Firestore where every user is able to see all the recipes all users created but only the original author of the recipe is allowed to edit or delete the recipe. Any user is also allowed to create a new recipe.
My problem is that I am unable to setup the permissions a subcollection to "listen" on a field of the subcollections parentdocument.
Each recipe document contains three things. A field called name where the name of the recipe is stored, a field called creatorUID where the request.auth.uid of the creators uid is stored and a subcollection called ingredients containing documents with some random fields.
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth != null;
}
match /ListOfRecipes/{recipe} {
allow read, create: if isSignedIn();
allow update, delete: if resource.data.creatorUID == request.auth.uid;
match /{list=**} {
allow read: if isSignedIn();
// Should return true if recipe.creatorUID has same value as request.auth.uid
allow write: if recipe.creatorUID == request.auth.uid;
}
}
}
}
The problem is that with these rules it only works to create the recipe document. The subcollection and it's documents are not created since the db says
FirebaseError: [code=permission-denied]: Missing or insufficient permissions.
FirebaseError: Missing or insufficient permissions.
The calls is made from Angular client and it's official library.
Rules don't cascade, so you'll need to perform whatever checks you need for the document being captured by the Rules.
Generally speaking, {x=**} rules are more often a mistake and the usage of =** only for extremely specific use cases.
From your question, I'm assuming your data mode is something like this:
/ListofRecipes/{recipe_document}/List/{list_document}
In this case, you'll need your Rules to be configured something like this:
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth != null;
}
match /ListOfRecipes/{recipe} {
allow read, create: if isSignedIn();
allow update, delete: if resource.data.creatorUID == request.auth.uid;
function recipeData() {
return get(/databases/$(database)/documents/ListOfRecipes/$(recipe)).data
}
match /List/{list} {
allow read: if isSignedIn();
allow write: if recipeData().creatorUID == request.auth.uid;
}
}
}
}
Dan's answer above works great! Just for reference, in my case I only needed the root parent document ID, you can use the variable from the match statement above the nested one, like this:
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth != null;
}
match /ListOfRecipes/{recipeID} {
allow read, create: if isSignedIn();
allow update, delete: if resource.data.creatorUID == request.auth.uid;
match /List/{list} {
allow read: if isSignedIn();
allow write: if recipeID == 'XXXXX';
}
}
}
}
Building upon Dan's answer, you should be able to reduce the number of reads on your database for update and delete on the subcollection by adding the creatorUID to the subcollection document.
You'll have to restrict create to just the creator and make sure the creatorUID is set. Here's my modification of Dan's rules:
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth != null;
}
match /ListOfRecipes/{recipe} {
allow read, create: if isSignedIn();
allow update, delete: if resource.data.creatorUID == request.auth.uid;
function recipeData() {
return get(/databases/$(database)/documents/ListOfRecipes/$(recipe)).data
}
match /List/{list} {
allow read: if isSignedIn();
allow update, delete: if resource.data.creatorUID == request.auth.uid;
allow create: if recipeData().creatorUID == request.auth.uid
&& request.resource.data.creatorUID == request.auth.uid;
}
}
}
}