[为了我的一生,我不明白为什么以下内容会导致允许写入的false
。假设我的users
集合是空的,并且正在从Angular前端编写以下形式的文档:
{
displayName: 'FooBar',
email: 'foo.bar@example.com'
}
我当前的安全规则:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
function isAdmin() {
return resource.data.role == 'ADMIN';
}
function isEditingRole() {
return request.resource.data.role != null;
}
function isEditingOwnRole() {
return isOwnDocument() && isEditingRole();
}
function isOwnDocument() {
return request.auth.uid == userId;
}
allow read: if isOwnDocument() || isAdmin();
allow write: if !isEditingOwnRole() && (isOwnDocument() || isAdmin());
}
}
}
通常,我希望没有用户能够编辑自己的角色。普通用户可以编辑自己的文档,否则管理员可以编辑任何人的文档。
将isEditingRole()
插入false
会得到预期的结果,所以我将其缩小为该表达式。
写入不断返回假,我无法确定原因。任何想法或修复都会有所帮助!
编辑1
我尝试过的事情:
function isEditingRole() {
return request.resource.data.keys().hasAny(['role']);
}
和
function isEditingRole() {
return 'role' in request.resource.data;
}
和
function isEditingRole() {
return 'role' in request.resource.data.keys();
}
编辑2
请注意,最终管理员将为用户设置角色,因此角色最终可能存在于文档中。这意味着,根据下面的Firestore docs,即使原始请求中没有该请求,该请求也将具有role
键。
资源中存在的请求中未提供的字段将添加到
request.resource.data
。规则可以通过比较request.resource.data.foo
与resource.data.foo
来测试字段是否被修改,知道即使resource
中的每个字段也都存在于request.resource
中,即使它不是在写入请求中提交的。
据此,我认为排除了“编辑1”中的三个选项。我确实尝试过request.resource.data.role != resource.data.role
的建议,但也没有用...我很茫然,开始怀疑Firestore中是否确实存在错误。
如果您创建自定义函数来检查更新,则您的规则将更具可读性和可维护性。例如:
service cloud.firestore {
match /databases/{database}/documents {
function isUpdatingField(fieldName) {
return (!fieldName in resource.data && fieldName in request.resource.data) || resource.data[fieldName] != request.resource.data[fieldName];
}
match /users/{userId} {
//read rules here...
allow write: if !isUpdatingField("role") && !isUpdatingField("adminOnlyAttribute");
}
}
}
我使用writeFields
解决了。请尝试此规则。
allow write: if !('role' in request.writeFields);
就我而言,我使用list
来限制更新字段。它也有效。
allow update: if !(['leader', '_created'] in request.writeFields);
所以最后,我似乎以为resource.data.nonExistentField == null
会在实际返回false
时返回Error
(根据this和我的测试)。所以我原来的解决方案可能已经遇到了。令人费解的是,相反的内容应根据the docs起作用,但也许文档所指的是“不存在”的值,而不是键-细微的区别。]
我仍然没有100%的清晰度,但这是我最终成功的原因:
function isAddingRole() {
return !('role' in resource.data) && 'role' in request.resource.data;
}
function isChangingRole() {
return 'role' in resource.data && 'role' in request.resource.data && resource.data.role != request.resource.data.role;
}
function isEditingRole() {
return isAddingRole() || isChangingRole();
}
[令我困惑的另一件事是,根据文档,我不需要&& 'role' in request.resource.data
中的isChangingRole()
部分,因为它应该由Firestore自动插入。尽管事实并非如此,但删除它会导致我的写入因权限问题而失败。
[可能通过将写入分为create
,update
和delete
部分,而不只是allow write: if !isEditingOwnRole() && (isOwnDocument() || isAdmin());
而得到澄清/改进。
由于文档中对writeFields的引用已经消失,所以我不得不想出一种新的方法来完成我们对writeFields的处理。
使用此功能,您可以检查是否/未创建/修改字段。
汤姆·贝利(https://stackoverflow.com/a/48177722/5727205)的解决方案确实很有希望。
这看起来似乎是致命的,但是对于更新文档,其中您可能具有非用户生成的其他字段,例如。角色,创建的角色等。您需要可以测试这些字段是否不变的功能。因此满足这三个FN。