I have this Firebase DB which could be changed if necessary:
The JSON of that DB is:
{
\"groups\": {
\"1\": {
\"name\": \"G1\",
What if you try nested rules for the "points" and "visits" levels:
"groups": {
"$groupId": {
".read": "root.child('userGroups').child(auth.uid).child($groupId).exists()",
".write": "root.child('userGroups').child(auth.uid).child($groupId).val() == 'admin'",
"points": {
".write": "root.child('userGroups').child(auth.uid).child($groupId).val() != 'readonly'"
},
"visits": {
".write": "root.child('userGroups').child(auth.uid).child($groupId).val() != 'readonly'"
}
}
},
"users": {
"$userId": {
".read": "auth != null",
".write": "auth != null &&
$userId === auth.uid &&
newData.val() != null"
}
},
"userGroups": {
"$userId": {
".read": "auth != null",
".write": "auth != null &&
data.child(auth.uid).val() === 'admin' &&
newData.val() != null"
}
}
Firebaser here. Expect this answer to be updated as I go along.
My first step is to move the rules for the specific child nodes into that specific child node. That removes the parent()
problem you've been having. First iteration is:
"groups": {
"$groupId": {
".read": "root.child('userGroups').child(auth.uid).child($groupId).exists()",
"points": {
".write": "root.child('userGroups').child(auth.uid).child($groupId).val() !== 'readonly'"
}
}
},
This allows user h3KYDXkPQrY246w6Y6NXIanVoNS2
to write to /groups/1/points
(of which the user is an admin), but not to /groups/2/points
(to which the user only has readonly access).
A next step is to make the rule more generic. To do this I introduce a $child
variable, which matches any node under the group:
"groups": {
"$groupId": {
".read": "root.child('userGroups').child(auth.uid).child($groupId).exists()",
"$child": {
".write": "root.child('userGroups').child(auth.uid).child($groupId).val() !== 'readonly'
|| ($child !== 'points' && $child !== 'visits')"
}
}
This allows user h3KYDXkPQrY246w6Y6NXIanVoNS2
to write to /groups/2/name
(which is writeable by any group member), but not to /groups/2/points
(to which the user only has readonly access).
Update: apparently I inverted your logic above, so here's my final take:
"groups": {
"$groupId": {
".read": "root.child('userGroups').child(auth.uid).child($groupId).exists()",
".write": "root.child('userGroups').child(auth.uid).child($groupId).val() == 'admin'",
"$child": {
".write": "root.child('userGroups').child(auth.uid).child($groupId).val() === 'readwrite'
&& ($child !== 'points' || $child !== 'visits')"
}
}
With this user h3KYDXkPQrY246w6Y6NXIanVoNS2
:
/groups/1/name
because they're admin of group 1/groups/2/points
because they're admin of group 1/groups/2/name
because they're not an admin of group 2/groups/2/points
because they're a readwrite member of group 2