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;
}
}
}
Related
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.
My database structure is:
users
david#gmail.com
records
ApK2DFpG87NDGYutgAVO
pulse: 80
Bryd87NAS20dfDGYtghg
pulse: 78
eva#fb.com
records
A81hxASDKH38dhaj9321
pulse: 93
A82ndasklih38ASD2eda
pulse: 67
and rules are:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{email} {
allow create, read, update, delete: if request.auth.token.email == email;
}
}
}
I would like every user (e.g. foo#gmail.com) to be able to read and write data only under that user (users/foo#gmail.com/**).
When I read users/me#gmail.com in the Rules playground (while being authenticated as me#gmail.com), I get "Simulated read allowed", as expected.
However, when I read users/me#gmail.com/records from my app (while being authenticated as me#gmail.com), I get:
FirebaseError: Missing or insufficient permissions.
What am I missing?
By the way, why the Rules playground doesn't allow reading collections (e.g. users/me#gmail.com/records)? It says:
Path must be document-level
You should take advantage of the recursive wildcards of version 2 of the security rules, as follows:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{email}/{document=**} {
allow create, read, update, delete: if request.auth.token.email == email;
}
}
}
As explained in the doc, it will match documents in any subcollections of the users collection as well as documents in the users collection.
By the way, why the Rules playground doesn't allow reading collections
If I am not mistaking, this is because rules are not filters, and therefore you need to exactly specify the document you want to target in the "Rules Playground".
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
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.
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));
}
}
}