为什么我的 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({})
据我所知,这次您要通过非管理员引用,这意味着写入必须遵守安全规则才能被接受。
这是我在 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({})
据我所知,这次您要通过非管理员引用,这意味着写入必须遵守安全规则才能被接受。