Search code examples
firebasegoogle-cloud-firestorefirebase-security

Firestore Security Rules - how to prevent modification of a certain field


Let's assume we have a Firestore collection called todos, where each todo will look something like this:

{
    name: "Buy milk",
    completed: false,
    user: "eYtGDHdfgSERewfqwEFfweE" // some user's uid
}

Now, I want to prevent modification of the user field during updates (in other words - make this field read-only).

Believe me, I've done my research. My update rule looks like this:

allow update: if request.auth.uid == resource.data.user
              //&& !request.writeFields.hasAny(["user"]);
              //&& !(request.writeFields.hasAny(["user"]));
              //&& !request.resource.data.keys().hasAny(["user"]);
              //&& !('user' in request.resource.data);
              //&& ('user' in request.resource.data) == false;
              //&& !('user' in request.writeFields);

None of the above (commented out) work. They all result in an error: Error: Missing or insufficient permissions..

But...

It gets even more interesting! Because if we compare some of the above rules for positive outcome (aka true) they will work!

For example, this one works perfectly (assuming we include user field in our request):

allow update: if request.resource.data.keys().hasAny(["user"]) == true;

BUT this one doesn't work (assuming we do NOT include the user field in the request):

allow update: if request.resource.data.keys().hasAny(["user"]) == false;

Can anyone explain me what is going on here? Is it possible this is actually a bug in Firestore?


Solution

  • At "Writing conditions for Cloud Firestore Security Rules" section "Data validation" example #2

    service cloud.firestore {
      match /databases/{database}/documents {
        // Make sure all cities have a positive population and
        // the name is not changed
        match /cities/{city} {
          allow update: if request.resource.data.population > 0
                        && request.resource.data.name == resource.data.name;
        }
      }
    }
    

    So request.resource.data.user == resource.data.user should work for you? CMIIW

    Ref: https://firebase.google.com/docs/firestore/security/rules-conditions#data_validation