简体   繁体   中英

Array comparison in Firestore security rules

A user needs to be able to add their uid to an array called 'users', within a document. They should only be able to add/remove their uid, and nothing else.

I need to write a security rule to allow for this, but can't figure out how to do array comparison in the security rules.

It needs to be something like this:

allow update: if request.resource.data.users == [...resource.data.users, request.auth.uid]

This is the query:

.update({
      users: firebase.firestore.FieldValue.arrayUnion(params.uid),
    })

Is this possible?

Yes, it's possible,:

allow update: if request.auth != null 
&& request.auth.uid in request.resource.data.users
&& request.resource.data.users.size() - resource.data.users.size() == 1

request.resource.data.users is the state of the users document AFTER the proposed change and resource.data.users is the state of the document BEFORE the proposed change. By checking if the difference is one, you guarantee that only one item was added to the array.

Yes. It is possible to check if a particular uid is present in an array field of the document in the security rules. You can use in condition to check the same.

So it would look a lot like this.

allow update: if (request.auth.uid in request.resource.data.users);

Hope this helps.

There's a removeAll method on arrays for array exclusion. See this answer

Here's a little function checking that two arrays differ exactly by the provided element.

function arrDifferBy(arr1, arr2, el) {
            let diff12 = arr1.removeAll(arr2);
            let diff21 = arr2.removeAll(arr1);
            return diff12.length == 1 && diff21.length == 0 && diff12[0] == el || 
                diff12.length == 0 && diff21.length == 1 && diff21[0] == el
}

In your case it would be used like

allow update: if arrDifferBy(request.resource.data.users, resource.data.users, request.auth.uid)

Edit: after a few hours of battling with a similar task myself I've decided to do it in a cloud function. If this depends on a creation of some other document, or you can use callable cloud functions, it's much less hassle.

I write this, I think its safe for arrayUnion , its for adding auth.uid but you can transform code for your targets

function isOwnerUnionedInArray(arrayName) {
  let isCreating = resource == null;
  let resourceArray = resource.data.get(arrayName, []);
  let requestArray = request.resource.data[arrayName];
  let arrayDiff = requestArray.removeAll(resourceArray);

  return ((
      isCreating &&
      requestArray.size() == 1
    ) || (
      requestArray.size() == resourceArray.size() &&
      arrayDiff.size() == 0
    ) || (
      requestArray.size() == resourceArray.size() + 1 &&
      arrayDiff[0] == request.auth.uid
    )
  ) && request.auth.uid in requestArray;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM