Firebase firestore rules, read only when document contain specific value - firebase

I was learning through official doc for security rules, but i cant make it work.
in my collections users under document user have some map values, one of them is role: "guest". role values can be "guest" or "superAdmin"
i want access to /users only when role == "superAdmin"
here is what i tried
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read: if get(/databases/$(database)/documents/users/$(userId)).data.role == "superAdmin";
}
}
}
and got error when i log in as superAdmin
ERROR Error: Missing or insufficient permissions.
i believe i followed docs correctly. and found a similar question in SO where says some bug specific to evaluating nested fields in queries. But i have no nested queries. am i doing anything wrong here?
here is my firestore look
Please help.

Instead of using get(), since you're fetching the document at the location you're reading from, simply address it using resource (which is the prefetched document at that location):
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read: if resource.data.role == "superAdmin";
}
}
}
Only use get() if you're going to a different collection to fetch data.

I think the part that breaks it is allow read: if get(/databases/$(database)/documents/users/$(userId)). Here you are comparing the owner of the document, not the one requesting it. Try replacing userID with request.auth.uid.

Related

What is the purpose of the "databases" line here in this Firestore security rule?

In the documentation bout recursive wildcards: https://firebase.google.com/docs/firestore/security/rules-structure#recursive_wildcards
We have the following:
service cloud.firestore {
match /databases/{database}/documents {
// Matches any document in the cities collection as well as any document
// in a subcollection.
match /cities/{document=**} {
allow read, write: if <condition>;
}
}
}
What is the purpose of the line match /databases/{database}/documents {? I thought I could only create one database per project in Firestore. Does this mean I can create database A and database B within the same project? Do I even need this line if I only want to have 1 database?
firebaser here
Currently you can indeed only have one Firestore database per project, but that may change in the future. At that point, the {database} variable can be used to differentiate between the database instances in your project.

Read Firebase Firestore document if it document's property matches a request property

I'm sure this is a popular question but I can't find the question/answer!
Aim: Read Firestore if the currently logged-in user's uid matches a document's uid.
Current result: I get an error when trying to read.
Uncaught Error in snapshot listener: FirebaseError: Missing or insufficient permissions.
Here's my Firestore rule:
service cloud.firestore {
match /databases/{database}/documents {
match /cars/{document} {
allow read: if request.auth.uid == document.uid;
}
}
}
Here's a sample data:
If you want to refer to document data in rules, the syntax is like this:
allow read: if request.auth.uid == resource.data.uid;
This will work when the client performs a get() of the individual document, and they are signed in with a UID that matches the uid field of the document.
I suggest reviewing the documentation on authentication and data validation.

Role based access to FireStore

I am trying to implement role based access to Firebase Firestore but although I took a look at Googles well done documentation I wasn't able to get it working.
I have set a string "role" in each users document. The document itself has the uid of the user.
My rule to access the users collection looks like this and the write rule must be wrong:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{document} {
allow read: if get(/databases/$(database)/documents/users/$(request.auth.id)/role/$(role)) == "Mitarbeiter"
}
}
}
XCode-console error:
[Firebase/Firestore][I-FST000001] Listen for query at users/EStQNC4cYcaSnKpxs4XnQf96brm2 failed: Missing or insufficient permissions.
The path of your document get() is looking inside a subcollection called "role", but you have none:
/users/$(request.auth.id)/role/$(role)
Also, you have no variable called "role" that we can see here.
Using what you've shown, it looks like the role you need is in the document itself, so there's no need for a get(). Just check it like this:
allow read: if resource.data.role == "Mitarbeiter";

Set createdBy field in document with current userId (auth.uid)

I know that Firebase has the FieldValue class, which can be used to generate e.g. a server-side timestamp when writing a document (link).
What's the preferred practice for inserting the current user's uid into a document?
Having the client provide that field seems to allow misuse - unless I provide a server rule that checks for (new/updated) documents to match the request.auth.uid, something like:
service cloud.firestore {
match /databases/{database}/documents {
match /broadcasts/{broadcast}/chatMessagesCollection/{message} {
allow write: if request.resource.data.uid == request.auth.uid;
allow read: if true;
}
}
}
I can't find anything on the web for the use-case of having a document be populated with the user writing it -- so what's the best take on this?
What you're doing now with security rules to enforce that the provided UID matches the current user is exactly the right thing to do. There is really nothing better (for this specific use case), and this is a common practice.
I've even written about it in this blog series: https://medium.com/firebase-developers/patterns-for-security-with-firebase-per-user-permissions-for-cloud-firestore-be67ee8edc4a

Firestore - disallow reading multiple documents at once

I've got a Firestore collection.
The IDs of the documents are secrets. You should be able to read only the document whose ID you know.
For the sake of simplicity. I'd like to stick to this approach.
However, by default, one can read an entire collection from Firestore, for example
await firestore.collection("secret_documents").get()
Is it possible to allow reading only one document at once, only when it's referred by its ID?
Yes, that is actually quite easy. To control what documents can be accessed, use Firebase security rules for Firestore.
By default your security rules will be read and write, but those can actually be broken down into more granular operations of get, list, create and update. And what you're trying to do is to allow get, but not a list operation. From the documentation:
service cloud.firestore {
match /databases/{database}/documents {
// A read rule can be divided into get and list rules
match /cities/{city} {
// Applies to single document read requests
allow get: if <condition>;
// Applies to queries and collection read requests
allow list: if <condition>;
}
...
So to allow get for everyone and disallow list calls:
allow get: if true;
allow list: if false;
You'll probably want to elaborate on the allow get rule a bit, because it's more common to restrict it, for example to users that are signed in to your project with Firebase Authentication:
allow get: if request.auth.uid != null;
https://firebase.google.com/docs/firestore/security/rules-query

Resources