Как правильно сформулировать иерархическое правило чтения в Firestore

#google-cloud-firestore #firebase-security

#google-облако-firestore #firebase-безопасность

Вопрос:

Это должно быть довольно просто, но я столкнулся с проблемой чтения иерархических данных в Firestore. Взяв пример из официальной документации и немного адаптировав его, я…

 service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      allow read: if resource.data.visible == true;

        // Explicitly define rules for the 'landmarks' subcollection
        match /landmarks/{landmark} {
          allow read: if get(/databases/$(database)/documents/cities/$(city)/landmarks/$(landmark)).data.promoted == true;
        }
    }
  }
}
 

Похоже, это способ выразить правило чтения на более низких уровнях, где условие основано на значении поля во вложенных данных, но, к сожалению, это не работает. Кто-нибудь знает правильный способ сделать это?

Ответ №1:

Firestore может следовать этому правилу в лучшем случае только для get одного документа, а не list для коллекции. «Правила безопасности — это не фильтры» — это мантра здесь: правила безопасности НЕ будут просто проходить через документы, которые соответствуют правилам, блокируя те, которые не соответствуют; если запрос может привести к блокировке файла, весь ЗАПРОС будет отклонен. Поскольку единственным документом, который может быть разрешен, будет один.get() документа

использование get() в правилах безопасности должно указывать один документ, даже если запрос предназначен для коллекции. Таким образом, единственным способом, которым могло бы работать подобное правило, было бы:

 service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      allow read: if resource.data.visible == true;

        // Explicitly define rules for the 'landmarks' subcollection
        match /landmarks/{landmark} {
          allow read: if resource.data.promoted == true;
        }
    }
  }
}
 

к нему можно получить доступ с помощью .get() одного documentRef, который, оказывается promoted , установлен, или
collectionRef..where(field:'promoted', opStr:'==', value:'true')
таким образом, чтобы условие запроса соответствовало условию правила безопасности, то есть запрос фильтрует документы таким образом, чтобы все они соответствовали правилу безопасности.

Комментарии:

1. Спасибо. Я пробовал это с несколькими вариантами. Я использую Swift, но синтаксис запроса такой ref.collection(cities).document("Paris").collection(landmarks).document("Eiffel Tower").whereField("promoted", isEqualTo: true).getDocuments() {... . Нет проблем, когда правило ниже allow read: if true . Устранение неполадок — сложная задача. Есть идеи, как действовать дальше?

2. предложения .where() применяются только к операциям со СПИСКОМ (т. Е. К Коллекции), поэтому обычно мы рассматриваем ref.collection(cities).document("Paris").collection(landmarks).whereField("promoted", isEqualTo: true).getDocuments() {... .where() определяет подмножество документов, которые должны быть возвращены. Они не являются «более низким уровнем». В вашем примере будут возвращены все «знаковые» документы, помеченные как «повышенные». Вы пытаетесь получить все города , в которых есть повышенные ориентиры?

3. Да, я понимаю, что a where делает в запросе. Цель запроса здесь — найти все продвигаемые достопримечательности в одном городе, например, в Париже. Затем запрос соответствует if инструкции в правиле безопасности.

4. ОПЯТЬ ЖЕ — Правила безопасности не являются фильтрами — они НЕ будут выбирать одни документы, а не другие, либо разрешены ВСЕ записи в запросе, либо НЕТ. Ваш ЗАПРОС — это фильтр; правило безопасности — ваша защита.

5. «Нижнее правило» НЕ будет действовать как фильтр.