10

I've a form which creates the following JSON structure.

{  
   "reviewed":false,
   "title":"Just a title",
   "user":"UYV9TRKXfNW1NeCyFyfjZfagJ8B",
   "items":[  
      {  
         "age":"33",
         "experience":"Newcomer",
         "image":"https://image-url",
         "job":"Nerd",
         "name":"Testname",
         "party":"AAA",
         "type":"person"
      },
      {  
         "age":"33",
         "experience":"Newcomer",
         "image":"https://image-url",
         "job":"Informatiker",
         "name":"Testname",
         "party":"AAA",
         "type":"person"
      }
   ]
}

How do I check the values of "items" with firestore's security rules? Is there a way to loop/iterate over the array?

André Kool
  • 4,880
  • 12
  • 34
  • 44
Fabian
  • 354
  • 2
  • 14

2 Answers2

16

For the sake of completeness: That's my solution so far. I did it the way described in the linked answer. The possible amount of items is limited to 10, so we can go without dynamic loops.

service cloud.firestore {
  match /databases/{database}/documents {
    match /events/{event} {

      function isAuthed() {
        return request.auth.uid != null
            && request.auth.uid == request.resource.data.user
            && request.auth.token.email_verified == true;
      }

      function isReviewed() {
        return request.resource.data.reviewed == false
            || request.resource.data.reviewed == "false"
      }

      function isValidTitle() {
        return isValidStringInput(request.resource.data.title, 200);
      }

      function items() {
        return request.resource.data.items;
      }

      function isValidPerson(item) {
        return items()[item].keys().hasAll(['image','type','name','job','age','party','experience'])
                && isValidStringInput(items()[item].image, 100)
                && isValidStringInput(items()[item].type, 10)
                && isValidStringInput(items()[item].name, 50)
                && isValidStringInput(items()[item].job, 50)
                && isValidStringInput(items()[item].party, 50)
                && isValidStringInput(items()[item].experience, 50)
                && isValidNumber(items()[item].age);
      }

      function isValidParty(item) {
        return items()[item].keys().hasAll(['image','type','name','orientation','experience','promi'])
                && isValidStringInput(items()[item].image, 100)
                && isValidStringInput(items()[item].type, 10)
                && isValidStringInput(items()[item].name, 50)
                && isValidStringInput(items()[item].orientation, 50)
                && isValidStringInput(items()[item].experience, 50)
                && isValidStringInput(items()[item].promi, 50);
      }

      function isValidItem(item) {
        return isValidPerson(item)
            || isValidParty(item);
      }

      function isValidStringInput(input, maxSize) {
        return input is string
            && input.size() > 0
            && input.size() <= maxSize;
      }

      function isValidNumber(input) {
        return input is int
            || input.matches('^[0-9]+$');
      }

        // One can READ
            // always ...
        allow read: if true;

      // One can WRITE, when ...
        // writer is logged in
        // uid in event is same as uid of writer
        // writer has email confirmed
        // reviewed is initial set to false
        // form/user input is ok
      allow write, update:
        if isAuthed()
        && isReviewed()
        && isValidTitle()
        && items().size() >= 1
        && items().size() <= 10
        && isValidItem(0)
        && (items().size() < 2 || isValidItem(1))
        && (items().size() < 3 || isValidItem(2))
        && (items().size() < 4 || isValidItem(3))
        && (items().size() < 5 || isValidItem(4))
        && (items().size() < 6 || isValidItem(5))
        && (items().size() < 7 || isValidItem(6))
        && (items().size() < 8 || isValidItem(7))
        && (items().size() < 9 || isValidItem(8))
        && (items().size() < 10 || isValidItem(9));
    }
  }
}
Fabian
  • 354
  • 2
  • 14
  • 1
    simply brilliant chap !! – Harkal Aug 04 '19 at 00:26
  • Are you checking all the Persons in `items` ? Or just the first one with `items[0]` ? Thanks – cbdeveloper Sep 04 '20 at 17:44
  • I think you might have a logical err here: (items().size() < 2 || isValidItem(1)). If size < 2 is true, the isValidItem(1) might be false and the OR expression will result in TRUE... – Xao Aug 18 '23 at 16:50
0

As far as I know. You still can't use loops in firestore security rules and the linked answer and the example is still valid and shows how you can do validations using functions. This could become unusable if the array grows and it might be better to choose another data structure like an own collection for your items.

Cheers, Lars

Lars-B
  • 284
  • 1
  • 2
  • 11
  • Thanks for the answer. I wonder why I didn't find the linked answer by myself. Anyways... I will think about doing it this way or to save the items into a collection. – Fabian Aug 15 '18 at 14:25