【问题标题】:Firebase rules acting very strangeFirebase 规则表现得很奇怪
【发布时间】:2023-03-22 12:40:01
【问题描述】:

大家好。

我正在为一家医院编写一个具有这种数据库结构的颤振应用程序。

我在获取会话数据时遇到问题,正是以下文档。

使用以下方法获取治疗师的lastSession,使用他的治疗师UID作为过滤字段。

Future<Session> getLastSession() async {
 Query query;
 query = Firestore.instance
     .collection("sessions")
     .where("therapistUID",
         isEqualTo: this.uid)
     .orderBy("date", descending: true)
     .limit(1); //this.uid = auth uid of current therapist.
 try {

  QuerySnapshot querySnapshot = await query.getDocuments(); //exception thrown here

  if (querySnapshot.documents.isEmpty) {
    throw Exception("Empty query");
  } else {
    lastSession = Session.fromDocument(querySnapshot.documents[0]);
    return lastSession;
  }
} catch (e) {
  throw Exception("cannot get data from database");
}}

遵循以下规则

rules_version = '2';
 service cloud.firestore {
  match /databases/{database}/documents {

match /patients/{document=**} {
  allow read,write,list: if checkPatientAccess(resource.data);
}

match /therapists/{document=**} {
  allow read,write,list: if checkOwnership();
}

match /sessions/{document=**} { 
    allow read, write,list: if checkPatientAccess(get(/databases/$(database)/documents/patients/$(resource.data.patientUID)).data);
}

match /devices/{document=**} {
    allow read, write,list: if false;
}

match /clinics/{document=**} {
    allow read, write,list: if false;
  }}}

function checkOwnership(){
  return resource.id == request.auth.uid;
}

function checkPatientAccess(patient){
  return request.auth.uid in patient.therapistUIDs;
}

代码抛出此异常

有谁知道它为什么拒绝查询?请记住,查询只是一个文档,并且数据库中只有一个文档可以适合这些过滤器。使用具有相同参数的 testlab 是可行的。

【问题讨论】:

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


    【解决方案1】:

    Firebase 安全规则不会自行过滤数据,因为这无法扩展。当我们查看以下内容时,这一点变得清晰:

    match /sessions/{document=**} { 
        allow read, write,list: if checkPatientAccess(get(/databases/$(database)/documents/patients/$(resource.data.patientUID)).data);
    }
    function checkPatientAccess(patient){
      return request.auth.uid in patient.therapistUIDs;
    }
    

    为了保护您的读取操作,这些规则必须加载每个文档并检查其中的 therapistUIDs 值。这将是一个 O(n) 操作,而 Firestore 保证返回 O(1) 的结果。因此,此类安全规则不起作用。

    您的规则确实适用于读取单个文档,但不适用于 list 操作。

    如果您可以使用返回所需数据的查询,则可以保护该查询。但由于 Firestore 不支持任何类型的查询连接,因此您需要将要过滤的数据从 patient 文档复制到每个 session 文档中才能使这项工作正常进行。


    正如 cmets 中所讨论的:由于您的查询确保所有文档具有相同的 patientUID,因此您的规则中的 get() call 保证始终获得相同的文档,因此规则引擎可以保证它永远不会返回查询的授权文档。

    其实很漂亮。

    【讨论】:

    • 这些规则使用另一个参数工作是不是很奇怪?像患者UID?因为它可以使用它。 (获取患者的最后一次会话)
    • 如前所述:它适用于您正在阅读的文档中的字段,因为规则引擎足够智能,可以检查您何时连接了侦听器。但它不能对其他文档执行此操作,因为它必须主动检查这些文档。
    • 但这是相同的方法、相同的规则、相同的规则流程。使用 where("patientUID", equals: uid) 代替 where("treatmentUID", equals: uid) 很抱歉再次问你,但对我来说这很奇怪,它应该双向出错。
    • 我得到了关于文件应该存在并且不能每次都检查的解释,对此我非常感谢,现在更清楚了。但我不解释这种态度。
    • 您说“(为患者获取最后一次会话)”我认为这意味着您正在获取单个文档。在这种情况下,规则引擎确实真正读取了该文档,因为它可以保证需要多长时间。请参阅我的回答中的第二段。
    【解决方案2】:

    无论您请求多少文档 - Firestore 安全规则will not act as a filter on those documents。请阅读并理解本文档。它不会让您有条件地检查每个文档的某些内容以确定它是否可以阅读。您的规则试图表明,对于每个会话读取,匹配的患者文档中必须存在某些内容,但这是不允许的。它根本无法按照 Firestore 所需的方式进行扩展,并且对于具有大型结果集的查询来说成本非常高。

    【讨论】:

    • 是的,但我只要求我知道存在匹配文件的文件,这样做还有错吗?
    • 没关系。规则不知道这一点。您不能覆盖规则的行为。
    猜你喜欢
    • 2013-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-02
    • 2014-12-12
    • 2012-11-11
    • 1970-01-01
    相关资源
    最近更新 更多