Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

security rules to allow update of specific fields

I am new to security rules. I have to write security rule to prevent a user to update a document except one field.

lets say i have a doc

{ field1 : one, field2 : two, field3 : three, . . . fieldn : n }

the user logged in should be able to update only field2. using firestore security rules.

like image 702
Sonu Sharma Avatar asked Sep 28 '19 04:09

Sonu Sharma


People also ask

How do you make a field unique in firebase?

If you need some value (or combination of values) to be unique, you need to create a node that contains that value (or combination) as its key. If you need to guarantee that multiple values (or combinations) are unique, you'll need multiple of such nodes.

How do you update firestore rules?

Edit and update your rulesOpen the Firebase console and select your project. Then, select Realtime Database, Cloud Firestore or Storage from the product navigation, then click Rules to navigate to the Rules editor. Edit your rules directly in the editor.

What is a document snapshot?

A DocumentSnapshot is an immutable representation for a document in a Firestore database. The data can be extracted with [data()] or [get(fieldPath)] to get a specific field. For a DocumentSnapshot that points to a non-existing document, any data access will return 'undefined'.


2 Answers

There is no explicit way in security rules to validate the update that is happening. But what you can do is validate the data in the document before and after the write operation. By comparing those two, and by knowing what fields the document can contain, you can ensure that only specific fields can be updated.

I often use this little helper function in my security rules:

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

As its name implies, it ensures that a certain key/field is not modified in this write request. For example, this rule then only allows a user to update their profile document, as long as they don't modify the name field (unless they're an admin):

allow update: if isAdmin(request) || 
  (request.auth.uid == uid && isUnmodified(request, resource, 'name'));

I also have this helper function, which checks whether a specific field exists:

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

This is important, because sometimes you want to allow a field to be only written once, or only allow it to be updated if it already exists. Sometimes I use isNotExisting for that, but I find myself more these days using the more granular actions (create, update) over the aggregate write rule.

Finally, you can require certain fields, as in this creation rule:

  allow create: if request.auth.uid == uid &&
    request.resource.data.keys().hasOnly(['lastIndex', 'lastUpdated']) &&
    request.resource.data.keys().hasAll(['lastIndex', 'lastUpdated']) 

So a user can only create a profile document if they specify lastIndex and lastUpdated fields. If they specify any additional fields, or specify fewer fields, the creation will be rejected.

Now with this knowledge, we can go back to your requirement, and see how to implement it. As said before, you will need to make a statement on each individual field, without having a wildcard in there. So if your document has three fields (field1, field2, and field3), which must all exist, and the user can only update field2, that'd be something like:

allow update: if request.resource.data.keys().hasAll(['field1', 'field2', 'field2']) &&
  isUnmodified('field1')) && isUnmodified('field3'));

There may nowadays be a shorter way to do this by using the set and map diff operations that are shown here: https://firebase.google.com/support/release-notes/security-rules#february_13_2020 like:

// This rule only allows updates where "a" is the only field affected
allow update: if request.resource.data.diff(resource.data).affectedKeys().hasOnly(["a"]);
like image 59
Frank van Puffelen Avatar answered Oct 21 '22 09:10

Frank van Puffelen


If you prefer the method of specifying which fields can be editable, I found a section in this tutorial to be helpful: Intermediate topics in Firebase Security Rules - Firecasts

// Make sure the request has only the specified allowed fields    
function editingOnlyAllowedFields(allowedFields) {
    let editedKeys = request.resource.data.diff(resource.data).affectedKeys();
    return editedKeys.hasOnly(allowedFields);
}
// Then, you can specify which fields you want to be updatable
allow update: editingOnlyAllowedFields(["field1", "field2"]);
like image 2
ObjectJosh Avatar answered Oct 21 '22 10:10

ObjectJosh