Firestore security rules: check for not exists(/collection/document) - firebase

In firebase firestore security rules, I want to allow a request only if a particular document does not exist. My code is:
match /users/{user_id} {
allow create: if !exists(/databases/$(database)/merchants/$(request.auth.uid));
}
I am pretty sure the document does not exist but it does not work.
Both exists() and !exists() give false somehow, or maybe !exists() raises some error.
I have even tried:
match /users/{user_id} {
allow create: if exists(/databases/$(database)/merchants/$(request.auth.uid)) == false;
}
Is there any way to make this work?

Are your rules inside the database block? Cause I had a similar problem and was actually putting the rule bellow the database block, and my rule was using the $(database)parameter just like yours
service cloud.firestore {
match /databases/{database}/documents {
match /users/{user_id} {
allow create: if !exists(/databases/$(database)/merchants/$(request.auth.uid));
}
}
}

Related

Creating security rule depend on doc data

I just added new value to my users collection appVersion
My user collection like:
documents/users/5kwgNgGi3sY6oCbUAg9v
so i just added value versionCode to collection. if user download new app it will be update
My rule now:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth !=null
}
}
}
what i need as a if else statement
if(request.auth!=null){
firebase().collection('users').where('UID','==',request.auth.uid).get().then(x=>{
if(x.docs[0].versionCode==4){
allow
}
else{
deny
}})
else{
deny
}
There is no way to perform a query in your security rules, as that would never scale on the backend.
Instead, what you'll want to do is store the user documents with the UID as the document ID, so that the document you show is at path documents/users/xHzKoXbrZqbHZY8DI3wkXnMShF92. Then you can use get() in your rules to load the document, as shown in the documentation on accesing other documents in your rules.
I'd also recommend checking out the documentation on attribute-based and Role-based access, as it covers a pretty similar scenario.

Firebase Security Rules to allow read

I am learning about Firebase Database and am trying to set up security rules. To start, I am creating very permissive rules with something like this:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/users/{user_id} {
allow read, write: if true;
}
}
I found the Rules Playground tool in the Firebase Console and attempt to get a document:
But when I click Run, I get "Simulated read denied".
What am I missing? Why doesn't my rule allow the read? What do I need to change to allow reads and writes?
Your rules are just incorrect. It look like you edited down the path too far. Your outer match should always be "/databases/{database}/documents". The whole thing should look like this if you want to allow full access to the users collection:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{user_id} {
allow read, write: if true;
}
}
}

-Firebase- Don't understand the rules

After watching movie and read i really don't understand what i want to do.
I'm explain, i have a website, he read information in my database realtime and printed on my website ...
And i got a system for vote, visitor can coming and click on button for send a vote and i sended directly on my server database.
My problem is actually write/read is true and all word but if i pass anyone on false one of two don't work.
But what need to do for nobody can modifi or delete my data ??
The rules is clear i can for who i want (admin,guest,anonymous..) what i want. But is not my problem, i don't understand what i want to do :/
Thanks a lot !
From what you have explained in your comment I think you want to make the firebase rules read-only?
If this is true the following code will accomplish that:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if true;
allow write: if false;
}
}
}
Otherwise, you could allow writing tp database only if a user is authenticated (signed in):
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if true;
allow write: if request.auth.uid != null;
}
}
}
more information on firestore rules can be found here: https://firebase.google.com/docs/firestore/security/overview?authuser=0

How to debug firestore.rules variables and functions?

I am having difficulty trying to diagnose a particular rule in my firestore.rules file. See that question here for context.
Is there a way to debug the firestore.rules file and/or functions? I'm using unit testing and the emulators to test my rules, but I would really love to see exactly what values are being evaluated by the rules engine.
For instance, here is my firestore.rules file:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /organizations/{orgId} {
allow read: if isAdmin();
allow create, update: if isAdmin();
match /classes/{classId} {
allow read: if request.auth.uid != null;
allow create, update: if isAdmin();
match /students/{studentId} {
allow read: if isAdmin() || belongsToCurrentClass();
allow create, update: if isAdmin();
}
}
}
}
}
function isAdmin() {
// removed for security
}
function belongsToCurrentClass() {
// retuns true if the authenticated user is the teacher of the requested class
return get(/databases/$(database)/documents/organizations/$(orgId)/classes/$(classId)).data.teacherUid == request.auth.uid;
}
What I'd love to do is set breakpoints or step through the code. When attempting CRUD operations on a organizations/{orgId}/classes/{classId}/students/{studentId} path I'd love to inspect exactly what values the orgId, classId, and studentId variables are holding, as well as the resource and request parameters. I'd love to inspect exactly which document (if any) is returned by the get request in belongsToCurrentClass and what the return value is.
Does anyone know of any way to do this? I think I'd answer my question referred to above in 10 seconds if I could just see the data being evaluated.
There is a local emulator for Cloud Firestore security rules. This is your best (and really only) tool for digging into security rule execution. There is no step-through debugging, but you can see a lot of debug output in the console.
https://firebase.google.com/docs/rules/emulator-setup
We can add the built-in debug function to rules. As noted in a comment, you'll see an unhelpful message like this in the browser:
Received: [path] Expected: [bool]. for 'list' # L6
On the plus side, we won't forget to remove debug messages. Tail the log file to see the output: tail -f firestore-debug.log
For example, to see which paths are being called:
match /databases/{database}/documents {
match /{document=**} {
allow create, read, update, delete: if debug(request.path);
}
}

