Home > Software engineering >  Can not write firestore security rule for top level collection (list permission) given a specific au
Can not write firestore security rule for top level collection (list permission) given a specific au

Time:11-10

I'm just starting to use Firebase services (firestore) and I can't figure out why the following does not work.

Here is my security rules file:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents/{document=**} {
    allow read, write: if request.auth.uid == resource.data.user_id;
  }
}

My database is structured as followed:

A top-level collection called memos contains documents. I only have one, "H5i5p0zenBwkV7vsS12G" in my example. It is named after its auto-generated id. This document has 2 data fields. One "user_id" String data equal to "7llpal4k0xVxILawceAPPsJe3Vy1" and one "title" String data equal to "Hello Firestore User".

   /memos/H5i5p0zenBwkV7vsS12G with some data: {
      title: "Hello Firestore User"
      user_id: "7llpal4k0xVxILawceAPPsJe3Vy1"
    }

It works perfectly when I run a get (/databases/(default)/documents/memos/H5i5p0zenBwkV7vsS12G) operation using both the testing tool and my android app.

But every time I request a list of the memos collection using

Firebase.firestore.collection("memos")

it fails with a permission denied exception. Here is the associated logs:

2021-11-08 19:36:00.946 2996-3045/com.ygoular.memo I/Firestore: (23.0.4) [WatchStream]: (e04cf9f) Stream sending: # com.google.firestore.v1.ListenRequest@7a872c89
      add_target {
        query {
          parent: "projects/memo-da978/databases/(default)/documents"
          structured_query {
            from {
              collection_id: "memos"
            }
            order_by {
              direction: ASCENDING
              direction_value: 1
              field {
                field_path: "__name__"
              }
            }
          }
        }
        target_id: 2
      }
      database: "projects/memo-da978/databases/(default)"
2021-11-08 19:36:00.946 2996-3045/com.ygoular.memo I/Firestore: (23.0.4) [FirestoreCallCredentials]: Successfully fetched token.

2021-11-08 19:53:05.815 4440-4480/com.ygoular.memo I/Firestore: (23.0.4) [WatchStream]: (2faa0c0) Stream received: # com.google.firestore.v1.ListenResponse@c4bcaabe
    target_change {
      cause {
        code: 7
        message: "Missing or insufficient permissions."
      }
      target_change_type: REMOVE
      target_change_type_value: 2
      target_ids: 2
    }

This call is made after being authenticated. I am sure that my app is authenticated at that moment because if I modify the security rule with the following:

allow read, write: if request.auth.uid != null

it passes and returns the available memos. I am also certain that the user ids are supposed to match because I pasted the value returned by

FirebaseAuth.getInstance().currentUser.uid

to the memo "user_id" field in the database after the phone authentication has been performed. This id will never change for that provider (phone).

Here is the uid obtained from the logcat:

2021-11-08 19:53:05.633 4440-4440/com.ygoular.memo E/Test: uid: 7llpal4k0xVxILawceAPPsJe3Vy1

The only way I could make this work is by creating a top collection /users that has its document name as its user id. In that case the document contains the subcollection /memos and the rule would look something like this:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents/users/{userId}/{anythingBehind=**} {
    allow read, write: if request.auth.uid == userId;
  }
}

If you have any idea how this could be solved, please let me know! :) Thanks a lot.

CodePudding user response:

RULES ARE NOT FILTERS

I'll repeat that

RULES ARE NOT FILTERS

in fact, I'll let the documentation repeat it again:

https://firebase.google.com/docs/firestore/security/rules-query

Rules do NOT check EACH record to see if each is allowed. Rules check to see if the query AS WRITTEN might match a disallowed record - in which case the ENTIRE QUERY is disallowed. It does NOT EVEN check to see if ONE of the records might work - you MUST do a QUERY that AT LEAST specifies the user_Id field.

  • Related