Debugging security rules for Cloud Firestore - firebase

I am setting up security rules for my Google Cloud Firestore database. I am trying to allow deletion of a document only if its timestamp value is more than 30 days in the past using the following logic:
allow delete: if
resource.data.locked == false
&& (request.time - resource.data.timeStamp).seconds > 2592000;
When I try this I get Error: Missing or insufficient permissions. So, first question - am I going about this wrong or is my logic flawed?
And as a followup question, is there a way to debug rules? Perhaps a console.log equivalent whereby I can see the result of conditional rules as they are applied and check that I'm not submitting a string in place of a timestamp or anything daft like that?
I'm assuming that request.time is 'now' and that my resource.data.timeStamp is correct and that one minus the other returns a Duration and that thatDuration.seconds is returning a number but I'm a newb to this and any one of those assumptions could be wrong and it would be great to be able to see these values as they are processed.
Cheers all

I am not checked, please try something like this
request.time < resource.data.timeStamp + duration.value(30, "d");
And your second question, i don't know. There is no simulator like in Realtime database. Remember its still in beta.

There's now a local emulator to help debug Firestore rules. It won't allow you to step through the rules like a "real" debugger, but it'll at least give you more flexibility to test and verify which rules are broken.

Related

Why is this Cloud Firestore security rule suddenly failing?

For some time I was using the following rule for Cloud Firestore in my Flutter project:
"You can see chats if you're logged in, a member and the chat was not flagged".
match /chats/{chatId} {
allow read: if signedIn() && request.auth.uid in resource.data.members && resource.data.flagged == false;
}
It used to work and to me, it seems correct. But recently, it started to fail. When I use only
allow read: if signedIn();
It works just fine. Any idea what might be the problem? In the Firebase emulator, I can see it also fails, but there is no explanation. Obviously, members and flagged fields exist (when a chat document is available).
Could this have happened after updating a particular package, like cloud_firestore?
Any ideas?
I found out, finally, what was wrong. According to this page, "the result set should satisfy the rule's condition". However, I thought my query was exactly the same as my rule and I didn't know what was wrong.
It turns out, this is taken quite literally.
In my query I had:
where('flagged', isNotEqualTo: true) and my rule was flagged == false. I needed to change that to flagged != true. This is very confusing. It seems like a bug to me, and otherwise, I would be very curious to know the reason behind this.

Firestore rules: Allow list operation only if filtered by ID

Me and my team are wondering how to correctly write security rule in firestore,
which allows list operation only if filtered by id like so:
First one should succeed
db.collection('questions')
.where(firebase.firestore.FieldPath.documentId(), 'in', allowedIds)
.get()
Second one should fail
db.collection('questions')
.get()
Currently we are doing this with the following rule:
match /questions/{question_id} {
allow list: if question_id != null
}
And for now it works
However we get the following WARNING in the firebase console:
WARNING The sub-expressions are not comparable, so this comparison will always return true.
Which is weird, because for now it works (in the emulators) - which means in this case it returns false
So here are a few questions:
Is this the correct way to achieve our goal?
How long will this work (will it stop working in future versions)?
If this is wrong - what is the correct way to do it?
Is this discrepancy between emulators and production environment?
Thank you in advance!

Unable to base security rule condition on resource data in Firebase

I am attempting very simple thing and that is matching request.auth.uid to a field value in my transaction documents (like this resource.data.useruid) in Firebase security rule in order to get transactions of a particular logged in user. However, I don't get any documents while querying for them and get an error instead.
This is how the collection looks like - just one document there with useruid field.
The field's value is mapped to the users uid (screenshot taken in the Authentication -> Users tab.
And the rule looks like this
I should get the one document back but every time I query the documents with that user logged in (I am using angularfire2 for those purposes) I get Error: Missing or insufficient permissions.
If I modify the rule condition to return always true or if I only check for truthiness of request.auth.uid I get the query result alright. The funny thing though is that with resource.data involved - eg. checking for value of the amount field in the firebase rule - the condition is never met. I tried to write it like
allow read, write: if resource.data.amount == 3
and got the error again. Seems like I don't get the resource.data Map at all.
I feel like I am missing something obvious, although after reading the guides, it seems alright to me and I am already out of ideas. The debugging capabilities (or lack of) make the whole process very slow.
Could you please explain to me, why I don't get the resource.data Map in the firebase security rule or point me to a place where the problem might be?
You have most probably missed one specific point in the doc: your query fails "because it does not include the same constraints as your security rules". See https://firebase.google.com/docs/firestore/security/rules-query#secure_and_query_documents_based_on_authuid
The following, with your security rules works perfectly:
firebase.auth().signInWithEmailAndPassword("xxxx#xxxx.com", "xxxxx")
.then(function (info) {
db.collection("transactions").where("userid", "==", info.uid).get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
console.log(doc.id, " => ", doc.data());
});
});
});
If you remove the where clause, you get the exact error you are getting

