1

How do I use belows generic function for nested fields? I have this document below which lives at /userdata/{uid}:

enter image description here

As can be seen userBirthTime is a map, it contains another map called birthTime which in turn has the fields I want to veryify for their types e.g. check that the incoming year is of type int before it's allowed to be stored in firestore.

How would I do this? Is it even possible for nested fields? I tried with

function isInteger(fieldName) { return request.resource.data[fieldName] is int }

and then used it like this

isInteger('userBirthTime.birthTime.year')

with the overall security rule being this

match /userdata/{uid} {
  allow write: if isInteger('userBirthTime.birthTime.year')
}

but that's just always returning true even if I try with a string as fieldName rather than the required int type?!

What am I doing wrong? Is it the fact that's a nested field and not a top-level one?

2 Answers 2

2

Another solution for better generalization of objects is one which uses the .get() method.

match /userdata/{uid} {
  allow write: if isInteger(['userBirthTime', 'birthTime', 'year']);
}

function isInteger(keys) {
  let value = request.resource.data.get(keys, null);
  return value != null && value is int;
}

In case .get() is unable to find a value at the provided key path array, the function will return a default value of null.

Sign up to request clarification or add additional context in comments.

Comments

1

Apparently the dot notation doesn't work in security rules (although it returns false always):

enter image description here

// the request body
"data": {
  "userBirthTime": {
    "birthTime": {
      "year": "string"
    }
  }
}

Changing the rules to this works for me:

function isInteger(fieldName) { 
  return request.resource.data.userBirthTime.birthTime[fieldName] is int 
}

match /userdata/{uid} {
  allow write:  if isInteger('year'); // other fields
}

4 Comments

I see... hm... ok, the major downside of this of course is then I can't have a single generic function anymore that works for other fields in other maps as well but I literally have to hardcode the 'path' down to every nested field 🤔 For example, I can't use isInteger() to check the number of dogs as well and so on: isInteger('userPets.numberDogs')
@Tom yeah but on the other side maybe you can write a function to check all children of birthTime and so on for pets maybe.
I updated the screenshot above to show more maps inside the root of this document. To clarify the subject at hand: my goal is to have one generic function that checks for types such as int or string that works for various fields inside this document that live on various depths and locations inside this document. For example: isInteger('userBirthTime.birthTime.year') but also isInteger('userBirthTime.eventDetail.startValue'). That's the tricky thing, how do I do that with a single generic function? 🤓
@Tom I mean you would have to pass the value yourself then like: isInteger(request.resource.data.userBirthtime.birthTime.year) now the isInteger can be used anywhere or something similar. You can pass multiple arguments in function to pass the request object and do some processing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.