Firestore 安全规则:根据父文档的权限查询子集合

时间:2021-04-22 20:27:30

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

假设我有一个带有 restaurants 子集合的 menus 集合。餐厅权限设置在餐厅文档中,如下所示:

餐厅

{
 "roles": {
    "user123": "Owner"
  }
}

有权访问餐厅的任何人也可以访问其任何子集合,包括 menus 子集合。这是安全规则:

安全规则

match /restaurants/{restaurantId}/{document=**} {
  allow read: if resource.data.roles[request.auth.uid] > '' ;
}

查询餐厅列表可以这样完成,效果很好:

餐厅查询

firebase
      .firestore()
      .collection('restaurants')
      .where(`roles.${uid}`, '>', '');

现在如何查询 menus 子集合?我知道 Firestore 需要根据查询推断权限,而无需查看基础数据。从逻辑上讲,这应该是可能的,因为任何有权访问 restaurant 的人也可以访问 menus 子集合。但是我如何编写这个 menus 子集合查询,以便 Firestore 可以推断出这一点?我能想到的最合乎逻辑的查询是这样的:

查询菜单子集合(不起作用)

firebase
     .firestore()
     .doc(`restaurants/${restaurantId}`) // user has access to this restaurant
     .collection('menus');

...但这在权限被拒绝的情况下不起作用。

1 个答案:

答案 0 :(得分:1)

事实证明,我需要在 restaurant 级别创建 2 条安全规则才能实现我想要的:

    match /restaurants/{restaurantId} {
      allow read: if resource.data.roles[request.auth.uid] > ''
    }

    match /restaurant/{restaurantId}/{document=**} {
      allow read: if get(/databases/$(database)/documents/restaurants/$(restaurantId)).data.roles[request.auth.uid] > '';
    }

为了查询用户有权访问的餐馆,第一条规则是必要的。单独的第二个查询不起作用,尽管我不确定为什么。这是第一条规则的查询:

firebase
      .firestore()
      .collection('restaurants')
      .where(`roles.${uid}`, '>', '');

第二条规则对于所有子集合查询都是必需的。举个例子:

firebase
      .firestore()
      .collection(`restaurants/${restaurantId}/menus`);

第二条规则允许子集合查询的事实非常有趣,因为 Firestore 需要读取 restaurant 文档才能允许此查询。这与其声称它不查看查询的基础数据相矛盾,但如果它只需要读取路径中的一个文档,它看起来似乎确实如此。