1
votes

Is it possible to set the security rules so that only some field could be updated based on the user role?

Consider a user attempts to edit the following document to edit:

/tasks/task1
  {
   "id":"task1",
   "description":"anyone can edit this",
   "sensitive_info":"only editor can edit this",
   "very_sensitive_info":"only admin can edit this",
  }
}

And here is the users collection with roles

/users/user1 { "role":"admin" }

/users/user2 { "role":"editor" }

/users/user3 { "role":"anyone" }

 match /tasks/{userId} {
        allow read: if true;
        allow create: if true;
        allow update: if <CONDITION HERE>; // <-WHAT GOES HERE?
}

How to allow the field "sensitive_info" to only be editable by user2 and user1 but not user3?

1

1 Answers

1
votes

Write rules either allow access to an entire document, or they deny it. So at first glance that may seem like you can't restrict what a user can modify.

But you can actually control what a user can write by comparing the document before and after the write operation. For example, I often have a check like this in my rules:

allow write: if request.resource.data.creator == resource.data.creator

This allows the write operation if the creator field is unmodified.

In fact, this is so common that I have a helper function for it:

function isUnmodified(request, resource, key) {
  return request.resource.data[key] == resource.data[key]
}

allow write: isUnmodified(request, resource, 'creator');

Note that a rule will fail once you try to access a non-existing field, so using isUnmodified often goes hand in hand with:

function isNotExisting(request, resource, key) {
  return !(key in request.resource.data) && (!exists(resource) || !(key in resource.data));
}

allow write: isNotExisting(request, resource, 'creator') || isUnmodified(request, resource, 'creator');

If you define the helper functions in the right scope, you can do without having to pass request and resource and shorten the whole thing to:

function isUnmodified(key) {
  return request.resource.data[key] == resource.data[key]
}

function isNotExisting(key) {
  return !(key in request.resource.data) && (!exists(resource) || !(key in resource.data));
}

allow write: isNotExisting('creator') || isUnmodified('creator');