如何通过自定义声明控制从多个来源对文档的访问

时间:2020-08-09 22:08:13

标签: firebase google-cloud-firestore firebase-security

情况

仅当用户自定义声明中存在特定的键/值对时,我才希望允许更新文档。用户自定义声明中可以有多个键/值对。

设置

想象以下自定义声明已添加到用户:

{
    theClubId: 3,
    otherId: 2,
    oneMoreId: -1
}

关键是现在称为俱乐部ID。该值是编码为整数的用户角色。他在俱乐部theClubId中的角色为3,因此他应该能够在该俱乐部中编辑其他俱乐部成员的角色。

我在Firestore中创建了集合clubMembers。它包含以下结构的多个文档:

{
    uid: "some-uid",
    clubIds: ["theClubId", "oneMoreId"],
    clubData: {
                  theClubId: { role: 1 },
                  oneMoreId: { role: 1 }
              },
    cantChangeMe: "foobar"
}

如果满足以下条件,则用户只能更新此文档:

  1. 只应允许他编辑他所属的clubData对象。这是自定义声明起作用的地方。
  2. 他的角色应高于2。
  3. 其他属性(例如cantChangeMe)是只读的

我当前的解决方案

这很丑陋,但是有效。至少直到clubIds数组大于3个项目为止。另外,这种方法限制用户每次更新最多只能编辑1个角色,但是由于用户一次只能登录一个俱乐部,所以很好。

function canUpdateMember() {
    let claimKeys = request.auth.token.keys();

    let didNotChangeForbiddenKeys = request.resource.data.diff(resource.data).affectedKeys().hasOnly(["clubData"]);

    let affectedClubId = request.resource.data.clubData.diff(resource.data.clubData).affectedKeys();
    let isMemberOfChangedClubMember = affectedClubId.hasAny(claimKeys);

    let clubId0 = claimKeys[0];
    let clubId1 = claimKeys[1];
    let clubId2 = claimKeys[2];

    let clubId0Result = [clubId0].toSet() == affectedClubId && request.auth.token[clubId0] >= 3 && innerValidation(request.resource.data.clubData[clubId0]);
    let clubId1Result = [clubId1].toSet() == affectedClubId && request.auth.token[clubId1] >= 3 && innerValidation(request.resource.data.clubData[clubId1]);
    let clubId2Result = [clubId2].toSet() == affectedClubId && request.auth.token[clubId2] >= 3 && innerValidation(request.resource.data.clubData[clubId2]);

    return didNotChangeForbiddenKeys && 
           affectedClubId.size() == 1 && 
           isMemberOfChangedClubMember && 
           (clubId0Result || clubId1Result || clubId2Result);
}

function innerValidation(clubObj) {
    let accessLevelValid = clubObj.role <= 3 && clubObj.role >= -1;

    return accessLevelValid;
}

由于.affectedKeys()返回一个Set,所以没有简单的方法来获取特定索引处的值。

0 个答案:

没有答案