firestore安全规则文档ID导致零值错误

发布于 2025-02-14 00:36:21 字数 2832 浏览 0 评论 0 原文

我正在努力保护我的Firestore数据库,并遇到了一张通配符的麻烦。我有一个名为研究的根级集合,我只想在 status 字段设置为“已发布”时才允许阅读。

我当前的安全规则如下,

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

  function isStudyPublished(studyId) {
    return get(/databases/$(database)/documents/study/$(studyId)).data.status == "published";
  }

  ...

  // Match for participant permissioning
  match /study/{studyId}/{document=**} {
    // Deny all requests to create, update, or delete the study
    allow create, update, delete: if false

    // Allow the requestor to read the study if it is published
    allow read: if isStudyPublished(studyId)
  }

  ...

  }
}

此安全性规则在请求单个文档时效果很好,但在执行集合级查询时效果很好。我收到错误消息: null值错误。对于“列表” 。我知道Firestore规则不充当过滤器,但是,我相信我的查询只能在状态字段等于“已发布”的情况下请求文档。

这是我的两个查询:

// successfully retrieves a single document
export const getStudy: GetStudy = (studyId) => {
  return new Promise((resolve, reject) => {
    getDoc(doc(firestore, COLLECTION_KEYS.STUDY, studyId))
      .then((doc) => {
        resolve(dataToStudy(doc.id, doc.data()));
      })
      .catch((error) => reject(error));
  });
};
// fails to retrieve multiple documents
export const getStudies: GetStudies = (cursor) => {
  const studies: StudyDoc[] = [];
  return new Promise((resolve, reject) => {
    let q = query(collection(firestore, COLLECTION_KEYS.STUDY), where("status", "==", "published"), orderBy(FIELD_KEYS.UPDATED));

    if (cursor) q = query(q, startAfter(cursor));

    getDocs(q)
      .then((snapshot) => {
        snapshot.forEach((doc) => {
          studies.push(dataToStudy(doc.id, doc.data()));
        });

        return resolve([studies, snapshot.docs[snapshot.docs.length - 1]]);
      })
      .catch((error) => {
        return reject(error);
      });
  });
};

我最初的想法是,我的收集查询是请求将使安全规则失败的文档,但是在凝视查询之后,我认为这是不正确的...

作为测试,我更改了我的安全规则要测试在调试功能。我最终意识到每次我引用 studyID 变量时,支票都会失败。例如,以下匹配语句失败。

match /study/{studyId}/{document=**} {
  // Deny all requests to create, update, or delete the study
  allow create, update, delete: if false

  // Allow the requestor to read the study if it is published
  allow read: if debug(debug(true) && (debug(studyId) != null));
}

查看 firestore-debug.log ,如果我运行请求单个文档的查询,我会看到以下记录:

bool_value: true

string_value: "rMxuVTJ0O15qPjMbpEU1"

bool_value: true

但是,运行集合查询时,请从未运行:

bool_value: true

我从不运行进入文档ID通配符,抛出这样的错误。我无法确定这是我的匹配语句还是我的查询是错误,但是对任何帮助表示赞赏。

I am working on securing my Firestore database and am having trouble with a collection wildcard. I have a root-level collection called study, I want to allow reads only if the status field is set to "published".

My current security rules are as follows

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

  function isStudyPublished(studyId) {
    return get(/databases/$(database)/documents/study/$(studyId)).data.status == "published";
  }

  ...

  // Match for participant permissioning
  match /study/{studyId}/{document=**} {
    // Deny all requests to create, update, or delete the study
    allow create, update, delete: if false

    // Allow the requestor to read the study if it is published
    allow read: if isStudyPublished(studyId)
  }

  ...

  }
}

This security rule works great when requesting a single document, but not when doing a collection-level query. I get the error message: Null value error. for 'list'. I understand that Firestore rules do not act as a filter, however, I believe my query should only be requesting documents where the status field equals "published".

Here are my two queries:

