0
votes

I'm facing a permission denied error when querying firestore, when I have introduced a rule. I have narrowed down my complex rule and filter to the below 2 examples of which one query works, and one doesn't. I do not understand what is wrong with my failing query.

From https://cloud.google.com/firestore/docs/security/rules-query I understand that a rule is not a filter. According to this document: "If a query could potentially return documents that the client does not have permission to read, the entire request fails.".

Baring that in mind, I have been iterating over my rule, filter and data, and come with the below:

The data: I have NO data in my collection called "MyCollection". As a matter of fact, the collection "MyCollection" has never existed.

The rule:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    match /MyCollection/{id} {
      allow read: if (  
                       (resource.data.readAccess == 0)
                     )

      allow write: if (true)

    }
  }
}

My failing query (where I have the permission denied error):

Firestore.instance.collection('MyCollection')
      .where("readAccess", isLessThanOrEqualTo: 0)
      .getDocuments()
      .then((_) => print("Success!"));

When I run this query, I get the following error:

W/Firestore(12491): (21.3.0) [Firestore]: Listen for Query(MyCollection where readAccess <= 0) failed: Status{code=PERMISSION_DENIED, description=Missing or insufficient permissions., cause=null}

My successfull query: (The only difference in this query is that I replaced "isLessThanOrEqualTo" with isEqualTo)

Firestore.instance.collection('MyCollection')
      .where("readAccess", isEqualTo: 0)
      .getDocuments()
      .then((_) => print("Success!"));

Comments:

  • I have the same results when I do populate MyCollection with data.
  • It looks like the query is validated against the rule, not the "potential return documents" as the document https://cloud.google.com/firestore/docs/security/rules-query states. If this is the case I wonder how I will be able to translate the following rule into a filter:

                   (resource.data.readAccess == 0) || 
                   ((request.auth != null) && 
                    (resource.data.readAccess <= get(/databases/$(database)/documents/App/$(resource.data.appId)).data.group[request.auth.uid])
                   )
    
  • This rule is fairly similar, except that it validates the readAccess level of a document against the group access level in the "App" document for that data's app, for the logged on user. If I can't match the query for a simple rule, I can't imagine what I need to do for this complex rule.

Please advise. Many thanks.

1

1 Answers

2
votes

With security rules, the query must exactly match the rules. The behavior you're observe is exactly what I would expect.

With a rule like this:

allow read: if resource.data.readAccess == 0;

That means the query must be filtered exactly like this;

where("readAccess", isEqualTo: 0)

Nothing else will satisfy this rule. It's absolutely demands that the query filter for exactly the value of 0 on the readAccess field. It's not clear to me why you're expecting a different outcome.

Your query suggests that the client provide its own "access" to the collection. Note that this is not secure. You can't depend on client apps self-reporting their own level of access in a database query. Something else on the backend needs to determine if the app is allowed to make the query.

Typically, Firebase Authentication is used to determine who the user is, then allow access based on what that user is allowed to do. You could store the user's permissions somewhere in another document, and use the contents of that document to determine what they can do. Or perhaps use custom claims. But you can't trust the the user pass their own permissions.