繁体   English   中英

Firestore 规则在规则游乐场中失败(如预期)但在我的实时站点中通过

[英]Firestore rule fails in the rules playground (as expected) but passes in my live site

我有以下 Firestore 数据库规则。 我的类别有一个“私人”boolean 字段,以及一个 userId。 这些是我要执行的条件:

  1. 如果一个类别是私有的并且由用户拥有(即,与类别文档中的 userId 相同的 userId),他们可以阅读它。
  2. 如果类别是私有的且不属于用户(userId 不匹配),则他们无法读取它。

在规则游乐场中,当我尝试访问文档路径 categories/{id} 中的类别文档时,对于私有字段设置为true的类别,它正确地拒绝了访问。 但是,当我在我的实时应用程序中阅读与不拥有该类别的用户相同的类别时,规则允许它。 我很难过。

rules_version = '2';

function fieldExists(data, field) {
  return !isUndefined(data, field) && !isNull(data, field);
}

function isUndefined(data, field) {
  return !data.keys().hasAll([field]);
}

function isNull(data, field) {
  return data[field] == null;
}

function canView(resource, auth) {
    return resource.data.private == true && isOwner(resource, auth);
}

function isPublic(resource) {
  return !fieldExists(resource.data, 'private') || resource.data.private != true;
}

function isAuthenticated(request) {
    return request.auth != null;
}

function isOwner(resource, auth) {
    return resource.data.userId == auth.uid;
}

service cloud.firestore {
  match /databases/{database}/documents {
    match /categories/{categoryId} {
      allow create: if isAuthenticated(request);
      allow update, delete: if isAuthenticated(request) && isOwner(resource, request.auth);
      allow read: if isAuthenticated(request) &&
                            (isPublic(resource) || canView(resource, request.auth));
    }
  }
}

我正在对该类别提出两个不同的请求。 一个在检索许多类别的列表页上,另一个在检索单个类别的视图页上。

import {
  collection,
  query,
  getDocs,
  where,
  limit,
} from "firebase/firestore/lite"; // ^9.5.0
import _ from 'lodash'; // ^4.17.21
 
// Some helper methods
export async function getCollection(collectionName) {
  return await collection(db, collectionName);
}

export async function getCollectionRef(collectionName) {
  return await getCollection(collectionName);
}

export async function runQuery(q) {
  const querySnapshot = await getDocs(q);
  if (_.isEmpty(querySnapshot)) {
    return null;
  }

  return querySnapshot.docs.map((doc) => doc.data());
}

// The query method to look up the category by id for the 'view' page
export async function queryById(collectionName, id) {
  const collectionRef = await getCollectionRef(collectionName);
  const q = query(collectionRef, where("id", "==", id), limit(1));
  const result = await runQuery(q);
  return result[0];
}

// I'm using this batch method to collect all the categories for the 'list' page based on an array of category ids
export async function queryByIds(collectionName, ids) {
  const collectionRef = await getCollectionRef(collectionName);
  const batches = [];

  if (!ids || !ids.length) {
    return [];
  }

  let arrayLength = ids.length;
  let currentIndex = 0;
  while (currentIndex <= arrayLength) {
    const batch = ids.slice(currentIndex, currentIndex + 10);
    if (!batch.length) {
      break;
    }
    const q = query(collectionRef, where("id", "in", batch));
    batches.push(runQuery(q));
    currentIndex += 10;
  }

  return await (await Promise.all(batches)).flat();
}

如果我将“私人”类别视为类别的非所有者,我仍然可以看到类别数据。 我还可以以该用户的身份查看类别列表中的类别(当查看其他用户的类别时)。 我希望“私人”类别不会包含在类别列表中,如果直接查看它,其数据将不会出现在系统中。 注意:我正在使用 Vue,当我路由到类别页面时,我从路由参数中的 id 请求类别文档。

这些规则在规则操场上按预期工作,但在实际情况下却行不通,我不明白为什么。 任何帮助将不胜感激。

更新:我的 Firestore 数据结构如下:

- users
- categories
  - category: {
    id: String
    name: String
    userId: String
    private: Boolean
    createdAt: Timestamp
    updatedAt: Timestamp
    notes: Array (of ids)
    ...additional data fields
  }
- notes
- photos

其他顶级根 collections 在数据库规则中应用了与类别相同的规则。 例如:


    match /users/{userId} {
        allow create: if isAuthenticated(request);
      allow update, delete: if isAuthenticated(request) && isOwner(resource, request.auth);
      allow read: if isAuthenticated(request) &&
                            (isPublic(resource) || canView(resource, request.auth));
    }

我认为他们不会影响这个问题,因为在这种情况下我不会查询他们。

Cloud Firestore 安全规则仅加载文档中包含在查询中的字段。 如果您不将其包含在查询中,它们肯定会失败(因为它们将针对未定义的内容进行测试)。

Cloud Firestore 安全规则不会自行过滤数据。 他们只是强制读取操作只访问允许的文档。 您需要将where("private", "==", true)添加到您的查询中以使其与您的规则相匹配。 请参阅下面的代码:

const q = query(collectionRef, where("id", "==", id), where("private", "==", true), limit(1));

请注意:这确实要求文档具有private字段,否则private字段不能为空。 您应该在所有文档上添加private字段。 您可以保留您的安全规则,因为它只是为了强制所有文档都具有private字段。

当您触发上面的查询时,控制台将通过控制台的错误消息要求您创建索引。 错误消息将如下所示:

[FirebaseError: The query requires an index. You can create it here: https://console.firebase.google.com/v1/r/project/<PROJECT-ID>/firestore/indexes?create_composite=ClRwcm9qZWN0cy90aXBoLW1hcmNhbnRob255Yi9kYXRhYmFzZXMvKGRlZmF1bHQpL2NvbGxlY3Rpb25Hcm91cHMvY2F0ZWdvcmllcy9pbmRleGVzL18QARoKCgZ1c2VySWQQARoLCgdwcml2YXRlEAEaDAoIX19uYW1lX18QAQ] {
  code: 'failed-precondition',
  customData: undefined,
  toString: [Function (anonymous)]
}

编辑:

如果您不使用评论中提到的简单查询和复合查询,那么您的 Firestore 规则将有效并按您的意愿工作,因为您没有将任何过滤器传递给查询,这使得 Firestore 安全规则可以读取所有给定文档中的fieldname及其数据。


您可以在这些文档中找到更多相关信息:

您的read规则中有一组&&

allow read: if isAuthenticated(request) &&
            && (isPublic(resource) || canView(resource, request.auth));

尝试删除其中的一组&& 这可能是造成怪异的原因?

allow read: if isAuthenticated(request) &&
            (isPublic(resource) || canView(resource, request.auth));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM