具有高级公共/令牌规则和数组包含内容的Firestore安全规则

时间:2020-07-17 21:35:46

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

我正在使用具有以下数据库的应用程序:

organizations/{ordId}

organizationsPrivateData/{orgId}

events/{eventId}

组织由以下组成:

  • 所有者:uid
  • public:布尔值

事件

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

organizationalprivatedata

  • organizationId:字符串(尽管organizationPrivateData id本身就是organizationId)
  • readToken:字符串

我有两种情况要处理:

案例1

组织是“公共的”(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运算符(在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

1 个答案:

答案 0 :(得分:1)

您想要以Firestore中不可能的方式在两个集合之间进行联接。正如您自己提到的那样,唯一的解决方案是在每个token文档中添加event

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

  1. organizationsPrivateData:使用包含所有历史令牌的数组readTokens,将新令牌附加到续签上

  2. 将您的规则更改为:

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