Firestore where query does not work [duplicate] - firebase

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.

Related

Firestore security rules - read count in a batch

If in a batch I update documents A and B and the rule for A does a getAfter(B) and the rule for B does a getAfter(A), am I charged with 2 reads for these or not? As they are part of the batch anyway.
Example rules:
match /collA/{docAid} {
allow update: if getAfter(/databases/$(database)/documents/collA/${docAid}/collB/{request.resource.data.lastdocBidupdated}).data.timestamp == request.time
&& ...
}
match /collA/{docAid}/collB/{docBid} {
allow update: if getAfter(/databases/$(database)/documents/collA/${docAid}).data.timestamp == request.time
&& getAfter(/databases/$(database)/documents/collA/${docAid}).data.lastdocBidupdated == docBid
&& ...
}
So are these 2 reads, 1 per rule, or no reads at all?
firebaser here
I had to check with our team for this. The first feedback is that it doesn't count against the maximum number of calls you can make in a single security rule evaluation run.
So the thinking is that it likely also won't count against documents read, since it doesn't actually read the document. That said: I'm asking around a bit more to see if I can get this confirmed, so hold on tight.
Are you using two different documents?
If it is the case, then two reads will be performed.

Firestore rule allow read condition timestamp problem

I am using firestore for the first time and I have a problem, I want to allow read if the message was send less than 5 minutes but it doesn't work.
service cloud.firestore {
match /databases/{database}/documents {
match /locations/{allDocuments=**} {
allow read: if request.time < Timestamp.fromMillis(resource.data.timestamp) + duration.time(0, 5, 0, 0)
allow write: if true;
}
}
}
Every data has a child call "timestamp" and the value is a number like "1554710156002"
With this read condition my app can't read anything but it can write.
Does someone know what the problem is?
Try using duration.time(hours, minutes, seconds, nanos) (documented here) to manipulate timestamps:
duration.time(0, 5, 0, 0)
Also, my personal preference is to put the duration after the (fixed) resource creation time, which makes reasoning about it a bit easier for me.
Your code would become
allow read: if request.time < resource.data.timestamp + duration.time(0, 5, 0, 0);
(NB: I did not verify this, and this is assuming your timestamp is actually a timestamp)
The controlling of read/write/update rights for users within a Firestore database is achieved via Firestore security rules that you would configure from within the Firebase console.

Firestore Rules verify timestamp with a Flutter client

I want to send the creation time of a Firestore document through the client and verify the time with Firestore Rules to avoid Cloud Functions calls (pricing).
Scenario
I am testing requests from clients against Firestore rules like this:
allow create: if request.resource.data.TIMEFIELD == request.time;
The request contains a TIMEFIELD that has a timestamp, just like request.time.
Problem
Apparently the request time and the time I am setting as a field right before sending the request are not equivalent, which makes this comparison impossible.
The following is the defition of request.time from the documentation.
When the request was received by the service.
I wonder if there is a way to set a field in a document equal to request.time.
I am unable to use server side timestamps because of an issue with Flutter.
Because of that I need to know how I could possibly validate client side timestamps like time.now with Firestore Rules.
You can use the Timestamp to add constraints to the time field (docs).
Here is an example of how to ensure that the change was within a certain amount of seconds:
function withinSeconds(secs) {
return request.resource.data.TIMEFIELD.seconds() - request.time.seconds() <= secs
&& request.resource.data.TIMEFIELD.seconds() - request.time.seconds() >= -secs
}
Edit
The above is for setting the value within a threshold of the request.time.
You can also just use the REST API in the mean time. Just make a write request that includes an update and a transform. The transform is where you would set the server timestamp. Here is a tool to help understand how to build the requests.
This has been implemented into the Flutter plugin for Cloud Firestore:
FieldValue.serverTimestamp()
Using this as a field's value will assign a timestamp equal to request.time to the field, server-side.
You can find out more about it in the API reference for cloud_firestore.
you'd first have to remember the creation (or last updated) timestamp:
firestore().collection("items").add({
....
created: firebase.firestore.FieldValue.serverTimestamp()
});
in order to let the client know of the timestamp, which you are trying to compare later on.

Debugging security rules for Cloud Firestore

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.

How to create time-expiring data with Firebase Rules?

This talk mentions time-expiring data using Firebase rules at 22:55
https://www.youtube.com/watch?v=PUBnlbjZFAI
How can one do this ?
I didn't find any information regarding this.
I recommend two solutions.
1) Use cloud functions to record a message path and the date it was posted. Then every hour sort that list by date, pick all the expired ones, and create a deep update object to null out every expired message. Nowadays you can use Cron Scheduler to handle the periodic flush.
2) Make a rule that says anyone can delete expired messages and make it so that clients automatically delete expired messages when they are in a chat room.
Written here: https://firebase.google.com/docs/database/security/securing-data
You can't have it auto delete your data but you can make them unreadable (which is the same thing from the user standpoint). Just send a timestamp child field with you data and check against it.
{
"rules": {
"messages": {
"$message": {
// only messages from the last ten minutes can be read
".read": "data.child('timestamp').val() > (now - 600000)",
// new messages must have a string content and a number timestamp
".validate": "newData.hasChildren(['content', 'timestamp']) && newData.child('content').isString() && newData.child('timestamp').isNumber()"
}
}
}
}
Same question here.
You can't do it using firebase rules. You should either have a NodeJS backend removing your old data or clients doing it for you. For example, before a client retrieves data, he could remove old data.

Resources