javascript - 如何通过 Firestore 规则检查数组类型的文档字段是否已包含值?
问题描述
给定文档类型:
interface Post {
content: string;
sharedBy: Array<string>;
}
要强制执行的约束是允许用户仅将其连接uid
到sharedBy
数组一次。
此外,在我的单元测试中,在最初的位置创建了一个post.sharedBy
文档[]
。因此,第一次更新成功,并且 的值由下面sharedBy
的['1']
查询确认('1'
是一个测试uid
)。
之后,执行完全相同的更新,但是它也成功了,而不是失败。
鉴于以下规则:
function canShare() {
return !resource.data.sharedBy.hasAny([request.auth.uid]); // also attempted with `hasAll`
}
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
match /posts/{postId} {
allow read: if true;
allow write: if request.auth != null && canShare();
}
}
}
为什么上面提到的第二次更新成功而不是失败?
编辑
以下是测试文件的内容:
import {
initializeTestApp,
initializeAdminApp,
assertSucceeds,
assertFails,
} from '@firebase/testing';
const projectId = 'projectId';
const myId = '1';
const myAuth = { uid: myId };
const getFirestore = (auth?: typeof myAuth) =>
initializeTestApp({
auth,
projectId,
}).firestore();
const getAdminFirestore = () =>
initializeAdminApp({
projectId,
}).firestore();
const myPost = {
content:
'Lorem ipsum dolor sit amet consectetur, adipisicing elit. Minus, doloribus praesentium nulla pariatur esse laborum a in obcaecati expedita recusandae.',
sharedBy: [],
};
const getMyPost = () => getAdminFirestore().collection('posts').add(myPost);
describe('App tests', () => {
test("user can increase any post's share count by 1 only once", async () => {
const db = getFirestore(myAuth);
const { id } = await getMyPost();
console.log((await db.collection('posts').doc(id).get()).data()!.sharedBy); // []
await assertSucceeds(
db
.collection('posts')
.doc(id)
.update({ sharedBy: [myId] }),
);
console.log((await db.collection('posts').doc(id).get()).data()!.sharedBy); // ['1']
await assertFails(
// error
db
.collection('posts')
.doc(id)
.update({ sharedBy: [myId] }),
);
});
});
解决方案
我认为目前安全规则中没有任何方法可以强制数组只包含唯一值。虽然客户端可以通过使用运算符来确保是这种情况array-union
,但恶意客户端可以添加重复条目。
在 Firestore 中确保唯一性的唯一方法是确保值是键。所以你可以制作sharedBy
一个地图,其中的关键是用户的 UID。这样可以保证每个 UID 只出现一次,因为映射中的键根据定义是唯一的。
另一种选择是将sharedBy
元素存储在子集合中,并且也使用 UID 作为文档 ID。这样,系统再次确保 ID 是唯一的。
最后一个选项是只允许sharedBy
通过 Cloud Function 或其他一些受信任的进程更新数组,您的代码可以在其中强制执行此业务规则。
推荐阅读
- facebook - 在网站上嵌入数十条来自 Facebook iframe 的评论
- arrays - 如何过滤掉 Gerrit JSON api 响应的前几个坏字符
- ios - 用于 UIContextMenuConfiguration 的 iOS 13.0 UIMenu 和 UIAction
- java - 如何在我的布局中滚动时查看所有图像
- html - 如何向上移动导航栏,以便顶部栏之间没有太多空间?
- c - GDB 一直在第 7 行设置断点,而不是我指定的行
- java - Spring Security 通过注册用户注册另一个用户
- python - django-heroku 安装报错
- .net-traceprocessing - 使用 TraceProcessor 处理 ETW 跟踪时如何允许丢失事件和时间倒置
- r - 在 R Sweave 中不可能用 biblatex 引用