Firestore where query does not work [duplicate]

I want to create a FireStore rule that grantes read privilages to documents after the current date has surpassed a timestamp value in the document.
This is for a blog web application.
E.G a blogger sets a blog post to be available to the public on a certain date.
From reading the documentation this should work, but It dosn't.
service cloud.firestore {
match /databases/{database}/documents {
match /Articles/{article}{
allow read: if request.time.date() < resource.data.date
}
}
}
What I am i missing ??
firebaser here
I tried the same thing a while ago, and found out it isn't currently possible.
It is possible to allow/deny read to a specific document based on a property of that document.
It is possible to allow a query that filters documents based on a property in that document, but currently that is only possible based on request.auth.
This means that unfortunately your filter currently can't be implemented with security rules. I recommend you file a feature request to chime in.
Update (2018-04-24): this might now be possible with request.time, but I haven't had a chance to test yet. Have a look here.
NOTE: As this is my first answer on Stack Overflow, I wasn't allowed to comment on Frank van Pueffelen's answer, so just as a heads-up, the credits for this solution are his!
The request has a request.time which is a timestamp, and Firestore allows for basic math operators on timestamp <> timestamp operations, so your request.time < resource.data.date will work ;)
service cloud.firestore {
match /databases/{database}/documents {
match /Articles/{article}{
allow read: if request.time < resource.data.date
}
}
}
This is based on my personal testing on 2018.09.29
trying switching the < to >.
request.time will be the time of accessing the document while resource.data.date should be the creation timestamp of the document.
try using this for your security rules:
allow read: if request.time > (resource.data.timestampPropertyName + duration.time(1, 0, 0, 0));
duration.time(4, 3, 2, 1) will create a four hour, three minute, two second, one nanosecond duration.
More information can be found at:
https://firebase.google.com/docs/firestore/reference/security/#timestamp
Do remember to wait for sometime after saving your security rules for it to take effect!
Answer of #user776914 is nice but what if there are diff timezones
Lets add +- 27 hours to be sure it was e.g. created in that day +- 1
duration.abs(request.time - request.resource.data.created) < duration.value(27, 'h')
What's max timezone offset
I wanted to do a similar thing for a game. I wanted to activate and deactivate a game object only after a particular date. I used google's cloud functions to do it. I deployed a function that runs every day to check the firestore documents and changes values according to the script.

Data fails to load due to permissions but only initially

What's happening is that usually the first time I authenticate a user after enough time has passed that the session has expired my requests for data that the user should have access to are denied. The requests will start working eventually with no actual rules or code changing. It can take from 2 to 10 minutes but will eventually right itself.
I don't have hard data on this; it's just something I've observed.
I have no idea what I can do about this. Is anyone else seeing this? Is this a known bug? I've search but haven't found any other accounts of this happening.
Thanks.
here are the rules:
{
"rules": {
"users": {
"$user": {
".read" :"$user == auth.id || data.child('email').val() == auth.email",
".write":"$user == auth.id"
}
},
"todos":{
"$list":{
".read":"data.child('members').hasChild(auth.id)",
".write":"newData.child('members').hasChild(auth.id)"
}
}
}
}
By default, your auth tokens live for 24 hours, so if you're seeing security errors before then, it's probably not related to your session. To verify this, you can attach a callback to tell you when your session has expired. See:
https://www.firebase.com/docs/javascript/firebase/auth.html
I suspect what's going on here is simply that the data your rules are depending on is changing and causing writes to succeed some times, and fail others. This is the intended behavior of Security Rules in Firebase. For example, if someone was added / removed from /todos/blah/members/ it would affect your ability to read at /todos/blah
One odd thing about your rules is that you appear to be allowing anyone to write to /todos/$list as long as the data they are writing contains a member list that contains the writer's user id. I suspect this is not your intended behavior. Perhaps you meant to make the write rules depend on the data that was already in Firebase rather than new data?

Resources