Firestore security rules: Using hasOnly on a request to check if only a specific field is updated

岁酱吖の 提交于 2020-06-26 12:07:40

问题


I'm setting up my Firestore security rules, but run against one issue wherein I want to limit updating only one specific field within a document by using the hasOnly function. The problem is that I keep getting 'denied' results using the simulator. I'm surely doing something simple wrong... I'm trying to prevent that somebody could update other fields in the document than update_requested_time, but allow them to update this particular field.

When using the example from the Firestore documentation on hasOnly() — ['a', 'b'].hasOnly(['b', 'a']) == true — the rule returns true. But when using my own it does not, by that I assume I'm getting something wrong in the part of request.resource.data.keys().

The specific rule targeting fields in a specific scenario:

match /scenarios/{scenario} {
      allow read: if true;
      allow update: if request.auth.uid != null
        && request.resource.data.keys().hasOnly(['update_requested_time']) == true;

The simulator request I'm sending (update with authentication):

{"__name__":"/databases/(default)/documents/scenarios/test1","data":{"update_requested_time":"2019-02-05T11:00:00.000Z"}}

My complete rules:

service cloud.firestore {
  match /databases/{database}/documents {
    match /scenarios/{scenario} {
      allow read: if true;
      allow update: if request.auth.uid != null
        && request.resource.data.keys().hasOnly(['update_requested_time']) == true;
      match /comments/{comment} {
        allow read: if true;
        allow create: if request.auth.uid != null;
        allow delete,update: if request.auth.uid != null && request.auth.uid == resource.data.user;
      }
      match /outputs/{tile} {
        allow read: if true;
      }
      match /mutations/{tile} {
        allow read: if true;
        allow create,update: if request.auth.uid != null;
      }
    }
    match /users/{user} {
      allow read: if true;
      allow update: if request.auth.uid != null && request.auth.uid == user;
    }
  }
}

screenshot of firestore rules + simulator


回答1:


request.resource.data doesn't contain the request data itself, but it rather contains the new version of the resource after the write operation. Therefore the check failed.

Firestore documentation on request.resource:

The new resource value, present on write requests only.




回答2:


Maybe I'm misreading this, but isn't a solution to have your rule do a deep comparison between request.data and request.resource.data to confirm that only the one field changed?

Maybe there is a cleaner way, but what about:

import _ from "lodash";

service cloud.firestore {
  match /databases/{database}/documents {

    function onlyRequestTimeChanged(currValue, newValue) {
      let newCopy = _.cloneDeep(newValue)
      newCopy.update_requested_time = currValue.update_requested_time

      return _.isEqual(currValue, newCopy)
    }

    match /scenarios/{scenario} {
      allow read: if true;
      allow update: if onlyRequestTimeChanged(request.resource,data, request.data)
  // ...
}



回答3:


Looks like the right way to do this now is

request.resource.data.diff(resource.data).affectedKeys().hasOnly(["update_requested_time"])

Found from this conversation



来源:https://stackoverflow.com/questions/54504528/firestore-security-rules-using-hasonly-on-a-request-to-check-if-only-a-specific

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!