8
votes

I have just started to get my head around Firestore rules and my head is expanding rapidly.

I'm trying to work out how to apply a rule to one collection and another rule to all other collections and their sub-collections.

So I start with the default rule that seems to come with Firestore:

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

    match /{document=**} {
      allow read, write;
    }
  }
}

which would allow read write access to all collections and their documents.

But suppose I want to apply a rule to the documents in one collection and retain the default rule for all other collections. The following will not work:

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

      match /suppliers/{supplier} {
        allow create: if !exists(/databases/$(database)/documents/supplierABNs/1260)
}

  match /{document=**} {
    allow read, write;
}


 }
}

because the second rule will override the first.

Is there a way to do what I am trying to do?

3

3 Answers

3
votes

I understand that you want to apply a rule to the documents in one collection and retain the default rule for all other collections. There's a way to do what you're trying to do and you're not going to like it.

You have to specify the default rules for all the other collections explicitly.

Here's a sample.

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

    //Rule for Suppliers collection
    match /suppliers/{supplier} {
        allow create: if !exists(/databases/$(database)/documents/supplierABNs/1260)
    }

    //Rule for Changelog collection allowing complete access
    match /Changelog/{id} {
        allow read: if true;
        allow write: if true;
    }

    //Rule for Vendors collection allowing complete access
    match /Vendors/{id} {
        allow read: if true;
        allow write: if true;
    }

  }
}

Note: Firestore rules doesn't support if else statements. But you can use AND and OR conditions as a workaround to simulate the same.

1
votes

The underlying issue here is that when you have multiple match statements, the read/write will be allowed if ANY of the match statements returns true. Reference for this can be found in the docs here...

https://firebase.google.com/docs/firestore/security/rules-structure#overlapping_match_statements

To achieve what you're wanting to do, you need to exclude the specific path from your rule that matches all documents. This can be done by adding a check to see if the first part of the path does not match your excluded path.

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

    match /suppliers/{supplier} {
      allow create: if !exists(/databases/$(database)/documents/suppliers/ABNs/1260)
    }

    // Use this instead of the original /{document=**} check
    match /{collectionName}/{document=**} {
      allow read, write: if collectionName != 'suppliers';
    }
  }
}
-2
votes
service cloud.firestore {
  match /databases/{database}/documents {

    match /suppliers/{supplier} {
      allow create: if isValidSupplier()
    }

    function isValidSupplier() {
        return resource.data.supplierABNs == '1260'
    }

    match /{document=**} {
      allow read, write;
    }
  }
}

The resource.data contains the values from your existing documents.