// successfully retrieves a single document
export const getStudy: GetStudy = (studyId) => {
  return new Promise((resolve, reject) => {
    getDoc(doc(firestore, COLLECTION_KEYS.STUDY, studyId))
      .then((doc) => {
        resolve(dataToStudy(doc.id, doc.data()));
      })
      .catch((error) => reject(error));
  });
};
// fails to retrieve multiple documents
export const getStudies: GetStudies = (cursor) => {
  const studies: StudyDoc[] = [];
  return new Promise((resolve, reject) => {
    let q = query(collection(firestore, COLLECTION_KEYS.STUDY), where("status", "==", "published"), orderBy(FIELD_KEYS.UPDATED));

    if (cursor) q = query(q, startAfter(cursor));

    getDocs(q)
      .then((snapshot) => {
        snapshot.forEach((doc) => {
          studies.push(dataToStudy(doc.id, doc.data()));
        });

        return resolve([studies, snapshot.docs[snapshot.docs.length - 1]]);
      })
      .catch((error) => {
        return reject(error);
      });
  });
};

My initial thought is that my collection query was requesting documents that would fail the security rules, but after staring at the query I don't think that's true...

As a test, I changed up my security rules to test what would pass using the help of the debug function. I ended up realizing that the check fails every time I reference the studyId variable. As an example the following match statement fails.

match /study/{studyId}/{document=**} {
  // Deny all requests to create, update, or delete the study
  allow create, update, delete: if false

  // Allow the requestor to read the study if it is published
  allow read: if debug(debug(true) && (debug(studyId) != null));
}

When looking at the firestore-debug.log, if I run the query that requests a single document I see the following logged:

bool_value: true

string_value: "rMxuVTJ0O15qPjMbpEU1"

bool_value: true

however, when running the collection query the following is logged:

bool_value: true

I've never run into the document id wildcard throwing an error like this. I can't determine if this is an error with my match statement or my query, but any help is appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

二智少女 2025-02-21 00:36:21

我不确定您的用例是什么,但是在尝试读取正在访问或阅读的文档中的数据时,您不必使用 get()。您应该使用 。请参阅下面的Firestore规则:

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

    function isStudyPublished() {
      return resource.data.status == "published";
    }

    // Match for participant permissioning
    match /study/{studyId}/{document=**} {

      // Deny all requests to create, update, or delete the study
      allow create, update, delete: if false

      // Allow the requestor to read the study if it is published
      allow read: if isStudyPublished();
    }
  }
}

更新:

访问子收集时,您的查询应与您的安全规则匹配。您应该具有根集合的文档ID来访问您的子集合。例如:

// Document-ID refers to study's document
let q = query(collection(db, "study", <DOCUMENT-ID>, <SUBCOLLECTION-NAME>), where(<FIELDNAME>, "==", <STRING>));

安全规则:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /study/{studyId} {
      // Deny all requests to create, update, or delete the study
      allow create, update, delete: if false

      // Do all restrictions here for the study collection.

      function isStudyPublished() {
        return get(/databases/$(database)/documents/study/$(studyId)).data.status == "published";
      }

      // Rules for accessing subcollection of study collection.
      match /subcollection-name/{docId} {
        // Allow read if the document referenced from the study collection is published.
        allow read: if isStudyPublished();

        // Do all restrictions here for study's subcollection.
      }
    }
  }
}

上面的示例规则仅允许在研究中的引用文档集合中标记为已发布的引用文档时,将允许读取。

I'm not sure what your use-case really is but you don't have to use get() when trying to read data from the document being accessed or read. You should use resource.data instead. See Firestore Rule below:

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

    function isStudyPublished() {
      return resource.data.status == "published";
    }

    // Match for participant permissioning
    match /study/{studyId}/{document=**} {

      // Deny all requests to create, update, or delete the study
      allow create, update, delete: if false

      // Allow the requestor to read the study if it is published
      allow read: if isStudyPublished();
    }
  }
}

UPDATE:

When accessing your subcollection, your query should be matched with your security rules. You should have the document ID of the root collection to access your subcollection. E.g:

// Document-ID refers to study's document
let q = query(collection(db, "study", <DOCUMENT-ID>, <SUBCOLLECTION-NAME>), where(<FIELDNAME>, "==", <STRING>));

Security rules:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /study/{studyId} {
      // Deny all requests to create, update, or delete the study
      allow create, update, delete: if false

      // Do all restrictions here for the study collection.

      function isStudyPublished() {
        return get(/databases/$(database)/documents/study/$(studyId)).data.status == "published";
      }

      // Rules for accessing subcollection of study collection.
      match /subcollection-name/{docId} {
        // Allow read if the document referenced from the study collection is published.
        allow read: if isStudyPublished();

        // Do all restrictions here for study's subcollection.
      }
    }
  }
}

The sample rule above will only allow reads to the subcollection documents if the referenced document from the study collection is tagged as published.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文