假设我在用户对象下面有此Firestore,其中包含字段名称,地址和汽车(请注意,唯一的用户是集合)。
user {
name: "John Smith"
address: '123 Firebase Road, Firestore CA, 10000"
cars: {
asfdfsd811r9UAdfasdf1: {
name: "Ford Explorer"
carSold: false,
salesComment: "This is the best SUV in the world"
},
12342342ADSfas! :{
name:" Testla Modal X"
carPrice:false,
salesComment: "This is the best electric car in the world"
}
}
}
我想设置一个安全规则来强制客户端库只能编辑salesComment,但是此用户集合对象中没有其他内容,我该怎么做?我设置了一个如下所示的匹配路径,但::不起作用。您可以设置字段字典的匹配路径,例如在这种情况下,是car吗?match和variableId模式仅仅适用于集合。
service cloud.firestore {
match /databases/{database}/documents {
match /user/{userId} {
match /cars/{carId}/salesComment {
allow write: if request.auth.uid == userId;
}
allow read: if request.auth.uid == userId;
}
答案 0 :(得分:3)
您不能使用内部字段匹配,而必须使用rules.List
,rules.Map
和rules.Set
对象。
请务必注意,规则是静态的,并且不能迭代列表(例如,使用forEach
,map
等)。可以通过使用someList.size() <= position
在执行元素比较之前检查列表是否足够长来克服此问题。不幸的是,必须对此进行硬编码,如下所示。
这些规则的一个目标是,它们应该能够与同一文档中的其他规则结合使用。即应限制“汽车”地图,但您仍应能够更新“名称”和“地址”字段。
在本节中,为了易于理解(例如,包括类型信息),变量将非常冗长。重命名它们以适合您的风格。
免责声明:虽然这套第一套规则有效,但它过于陈旧且过于具体-不建议用于生产环境。
service cloud.firestore {
match /databases/{database}/documents {
// assert no changes or that only "salesComment" was changed
function isCarEditAllowed(afterCarMap, beforeCarMap) {
return afterCarMap.diff(beforeCarMap).affectedKeys().size() == 0
|| afterCarMap.diff(beforeCarMap).affectedKeys().hasOnly(["salesComment"]);
}
// assert that if this car exists that it has allowed changes
function isCarAtPosValid(afterCarsList, beforeCarsList, position) {
return afterCarsList.size() <= position // returns true when car doesn't exist
|| isCarEditAllowed(afterCarsList[position], beforeCarsList[position])
}
function areCarEditsAllowed(afterDataMap, beforeDataMap) {
return afterDataMap.get("cars", false) != false // cars field exists after
&& beforeDataMap.get("cars", false) != false // cars field exists before
&& afterDataMap.cars.size() == beforeDataMap.cars.size() // cars field is same length
&& isCarAtPosValid(afterDataMap.cars, beforeDataMap.cars, 0)
&& isCarAtPosValid(afterDataMap.cars, beforeDataMap.cars, 1)
&& isCarAtPosValid(afterDataMap.cars, beforeDataMap.cars, 2)
&& isCarAtPosValid(afterDataMap.cars, beforeDataMap.cars, 3)
&& isCarAtPosValid(afterDataMap.cars, beforeDataMap.cars, 4)
}
match /carUsers/{userId} {
allow read: if request.auth.uid == userId;
allow write: if request.auth.uid == userId
&& areCarEditsAllowed(request.resource.data, resource.data)
}
}
}
现在上述规则起作用了,可以通过将步骤抽象为一组可重用的自定义函数来改进它们。
service cloud.firestore {
match /databases/{database}/documents {
/* Custom Functions: Restrict map changes */
function mapHasAllowedChanges(afterMap, beforeMap, setOfWhitelistedKeys) {
return afterMap.diff(beforeMap).affectedKeys().size() == 0 // no changes
|| setOfWhitelistedKeys.hasAll(afterMap.diff(beforeMap).affectedKeys()) // only named keys may be changed
}
function mapInListHasAllowedChanges(afterList, beforeList, setOfWhitelistedKeys, position) {
return afterList.size() <= position // returns true when element doesn't exist
|| mapHasAllowedChanges(afterList[position], beforeList[position], setOfWhitelistedKeys)
}
function listOfMapsHasAllowedChanges(afterList, beforeList, setOfWhitelistedKeys) {
return mapInListHasAllowedChanges(afterList, beforeList, setOfWhitelistedKeys, 0)
&& mapInListHasAllowedChanges(afterList, beforeList, setOfWhitelistedKeys, 1)
&& mapInListHasAllowedChanges(afterList, beforeList, setOfWhitelistedKeys, 2)
&& mapInListHasAllowedChanges(afterList, beforeList, setOfWhitelistedKeys, 3)
&& mapInListHasAllowedChanges(afterList, beforeList, setOfWhitelistedKeys, 4)
}
function largeListOfMapsHasAllowedChanges(afterList, beforeList, setOfWhitelistedKeys) {
return mapInListHasAllowedChanges(afterList, beforeList, setOfWhitelistedKeys, 0)
&& mapInListHasAllowedChanges(afterList, beforeList, setOfWhitelistedKeys, 1)
&& mapInListHasAllowedChanges(afterList, beforeList, setOfWhitelistedKeys, 2)
&& mapInListHasAllowedChanges(afterList, beforeList, setOfWhitelistedKeys, 3)
&& mapInListHasAllowedChanges(afterList, beforeList, setOfWhitelistedKeys, 4)
&& mapInListHasAllowedChanges(afterList, beforeList, setOfWhitelistedKeys, 5)
&& mapInListHasAllowedChanges(afterList, beforeList, setOfWhitelistedKeys, 6)
&& mapInListHasAllowedChanges(afterList, beforeList, setOfWhitelistedKeys, 7)
&& mapInListHasAllowedChanges(afterList, beforeList, setOfWhitelistedKeys, 8)
&& mapInListHasAllowedChanges(afterList, beforeList, setOfWhitelistedKeys, 9)
}
function namedListWithSameSizeExists(listPath) {
return request.resource.data.get(listPath, false) != false
&& resource.data.get(listPath, false) != false
&& request.resource.data.get(listPath, {}).size() == resource.data.get(listPath, {}).size()
}
function namedListOfMapsWithSameSizeExistsWithAllowedChanges(listPath, setOfWhitelistedKeys) {
return namedListWithSameSizeExists(listPath)
&& listOfMapsHasAllowedChanges(request.resource.data.get(listPath, {}), resource.data.get(listPath, {}), setOfWhitelistedKeys)
}
/* Rules */
match /carUsers/{userId} {
allow read: if request.auth.uid == userId;
allow write: if request.auth.uid == userId
&& namedListOfMapsWithSameSizeExistsWithAllowedChanges("cars", ["salesComment"].toSet())
}
}
}
注意:以上规则并未断言未删除列入白名单的密钥。为了确保更改后列出的键仍然存在,您需要将mapHasAllowedChanges
函数替换为:
function mapHasAllowedChanges(afterMap, beforeMap, setOfWhitelistedKeys) {
return afterMap.diff(beforeMap).affectedKeys().size() == 0 // no changes
|| (setOfWhitelistedKeys.hasAll(afterMap.diff(beforeMap).affectedKeys()) // only named keys may be changed
&& afterMap.keys().toSet().hasAll(setOfWhitelistedKeys)) // all named keys must exist
}
上述规则非常复杂,如果将汽车移至自己的收藏夹并使用rules.Map#diff
,则可以简化这些规则。
以下代码仅在用户拥有该汽车文档并且仅修改salesComment
键(modify =添加/更改/删除)的情况下才允许进行写操作。
service cloud.firestore {
match /databases/{database}/documents {
match /user/{userId} {
allow read, write: if request.auth.uid == userId;
match /cars/{carId} {
allow read: if request.auth.uid == userId; // Firestore rules don't cascade to subcollections, so this is also needed
allow write: if request.auth.uid == userId
&& request.resource.data.diff(resource.data).affectedKeys().hasOnly(["salesComment"]);
}
}
}
}
如果您要求salesComment
必须在写操作后出现(允许添加/更改-但不能删除),您也可以使用k in x
确保它仍然存在。
service cloud.firestore {
match /databases/{database}/documents {
match /user/{userId} {
allow read, write: if request.auth.uid == userId;
match /cars/{carId} {
allow read: if request.auth.uid == userId; // Firestore rules don't cascade to subcollections, so this is also needed
allow write: if request.auth.uid == userId
&& "salesComment" in request.resource.data
&& request.resource.data.diff(resource.data).affectedKeys().hasOnly(["salesComment"]);
}
}
}
}