22

I want to give a user the right to update a document. But ONLY if the user updates one specific field of this document. All other fields shouldn't be changed by this user.

Is this possible in firestore?

I tried something like this:

function isUpdateToOpenField(attr) {
    return attr == get(/databases/$(database)/documents/stores/$(store)).data.open;
}

allow update: if isUpdateToOpenField(request.resource.data);

But I don't know how to compare if the update corresponds to the right field.

6 Answers 6

42

Map diffs in the Rules language were introduced to solve this:

function isUpdateToOpenField() {
    return request.resource.data.diff(resource.data).affectedKeys().hasOnly(['open']);
}

allow update: if isUpdateToOpenField();
3
  • you're sending attr to the function but never use it
    – AvielNiego
    Commented Jul 29, 2020 at 11:48
  • 2
    now your function doesn't receive any parameters but you send it request.resource.data :)
    – AvielNiego
    Commented Jul 31, 2020 at 9:03
  • function isUpdateSpecificFieldOnly(fieldName) { return request.resource.data.diff(resource.data).affectedKeys().hasOnly([fieldName]); }
    – MadMac
    Commented Dec 7, 2022 at 23:29
24

Update: Instead of writeFields, you can now use Map.diff()

Check out the writeFields variable for security rules:

allow update: if ((request.writeFields.size() == 1) && ('open' in request.writeFields));
2
  • how does writeFields look like with nested fields, e.g. >> update("foo.bar", baz) ? Thank you.
    – lenhuy2106
    Commented Jul 19, 2018 at 20:36
  • @lenhuy2106: This && ('foo.bar' in request.writeFields) works as expected.
    – Pascal
    Commented Sep 15, 2018 at 9:29
6

You can use :

allow update: if request.resource.data.diff(resource.data).affectedKeys().hasOnly(["fieldToBeUpdated"]);

OR

allow update: if request.resource.data.diff(resource.data).affectedKeys() == ["fieldToBeUpdated"].toSet;

You can replace affectedKeys() with addedKeys() , removedKeys() or changedKeys() depending upon your use case.

affectedKeys() as the name suggests is equivalent to using all the three together.

For more details please read the docs here https://firebase.google.com/docs/reference/rules/rules.MapDiff

5

Since writeFields is deprecated and should not be used, you will have to examine request.resource.data. However, it always contains all of the fields of the written document (it's final state). This means that you will have to compare all of the fields of the written document to the fields of the original document in resource.data in order to make sure that only the ones that changed are the ones that you allow to be changed.

Currently this requires an explicit check for every field that could possibly be written, which is not fun to implement. The Firebase team is looking into ways of making this sort of rule easier to express by allowing you to diff the data maps of the "before" and "after" documents.

2
  • 1
    Here is a helpful article that discusses this strategy. Massively painful for more complex assertions. I hope this gets addressed soon.
    – ctrlplusb
    Commented Oct 22, 2019 at 16:27
  • 2
    Hi, is there any update regarding this topic? Thanks
    – nibbana
    Commented Jan 21, 2020 at 8:47
4

Since request.resource.data will show the future object after the write - the only way is to check every field using the following:

   function notUpdating(field) {
     return !(field in request.resource.data)
      || resource.data[field] == request.resource.data[field]
   }

   allow update: if notUpdating('title') && notUpdating('description')

Make sure you validate that the user doesn't update all of the fields except the field you want him to have access to

1

the same answer of "Scott Crossen" with little change, to be more genric

function isUpdateToOpenField(attr) {
    return request.resource.data.diff(resource.data).affectedKeys().hasOnly([attr]);
}

allow update: if isUpdateToOpenField('open');

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.