为什么我的 Firebase Firestore 安全规则允许对现有文档执行 .set() 操作,但 .set() 缺少字段

Why are my Firebase Firestore Security Rules allowing a .set() operation on an existing document, but the .set() has missing fields

这是我在 Whosebug 上提出的第一个问题,所以如果我遗漏了首选或必需的问题的某些方面,请告诉我。

我的目标是了解为什么当创建的文档缺少下面列出的 firestore 规则所需的字段时,下面显示的 mocha 测试会通过。

Mocha 测试代码(注意,项目 ID 已检查并有效,但此处省略)

const assert = require("assert");
const firebase = require("@firebase/rules-unit-testing");
const { firestore } = require("firebase-admin");
const { debug } = require("console");

const MY_PROJECT_ID = "####";

const myID = "user_abc";
const theirID = "user_def";

const myAuth = {uid: myID, email: "abc@gmail.com"};
const theirAuth = {uid: theirID};

function getFirestore(auth) {
    return firebase.initializeTestApp({projectId: MY_PROJECT_ID, auth: auth}).firestore();
}


function getAdminFirestore() {
    return firebase.initializeAdminApp({projectId: MY_PROJECT_ID}).firestore();
}


beforeEach(async ()=>{
    await firebase.clearFirestoreData({projectId:MY_PROJECT_ID});
});


describe("Our Social App", () => {

    it("Can overwrite an existing post with allowed fields", async()=>{
        const postPath = "/posts/post_123";
        const admin = getAdminFirestore();
        await admin.doc(postPath).set({authorID:myID, content:"content", 
                                       visibility:"private",
                                       headline:"headline"});

        const db = getFirestore(myAuth);
        const docRef = db.doc(postPath);
        await firebase.assertSucceeds(docRef.set({}));
    });

});

after(async ()=>{
    await firebase.clearFirestoreData({projectId:MY_PROJECT_ID});
});

Firestore 规则

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

    function documentFieldsCheckOut(requiredFields, allowedFields){
      let requiredAndAllowed = requiredFields.concat(allowedFields);
      return request.resource.data.keys().hasAll(requiredFields) &&
             request.resource.data.keys().hasOnly(requiredAndAllowed);
    }

    function updateHasOnlyAllowedFields(allowedFields){
      return debug(request.resource.data.keys()).hasOnly(allowedFields);
    }

    match /{document=**} {
      allow read, write: if false;
    }

    match /posts/{postID} {
   
      allow update: if ((resource.data.authorID == request.auth.uid) || userIsModerator()) 
                        && updateHasOnlyAllowedFields(["visibility", "content"]);
      
      allow create: if (request.resource.data.authorID == request.auth.uid) &&
                        documentFieldsCheckOut(["authorID", "content", "visibility", "headline"], 
                                                ["photo", "location", "tags"]);
    }

 }
}

注意:注释掉 await admin.doc(postPath).set({authorID:myID, content:"content", visibility:"private", headline:"headline"}); 会导致测试按预期失败。
此外,在 await firebase.assertSucceeds(docRef.set({})); 中将 .set 更改为 .update 也会导致测试按预期失败。

如果我没理解错的话,你是在问为什么这个调用成功:

const admin = getAdminFirestore();
admin.doc(postPath).set({authorID:myID, content:"content", 
                                   visibility:"private",
                                   headline:"headline"});

使用管理权限的调用会绕过数据库的安全规则。所以不管你的安全规则是什么,这个调用都不会被他们拒绝。


第二次写操作不同:

const db = getFirestore(myAuth);
const docRef = db.doc(postPath);
docRef.set({})

据我所知,这次您要通过非管理员引用,这意味着写入必须遵守安全规则才能被接受。