This is a really involved scenario, so I'm going to walk through it step-by-step. There is a good chance you'll need to make changes to make these rules work for your complete use-case, so I'm hoping that having each step will make it possible for you to tune them yourself.
The first step is to deobfuscate your data structure. I'll instead use this structure to get started:
{
"people" : {
"user1" : {
"domain" : "aol.com",
"likeCount" : 2,
"likers" : {
"user2" : {
"comain" : "aol.com"
},
"user3" : {
"domain" : "aol.com"
}
}
}
}
}
So user1
has two likes, by user2
and user3
both from the same domain.
I highly recommend using meaningful names like this in your database in general, but definitely in questions that you post about it. If people can't easily understand your data model, chances of them helping go down rapidly.
In the above data model, we can ensure that only users from the same domain can like this user with:
"people": {
"$uid": {
"likers": {
"$likerid": {
".write": "data.parent().parent().child('domain').val() == newData.child('domain').val()"
}
}
}
}
With these rules, I've tried two write operations to people/user1/likers/user4
. The first operation succeeds:
{
"domain": "aol.com"
}
The second operation fails:
{
"domain": "gmail.com"
}
We should probably also ensure that a user can only write their own likes, and not for other users. We can do that with:
"people": {
"$uid": {
"likers": {
"$likerid": {
".write": "$likerid == auth.uid &&
data.parent().parent().child('domain').val() == newData.child('domain').val()"
}
}
}
}
Next up, we'll add a rule that allows a user to like someone, only if they haven't liked them before. We'll do that on people/$uid
as we'll need to look at data under likers
and under likesCount
soon.
The rules for the first step are:
"people": {
"$uid": {
".write": "
!data.child('likers').child(auth.uid).exists() && newData.child('likers').child(auth.uid).exists()
",
"likers": {
"$likerid": {
".write": "$likerid == auth.uid &&
data.parent().parent().child('domain').val() == newData.child('domain').val()"
}
}
}
So these rules allow us to write to a user, if we are adding a like that doesn't exist yet. You may need to do additional checks here to allow updates of other child nodes, but for here we'll keep things as simple as possible (as it's already pretty involved).
Finally you want to ensure that the write must also increment the likeCount
, which should be something like this:
"people": {
"$uid": {
".write": "
!data.child('likers').child(auth.uid).exists() && newData.child('likers').child(auth.uid).exists()
&& newData.child('likeCount').val() == data.child('likeCount').val() + 1
",
"likers": {
"$likerid": {
".write": "$likerid == auth.uid &&
data.parent().parent().child('domain').val() == newData.child('domain').val()"
}
}
}
So the new line now checks if thew new data at likeCount
is one higher than its previous value.
I've taken each of the steps above in a test database of my own, and tested in the playground with both positive and negative cases. So while there may be some issues, the basic approach of each step works.
As said this is pretty involved, and it's quite likely that you'll need to make significant changes before it fully works for all your use-cases.