How does resource.data.size() work in firestore rules (what is being counted)?

Viewed 3154

TLDR: What is request.resource.data.size() counting in the firestore rules when writing, say, some booleans and a nested Object to a document? Not sure what the docs mean by "entries in the map" (https://firebase.google.com/docs/reference/rules/rules.firestore.Resource#data, https://firebase.google.com/docs/reference/rules/rules.Map) and my assumptions appear to be wrong when testing in the rules simulator (similar problem with request.resource.data.keys().size()).


Longer version: Running into a problem in Firestore rules where not being able to update data as expected (despite similar tests working in the rules simulator). Have narrowed down the problem to point where can see that it is a rule checking for request.resource.data.size() equaling a certain number.

An example of the data being passed to the firestore update function looks like

 Object {
   "parentObj": Object {
     "nestedObj": Object {
       "key1": Timestamp {
         "nanoseconds": 998000000,
         "seconds": 1536498767,
       },
     },
   },
   "otherKey": true,
 }

where the timestamp is generated via firebase.firestore.Timestamp.now(). This appears to work fine in the rules simulator, but not for the actual data when doing

let obj = {}
obj.otherKey = true
// since want to set object key name dynamically as nestedObj value,
// see https://stackoverflow.com/a/47296152/8236733
obj.parentObj = {} // needed for adding nested dynamic keys
obj.parentObj[nestedObj] = {
    key1: fb.firestore.Timestamp.now()
}

firebase.firestore.collection('mycollection')
.doc('mydoc')
.update(obj)

Among some other rules, I use the rule request.resource.data.size() == 2 and this appears to be the rules that causes a permission denied error (since commenting out this rules get things working again). Would think that since the object is being passed with 2 (top-level) keys, then request.resource.data.size()=2, but this is apparently not the case (nor is it the number of keys total in the passed object) (similar problem with request.resource.data.keys().size()). So there's a long example to a short question. Would be very helpful if someone could clarify for me what is going wrong here.

2 Answers

From my last communications with firebase support around a month ago - there were issues with request.resource.data.size() and timestamp based security rules for queries.

I was also told that request.resource.data.size() is the size of the document AFTER a successful write. So if you're writing 2 additional keys to a document with 4 keys, that value you should be checking against is 6, not 2.

Having said all that - I am still having problems with request.resource.data.size() and any alternatives such as request.resource.size() which seems to be used in this documentation https://firebase.google.com/docs/firestore/solutions/role-based-access

I also have some places in my security rules where it seems to work. I personally don't know why that is though.

Been struggling with that for a few hours and I see now that the doc on Firebase is clear: "the request.resource variable contains the future state of the document". So with ALL the fields, not only the ones being sent. https://firebase.google.com/docs/firestore/security/rules-conditions#data_validation.

But there is actually another way to ONLY count the number of fields being sent with request.writeFields.size(). The property writeFields is a table with all the incoming fields.

Beware: writeFields is deprecated and may stop working anytime, but I have not found any replacement.

EDIT: writeFields apparently does not work in the simulator anymore...

Related