Search code examples
firebasegoogle-cloud-firestorefirebase-security

Combining security rules when specific field is updatable by only certain users


I have an issue document and I want to apply the following restrictions to update it -

  1. user must be logged in
  2. user is either the creator of that issue or user is a moderator
  3. to update the status field user must be a moderator

I can implement the first 2 rules with -

allow update: if (isLoggedIn() && (isCreator() || isModerator()));

But I'm having a hard time combining proper logic for the 3rd rule with this.

On the front end, I can show/hide the status field based on whether the current user is a moderator or not. But how can I enforce this restriction on back end with security rules?

Following is my ruleset -

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
  
  function isLoggedIn(){
    return request.auth != null;
  }
  
  function isCreator(rsc){
    return request.auth.uid == rsc.data.postedById;
  }
  
  function isModerator(){
    return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.moderator == true;
  }
  
  function isStatusField(rcs){
    return request.resource.data.diff(rcs.data).affectedKeys().hasAny(['status']);
  }
  
    match /issues/{issue} {
      allow read;
      allow create: if isLoggedIn();
      allow update: if (isLoggedIn() && (isCreator() || isModerator()));
    }
  }
}

Solution

  • I recommend framing the rules slightly differently:

    1. A moderator on the document can modify any field in it.
    2. The creator of the document can modify all fields, except the status.

    This makes it clearer that you need to split on the user's role earlier on in your condition, and likely leads to something like:

    allow update: if (isLoggedIn() && (
      isModerator() || (
        isCreator() && !('status' in request.resource.data.diff(resource.data).affectedKeys())
      )
    ));