简体   繁体   English

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

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

I have the following Firestore database rules.我有以下 Firestore 数据库规则。 I have categories that have a 'private' boolean field, as well as a userId.我的类别有一个“私人”boolean 字段,以及一个 userId。 These are the conditions I want to enforce:这些是我要执行的条件:

  1. If a category is private and owned by a user (ie, same userId as the userId on the category document), they can read it.如果一个类别是私有的并且由用户拥有(即,与类别文档中的 userId 相同的 userId),他们可以阅读它。
  2. If a category is private and not owned by a user (userId does not match), then they cannot read it.如果类别是私有的且不属于用户(userId 不匹配),则他们无法读取它。

In the rules playground, when I attempt to access a category document at the document path categories/{id} for a category that has the private field set to true , it correctly denies access.在规则游乐场中,当我尝试访问文档路径 categories/{id} 中的类别文档时,对于私有字段设置为true的类别,它正确地拒绝了访问。 However, when I read the same category as a user that doesn't own the category in my live application, the rule allows it.但是,当我在我的实时应用程序中阅读与不拥有该类别的用户相同的类别时,规则允许它。 I'm stumped.我很难过。

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));
    }
  }
}

I'm making two different requests for the category.我正在对该类别提出两个不同的请求。 One is on a list page that retrieves many categories, and the other is on a view page that retrieves a single category.一个在检索许多类别的列表页上,另一个在检索单个类别的视图页上。

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();
}

If I view the 'private' category as a non-owner of the category I can still see the category data.如果我将“私人”类别视为类别的非所有者,我仍然可以看到类别数据。 I can also view the category in the list of categories as that user (when viewing another user's categories).我还可以以该用户的身份查看类别列表中的类别(当查看其他用户的类别时)。 I would expect that the 'private' category would not be included in the list of categories and if viewing it directly its data would not be present to the system.我希望“私人”类别不会包含在类别列表中,如果直接查看它,其数据将不会出现在系统中。 Note: I'm using Vue and when I route to the category page I request the category document from the id which is in the route params.注意:我正在使用 Vue,当我路由到类别页面时,我从路由参数中的 id 请求类别文档。

These rules work as expected in the rules playground but not in live situations, and I can't figure out why.这些规则在规则操场上按预期工作,但在实际情况下却行不通,我不明白为什么。 Any help would be greatly appreciated.任何帮助将不胜感激。

UPDATE: My Firestore Data Structure is as follows:更新:我的 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

The other top level root collections have the same rules applied to them in the database rules as do categories.其他顶级根 collections 在数据库规则中应用了与类别相同的规则。 For example:例如:


    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));
    }

I don't think they are affecting this issue since I am not querying them in this case.我认为他们不会影响这个问题,因为在这种情况下我不会查询他们。

Cloud Firestore Security Rules only load the fields from the document which are included in the query. Cloud Firestore 安全规则仅加载文档中包含在查询中的字段。 If you don't include it in a query they're guaranteed to fail (as they will be tested against something undefined).如果您不将其包含在查询中,它们肯定会失败(因为它们将针对未定义的内容进行测试)。

Cloud Firestore Security Rules don't filter data by themselves. Cloud Firestore 安全规则不会自行过滤数据。 They merely enforce that read operations only access documents that are allowed.他们只是强制读取操作只访问允许的文档。 You'll need to add where("private", "==", true) to your query to make it match with your rules.您需要将where("private", "==", true)添加到您的查询中以使其与您的规则相匹配。 See code below:请参阅下面的代码:

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

Just a note : This does require that the document has a private field, otherwise the private can't be empty.请注意:这确实要求文档具有private字段,否则private字段不能为空。 You should add the private field on all the documents.您应该在所有文档上添加private字段。 You can leave your security rules as it is just to enforce that all documents have the private field.您可以保留您的安全规则,因为它只是为了强制所有文档都具有private字段。

As you trigger the query above, the console will require you to create an index through the console's error message.当您触发上面的查询时,控制台将通过控制台的错误消息要求您创建索引。 Error message will look like this:错误消息将如下所示:

[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)]
}

Edit:编辑:

If you do not use simple and compound queries as you mentioned in the comment then your Firestore Rules will be valid and work as you want because you haven't passed any filters to the query which makes it possible for Firestore Security Rules to read all the fieldname and its data from the given document.如果您不使用评论中提到的简单查询和复合查询,那么您的 Firestore 规则将有效并按您的意愿工作,因为您没有将任何过滤器传递给查询,这使得 Firestore 安全规则可以读取所有给定文档中的fieldname及其数据。


You can find more relevant information on these documentation:您可以在这些文档中找到更多相关信息:

You have a double set of && in your read rule:您的read规则中有一组&&

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

Try removing one set of those && .尝试删除其中的一组&& It might be causing the weirdness?这可能是造成怪异的原因?

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

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

相关问题 Firestore 规则“允许模拟读取”但在浏览器中失败 - Firestore rules 'Simulated read allowed' but fails in browser Firestore 规则:使集合中的文档和所有子集合遵循相同的规则 - Firestore Rules: Making documents in a collection and all subcollections follow the same rule Firestore 安全规则:get() 返回与预期结果不同的结果 - Firestore security rules: get() returns different result than the expected one 我用于获取多个文档的 Firestore 安全规则不起作用 - My firestore sucurity rule for getting multiple documents doesn't work Firebase Firestore 规则子集合 - Firebase Firestore Rules Subcollections 使用 IN 查询的 Firestore 安全规则 - Firestore Security Rules for Query with IN Firestore 规则是否区分大小写或我的路径是否包含错误? - Are firestore rules case sensitive or does my path contain an error? AWS S3 Static 站点有多个路由规则应用时,哪个规则优先 - Which rule has priority when multiple routing rules apply for AWS S3 Static Site 如果我的应用程序调用云 function 并且我根据 Firestore 用户信息验证请求,这是否与 Firestore 安全规则一样安全? - If my app calls a cloud function and I validate the request against Firestore user info, is this just as secure as a Firestore security rule? 在 Firestore 规则中运行循环 - Run Loop in firestore rules
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM