我正在try 设置FiRestore规则,允许用户仅更新嵌套对象中的某些字段.我知道如果它是顶级密钥,我们可以简单地使用如下内容:

allow update: if request.auth.uid == userID &&
    request.resource.data.diff(resource.data).affectedKeys().hasOnly(["key1", "key2"]);

然而,我遇到了一个限制对象内部键的更新的问题,让我们称之为"omeData".这就是我试过的,看起来合乎逻辑,但不起作用:

    allow update: if request.auth.uid == userID &&
 request.resource.data.diff(resource.data).affectedKeys().hasOnly("someData", "otherData") &&
         (!("someData" in request.resource.data.keys()) ||
              (request.resource.data["someData"].diff(resource.data["someData"])
                 .affectedKeys().hasOnly(["key1", "key2"]));

或者像这样:

 allow update: if request.auth.uid == userID &&
request.resource.data.diff(resource.data).affectedKeys().hasOnly("someData", "otherData") &&
         (!("someData" in request.resource.data.keys()) ||
              (request.resource.data.someData.diff(resource.data.someData)
                 .affectedKeys().hasOnly(["key1", "key2"]));

两种方法都不管用.

有办法保护嵌套字段吗?

谢谢!

我try 在测试环境中使用"mocha"进行测试,try 使用Real FiRestore,并try 将此逻辑放在单独的函数中.毫无办法.

Firebase文档只说明文档的顶级密钥. 我在这里找到了一个解决方案,类似于我的第一个示例,但在现实生活中似乎不起作用.

我做错什么了吗?

Update with more specific details

以下是我最初的规则(在一款出租车预订应用上工作):

allow update: if request.auth.uid == userID && 
        editOnlyFields(["name", "phoneNumber", "driversData", "waitingDriverApproval", "lastUpdate"]) &&
        request.resource.data.lastUpdate == request.time &&
        (!("name" in request.resource.data.keys()) || 
          request.resource.data.name is string) &&
        (!("phoneNumber" in request.resource.data.keys()) || 
          request.resource.data.phoneNumber is string) &&
        (!("waitingDriverApproval" in request.resource.data.keys()) || 
          (request.resource.data.waitingDriverApproval is bool && request.resource.data.waitingDriverApproval == true)) &&
        //nested driversData (pain in the butt)
        (!("driversData" in request.resource.data.keys()) ||
          (request.resource.data.driversData.keys().hasOnly(["licensingAuthority", "amountOfVehicles", "DVLACheckCode", "DVLALicence", "PHBadge", "PHDriversLicence"])) && 
            (!("licensingAuthority" in request.resource.data.driversData.keys()) || 
              request.resource.data.driversData.licensingAuthority is string) &&
            (!("amountOfVehicles" in request.resource.data.driversData.keys()) || 
              request.resource.data.driversData.amountOfVehicles is int) &&
            (!("DVLACheckCode" in request.resource.data.driversData.keys()) || 
              (request.resource.data.driversData.DVLACheckCode.keys().hasOnly(["code", "pending"]) &&
                isValidDVLACheckCode(request.resource.data.driversData.DVLACheckCode))) && 
            (!("DVLALicence" in request.resource.data.driversData.keys()) || 
              (request.resource.data.driversData.DVLALicence.keys().hasOnly(["downloadUrl", "pending"]) &&
                isValidDriversPaperwork(request.resource.data.driversData.DVLALicence))) &&
            (!("PHBadge" in request.resource.data.driversData.keys()) || 
              (request.resource.data.driversData.PHBadge.keys().hasOnly(["downloadUrl", "pending"]) && 
                isValidDriversPaperwork(request.resource.data.driversData.PHBadge))) &&
            (!("PHDriversLicence" in request.resource.data.driversData.keys()) || 
              (request.resource.data.driversData.PHDriversLicence.keys().hasOnly(["downloadUrl", "pending"]) &&
                isValidDriversPaperwork(request.resource.data.driversData.PHDriversLicence))));

}

function editOnlyFields(allowedFields) {
      return request.resource.data.diff(resource.data).affectedKeys().hasOnly(allowedFields);
    }
  

一切都很顺利,直到我开始从事管理部分的工作.管理员具有身份验证令牌自定义声明isBoss: true

如果isBoss(),则规则允许读写

match /{document=**} {
      allow read, write: if isBoss();
    }
function isBoss() {
      return request.auth.token.isBoss == true;
    }

现在,对于原始规则,我面临着一个问题--当管理员更改用户driversData内的任何内容时,用户不会被授权做一些事情,比如approvedDriver: true,那么用户就不能更新他们的driversData,因为请求对象包含更新后出现的所有字段.

因此,我从替换下面这行开始了规则更新:

request.resource.data.driversData.keys().hasOnly(allowedFields)

这一个:

(editOnlyFieldsNested("driversData", ["licensingAuthority", "amountOfVehicles", "DVLACheckCode", "DVLALicence", "PHBadge", "PHDriversLicence"]))

哪里

function editOnlyFieldsNested(nestedObj, allowedFields) {
      return request.resource.data[nestedObj].diff(resource.data[nestedObj]).affectedKeys().hasOnly(allowedFields);
    }

当我运行FiRestore模拟器时,以下函数失败:

it("Can update a user document only with allowed fields", async () => {
    await testEnv.withSecurityRulesDisabled((context) => {
      return context.firestore().collection("users").doc(myId).set({
        name: "User Abc",
        email: "abs@ruxus.taxi",
        createdAt: serverTimestamp(),
      });
    });
    const testDoc = myUser.firestore().collection("users").doc(myId);
    await assertSucceeds(
      testDoc.update({
        name: "New Name",
        phoneNumber: "after",
        driversData: { amountOfVehicles: 1, licensingAuthority: "Bristol" },
        lastUpdate: serverTimestamp(),
      })
    );
  });

如果我用旧规则(没有MapDiff)运行它,它就通过了. 如果我使用带有editOnlyFieldsNested()的新规则,它只有在我注释掉driversData属性时才能通过,否则它会给我一个错误:

 Can update a user document only with allowed fields:



    FirebaseError: 7 PERMISSION_DENIED: 
Property isBoss is undefined on object. for 'update' @ L8, evaluation error at L17:24 for 'update' @ L17, Property isBoss is undefined on object. for 'update' @ L8, Property driversData is undefined on object. for 'update' @ L17

也许它可以在驱动程序上做一些事情对象上未定义的数据... 为什么? 我被困在这个上面好几天了,我的笔记本满是泪水..

如有任何建议,不胜感激.

推荐答案

我认为,主要问题在于.diff()方法试图比较两个对象-requestresource.如果资源没有driversData个对象,测试(和实际请求)将失败.

我看到了两种解决问题的方法.要么创建也有嵌套对象的空driversData的所有用户,要么重写在driversData不存在的情况下也有效的规则.

UPDATE + SOLUTION

终于把一切都解决了.我的解决方案是创建一个函数,该函数首先判断资源文档中是否存在driversData,然后使用.diff()函数创建一个MapDiff,但如果更新是针对还没有driversData的资源,则使用 .keys().hasOnly(allowedFields); 在请求对象上. 由于FiRestore规则帮助器函数中不能有任何"if语句",所以我使用了"&;&;"和"||"的组合,这看起来并不太笨拙:

function editOnlyFieldsNested(nestedObj, allowedFields) {
      return (nestedObj in resource.data && request.resource.data[nestedObj].diff(resource.data[nestedObj]).affectedKeys().hasOnly(allowedFields))
         || request.resource.data[nestedObj].keys().hasOnly(allowedFields);
    }

此外,我认为值得指出的是,如果我们使用点符号(如"driversData.DVLALicence")作为nestedObj的参数,上面的函数似乎不能正常工作. 因此,因为我的"DVLALicence"也是一个对象/映射,所以我必须创建另一个函数,该函数可以重复使用位于driversData内的任何其他对象:

function editOnlyFieldsNestedInDriversData(nestedObj, allowedFields) {
      return (nestedObj in resource.data.driversData && request.resource.data.driversData[nestedObj].diff(resource.data.driversData[nestedObj]).affectedKeys().hasOnly(allowedFields))
         || request.resource.data.driversData[nestedObj].keys().hasOnly(allowedFields);
    }

当然,如果我们不想在更新期间替换整个嵌套对象,那么非常感谢Frank van Puffelen关于DOT表示法的建议. 我希望它能帮助任何遇到这个或类似问题的人.

Javascript相关问答推荐

禁用从vue.js 2中的循环创建的表的最后td的按钮

React Native平面列表自动滚动

使用redux-toolet DelivercThunks刷新访问令牌

JavaScript文本区域阻止KeyDown/KeyUp事件本身上的Alt GR +键组合

node TS:JWT令牌签名以验证客户端和后台问题之间的身份验证

使用useEffect,axios和useParams进行react测试

在我的html表单中的用户输入没有被传送到我的google表单中

当Redux提供程序访问Reduxstore 时,可以安全地从Redux提供程序外部调用钩子?

闭包是将值复制到内存的另一个位置吗?

在Java中寻找三次Bezier曲线上的点及其Angular

如何将未排序的元素追加到数组的末尾?

在使用位板时,如何在Java脚本中判断Connect 4板中中柱的对称性?

变量在导入到Vite中的另一个js文件时成为常量.

当标题被点击时,如何使内容出现在另一个div上?

传递方法VS泛型对象VS事件特定对象

未捕获的运行时错误:调度程序为空

AddEventListner,按键事件不工作

FireBase FiRestore安全规则-嵌套对象的MapDiff

重新呈现-react -筛选数据过多

Reaction路由v6.4+数据API:重试加载程序而不显示错误