对于我的生活,我无法理解为什么以下内容导致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
中的每个字段也会出现在request.resource.data.role != resource.data.role
中,即使它未在写请求。
根据这一点,我认为来自"编辑1"被排除在外。我确实尝试了version
的建议,而且这种建议也没有...我不知所措,我开始怀疑Firestore中是否真的存在错误。
答案 0 :(得分:4)
我使用writeFields
解决了这个问题。请尝试这条规则。
allow write: if !('role' in request.writeFields);
就我而言,我使用list
来限制更新字段。它也有效。
allow update: if !(['leader', '_created'] in request.writeFields);
答案 1 :(得分:3)
如果您创建自定义函数来检查更新,那么您的规则将更具可读性和可维护性。例如:
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");
}
}
}
答案 2 :(得分:2)
所以最后,似乎我假设$bookings = $wpdb->get_var("SELECT COUNT(*) FROM wp_frm_item_metas WHERE field_id=12 AND meta_value='$select_date'");
会返回resource.data.nonExistentField == null
,当它实际返回false
时(根据this和我的测试)。所以我原来的解决方案可能已经遇到了。这是令人费解的,因为相反应该根据the docs起作用,但也许文档指的是一个值不存在"而不是键 - 一个微妙的区别。
我仍然没有100%的清晰度,但这是我最终的结果:
Error
另一件让我感到困惑的事情是,根据文档,我不应该需要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
部分,因为它应该由Firestore自动插入。虽然这似乎并非如此,但删除它会导致我的写入因权限问题而失败。
可以通过将写入分成isChangingRole()
,create
和update
部分而不仅仅是delete
来澄清/改进。
答案 3 :(得分:2)
由于文档中对writeFields的引用已经消失,所以我不得不想出一种新方法来完成我们对writeFields的处理。
function isSameProperty(request, resource, key) {
return request.resource.data[key] == resource.data[key]
}
match /myCollection/{id} {
// before version !request.writeFields.hasAny(['property1','property2','property3', 'property4']);
allow update: isSameProperty(request, resource, 'property1')
&& isSameProperty(request, resource, 'property2')
&& isSameProperty(request, resource, 'property3')
&& isSameProperty(request, resource, 'property4')
}
答案 4 :(得分:2)
汤姆·贝利(https://stackoverflow.com/a/48177722/5727205)的解决方案确实很有希望。
但是在我的情况下,我需要防止对字段进行编辑,并且可能会有这样的情况,即该字段根本不存在于现有数据中。因此,我添加了一个检查字段是否存在。
此解决方案会检查两项检查:
ALTER DATABASE mydatabase SET MULTI_USER
答案 5 :(得分:1)
使用此单一功能,您可以检查是否/未创建/修改字段。
function incomingDataHasFields(fields) {
return ((
request.writeFields == null
&& request.resource.data.keys().hasAll(fields)
) || (
request.writeFields != null
&& request.writeFields.hasAll(fields)
));
}
用法:
match /xxx/{xxx} {
allow create:
if incomingDataHasFields(['foo']) // allow creating a document that contains 'foo' field
&& !incomingDataHasFields(['bar', 'baz']); // but don't allow 'bar' and 'baz' fields to be created
答案 6 :(得分:1)
这看起来似乎是致命的,但是对于更新文档,您可能拥有其他非用户生成的字段,例如。角色,创建的角色等。您需要可以测试这些字段不变的功能。因此,满足这三个FN。
function hasOnlyFields(fields) {
if request.resource.data.keys().hasOnly(fields)
}
function hasNotChanged(fields) {
return (fields.size() < 1 || equals(fields[0]))
&& (fields.size() < 2 || equals(fields[1]))
&& (fields.size() < 3 || equals(fields[2]))
&& (fields.size() < 4 || equals(fields[3]))
&& (fields.size() < 5 || equals(fields[4]))
&& (fields.size() < 6 || equals(fields[5]))
&& (fields.size() < 7 || equals(fields[6]))
&& (fields.size() < 8 || equals(fields[7]))
&& (fields.size() < 9 || equals(fields[8]))
}
function equals(field) {
return field in request.resource.data && field in resource.data && request.resource.data[field] == request.resource.data[field]
}
因此,在说用户文档更新时,用户只能更新其姓名,年龄和地址,而不能更新角色和电子邮件,您可以这样做:
allow update: if hasOnlyFields(['name', 'age', 'address']) && hasNotChanged(['email', 'roles'])
请注意hasNotChanged最多可以检查9个字段。同样,这些也不是您要做的唯一检查。您还需要检查文档的类型和所有权。
答案 7 :(得分:1)
发现此规则非常有效:
function propChanged(key) {
// Prop changed if key in req but not res, or if key req and res have same value
return (
(key in request.resource.data) && !(key in resource.data)
) || (
(key in request.resource.data) && request.resource.data[key] != resource.data[key]
);
}
答案 8 :(得分:1)
这是一个不会触发安全规则错误的函数,例如“对象上的属性名称未定义”。
功能/属性:
function notUpdated(key) {
return !(key in request.resource.data)
|| (
(key in resource.data)
&& request.resource.data[key] == resource.data[key]
);
}
说明
1:如果request.resource.data
上不存在该字段,则意味着该字段既不在请求中也不在现有资源中。 (请记住,request.resource.data
代表成功写入操作之后的资源,即“未来”文档。)如果该字段在任何地方都不存在,则允许写入。
2:如果输入资源或现有资源上都存在字段 ,则需要再次检查。首先,检查现有资源上是否存在该字段。如果没有,更新将被拒绝。如果是这样,请继续检查请求字段是否等于现有字段。如果它们相等,则允许写入。此时request.resource.data[field]
不可能触发参考错误;如果该字段出现在resource.data
上,那么它也出现在request.resource.data
上。