Firebase Firestore Security Rules Variable not used

Writing rules for Firestore it seems that custom variables are not working.
Did anyone know why or have seen similar behaviour?
Using the below I got access denied although the uid is in the array of admin.
service cloud.firestore {
match /databases/{database}/documents {
match /conferences/{confid} {
allow read,write: if request.auth.uid in get(/databases/$(database)/documents/conferences/$(confid)).data.admin;
}
}
}
Simulator is giving the below error:
Function [get] called with path to nonexistent resource: /databases/%28default%29/documents/conferences/%7Bconfid%7D
Also testing this on a real devices I got access denied.
If however I use the ID of the document like below it works and access is granted.
service cloud.firestore {
match /databases/{database}/documents {
match /conferences/{confid} {
allow read,write: if request.auth.uid in get(/databases/$(database)/documents/conferences/ySWLb8NSTj9sur6n2CbS).data.admin;
}
}
}
Obviously I can't hardcode this for each and every ID.
UPDATE
Apart from logging the case with support I have done some further testing.
On the below the simulator is now granting access.
service cloud.firestore {
match /databases/{database}/documents {
match /conferences/{confID}{
allow read, write: if request.auth.uid in get(/databases/$(database)/documents/conferences/$(confID)/permissions/permission).data.users;
}
}
}
For reference I use the below to query from my web-application:
db.collection("conferences")
.get()
.then(query => {
console.log("SUCCESS!!!")
query.forEach(function(doc) {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
});
}).catch((e) => {
console.log(e)
})
This is the log from the browser:
FirebaseError: Missing or insufficient permissions.
at new FirestoreError (webpack-internal:///./node_modules/#firebase/firestore/dist/index.cjs.js:352:28)
at JsonProtoSerializer.fromRpcStatus (webpack-internal:///./node_modules/#firebase/firestore/dist/index.cjs.js:5649:16)
at JsonProtoSerializer.fromWatchChange (webpack-internal:///./node_modules/#firebase/firestore/dist/index.cjs.js:6146:44)
at PersistentListenStream.onMessage (webpack-internal:///./node_modules/#firebase/firestore/dist/index.cjs.js:14350:43)
at eval (webpack-internal:///./node_modules/#firebase/firestore/dist/index.cjs.js:14279:30)
at eval (webpack-internal:///./node_modules/#firebase/firestore/dist/index.cjs.js:14319:28)
at eval (webpack-internal:///./node_modules/#firebase/firestore/dist/index.cjs.js:7411:20)
I am using the latest Firebase package 5.8.3.
If I change the above rule to something simple like below it got access as long as I am logged in with a user:
service cloud.firestore {
match /databases/{database}/documents {
match /conferences/{confID}{
allow read, write: if request.auth.uid != null
}
}
}
This even confuses me more. Is this because the rule is more complex and it takes too long to get this verified and gives back access denied?
Update-2
Quickly tested this in a mobile app via Flutter. Same result. Access denied with this ruleset.
service cloud.firestore {
match /databases/{database}/documents {
match /conferences/{confID}{
allow read, write: if request.auth.uid in get(/databases/$(database)/documents/conferences/$(confID)/permissions/permission).data.users;
}
}
}
I think my problem was the query's don't match security rules. If you would only access a single specific document it would work but if you query multiple documents in a collection you got blocked by the security rules.
I had two options. Restructure my data so that a single document will hold all the data I need or redesign security rules to match query's.
In the end I have attached to each document an indentifier like the UID to make sure query's match the security rules.
One solution would be to put the users with permissions into an array in the conference document instead,
so request.resource.data.permissions
So, instead of this:
get(/databases/$(database)/documents/conferences/$(confID)/permissions/permission).data.users
use this:
request.resource.data.permissions
This wouldn't solve the get() problem, but it would eliminate the need for a get() call, which could save you 15% or more on your quota.

Resources