Firestore安全规则未按预期运行

时间:2020-07-09 13:40:41

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

我重新安排了Cloud Firestore的安全规则,以防止重新生成集合中的文档。这是我使用的规则:

match /UserData/{uid}/DAILY_USAGES/{day} {
  allow create: if !exists(/databases/$(database)/documents/UserData/$(request.auth.uid)/DAILY_USAGES/$(day));
  allow read, update: if request.auth.uid == uid;
}

我使用这些文档路径和用户凭据在控制台中模拟了此规则

/UserData/UoeJtUhPpJTi78HouKLIqhcRpfs48/DAILY_USAGES/09-07-2020

如果我创建一个ID为09-07-2020的文档并使用上述路径进行模拟,则它将失败。当我删除文档09-07-2020时,可以进行上述模拟。因此,它在规则模拟器中效果很好。

但是,当我在应用程序中尝试时,每次尝试时它都会再次创建文档。因此,如果文档内容以前有所不同,它将重置为用于创建文档的默认值。

这是我在android studio中仅用于一次创建此文档的代码

DailyUsage usage = new DailyUsage();
        usage.setUsage(0);
        usage.setTime(formatDate(date));
        FirebaseFirestore.getInstance().collection(Constants.USER_DATA_ROOT).document(uid)
                .collection(Constants.FIRESTORE_CHILD_DAILY_USAGES).document(today).set(usage);

我不想检查文档是否以前存在,因为它会使ui变慢,并且无法按我预期的那样工作。因此,我尝试使用安全规则来防止重新生成文档,但无法在应用程序上工作。有人可以帮忙吗?

2 个答案:

答案 0 :(得分:1)

根据文档是否存在,set() method有两种行为:

如果文档不存在,将创建该文档。如果文档确实存在,除非您指定将数据合并到现有文档中,否则其内容将被新提供的数据覆盖

这意味着当文档已经存在时,将要触发的规则将是update规则,因此您将必须更改规则:

allow read, update: if request.auth.uid == uid;

包括在调用set且文档存在时必需的逻辑。

您可能想看一下answer,有关设置和更新的用法。提到create method可能很适合您的需求,但它不是Web API的一部分,不知道您所使用的API是否可用。

答案 1 :(得分:0)

由于@DougStevenson和@Emmanuel,我清楚地了解了这种情况。我以为set()总是触发allow create安全规则,因为我们使用set()创建文档,并使用update()更新文档。但是,这并非完全正确,如果之前有相同ID的文档,则set()会触发allow update规则。 set()还用于更新合并选项,我不知道。

我的问题是关于只创建一次Firestore文档并阻止其重新创建。但是,一方面,我还在某些情况下更新了此文档。我想防止使用默认值0覆盖文档。因此,我更新了规则,并且知道如果值是0,我将阻止更新。

match /UserData/{uid}/DAILY_USAGES/{day} {
      allow create: if !exists(/databases/$(database)/documents/UserData/$(request.auth.uid)/DAILY_USAGES/$(day));
                allow read: if request.auth.uid == uid; 
        allow update: if request.auth.uid == uid && request.resource.data.usage != 0;
    }

在应用程序中,我没有进行任何更改,并使用了这样的方法:

DailyUsage usage = new DailyUsage();
        usage.setUsage(0);
        usage.setTime(formatDate(date));
        FirebaseFirestore.getInstance().collection(Constants.USER_DATA_ROOT).document(uid)
                .collection(Constants.FIRESTORE_CHILD_DAILY_USAGES).document(today).set(usage);

如果没有相同ID allow create安全规则的文档触发,则此代码将在应用启动时触发并创建该文档。但是,如果有文档allow update,安全规则将触发并检查即将到来的资源数据,并在使用率值为0时阻止更新。在应用程序的代码块中,当应用程序启动时,它将值发送为0,因此不允许更新文档中的所有内容。