【问题标题】:Firestore Security Rules with advanced public/token rules and array-contains具有高级公共/令牌规则和数组包含的 Firestore 安全规则
【发布时间】:2020-11-07 17:41:39
【问题描述】:

我正在使用以下数据库开发应用程序:

organizations/{ordId}

organizationsPrivateData/{orgId}

events/{eventId}

一个组织由以下人员组成:

  • 所有者:uid
  • 公共:布尔值

一个事件

  • 标题:字符串
  • organizationId:字符串

一个organizationPrivateData

  • organizationId:字符串(虽然 organizationPrivateData id 本身就是 organizationId)
  • readToken: 字符串

我要处理两个案例:

案例一

该组织是“公共的”(true),因此所有事件都可以在没有任何标记的情况下读取。这是通过以下安全规则isOrganizationPublic 完成的:


// DB Read
function organizationData(organizationId) { return get(/databases/$(database)/documents/organizations/$(organizationId)).data }
function organizationPrivateData(organizationId) { return get(/databases/$(database)/documents/organizationsPrivateData/$(organizationId)).data }

// Auth management
function authenticated() { return request.auth.uid != null }
function isAdmin(data) { return data.owner == request.auth.uid || request.auth.uid in data.members}
function isEmailVerified() { return request.auth.token.email_verified == true }

// Tokens management
function isOrganizationReadAccepted (organizationId, readToken) {
    let organization =  organizationData(organizationId);

    return isOrganizationPublic(organization);
}

function isOrganizationPublic (organization) { return organization.public == true }
function isReadTokenValid (organizationId, token) { return organizationPrivateData(organizationId).readToken == token }

match /events/{eventId} {
    allow read: if isOrganizationReadAccepted(resource.data.organizationId, debug(resource.data.token));
}


match /organizations/{organizationId} {
    allow get: if isOrganizationReadAccepted(organizationId, request.resource.data.token);
}

match /organizationsPrivateData/{orgId} {
    allow read: if authenticated() && isAdmin(organizationData(resource.data.organizationId));
}

案例 2

organization.public = false。然后isOrganizationPublic 返回false,我们可以做一个三元来做额外的检查。目标是检查organizationPrivateData 以确保readToken 有效。我的想法是通过 where 子句从客户端传递令牌,如下所示:

app
    .collection('events')
    .where('organizationId', "==", "org1")
    .where('token', 'array-contains-any', ["",'azerty'])
    .get()

规则:return isOrganizationPublic(organization) ? true : isReadTokenValid(organizationId, resource.data.token[0])

问题:我在这里被卡住了。如果这可行,我需要在每个事件上都有一个“令牌”字段,并带有一个空字符串,这对我来说是可以的。因此,如果安全通过,真正的“where”可以返回所有授予的事件。

在对可用的 where 运算符(in、array-contains、array-contains-any)进行额外检查时,似乎:

  • in: 规则中的值是一个字符串,可能每个数组值调用多次
  • array-contains: 是一个不同的结构,看起来像 constraint_value { simple_constraints { comparator: LIST_CONTAINS value { string_value: "" } } }。也只得到一个值
  • array-contains-any:看起来像 array-contains,但里面有所有的 where 数组。

可能的解决方案不是最优的:在每个“事件”中都有令牌。这可行,但在令牌更新时,我需要更改该组织的所有事件,这不是最佳的,也可能需要时间。

没有解决办法:

  • 使用hasAny 和'in' 运算符,in 没有所有的数组值。
  • 提取数组的第一项,array-contains-any 不是数组。

完整源代码可在github here上获得

可能的相关问题:Firestore Security Rules for Query with Array Contains

【问题讨论】:

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


    【解决方案1】:

    您想以在 Firestore 中无法实现的方式在两个集合之间进行连接。正如您自己提到的那样,唯一的解决方案是在每个 event 文档中包含 token

    您可以使用此解决方案,这样您就不必在更新令牌时更新所有 event 文档:

    1. organizationsPrivateData:使用数组readTokens 保存所有历史令牌,更新时追加新的

    2. 将您的规则更改为:

      function isReadTokenValid (organizationId, token) {
        return token in organizationPrivateData(organizationId).readTokens;
      }
      

    【讨论】:

      猜你喜欢
      • 2018-03-17
      • 2019-11-25
      • 2020-12-28
      • 1970-01-01
      • 1970-01-01
      • 2018-10-19
      • 2019-07-05
      • 1970-01-01
      • 2021-04-12
      相关资源
      最近更新 更多