Perhaps I'm tackling this problem too much from an SQL kind of perspective, but I'm having troubles understanding how to properly restrict which children should be allowed to populate a node.
Say that I want to keep a record of products with arbitrary names. Each product must contain a price, but nothing else is allowed.
My naive approach was to add a .validate rule to the products requiring newData to contain a price child, explicitly granting write access to the price node and then removing all access an $other node (somewhat like a default clause in a switch statement):
{
    "rules": {
        "$product": {
            ".read": true,
            ".write": true,
            ".validate": "newData.hasChildren(['price'])",
            "price": {
                ".write": true,
                ".validate": "newData.isNumber()"
            },
            "$other": {
                ".read.": false,
                ".write": false,
            }
        }
    }
}
This does not work. Adding a new product with {"price": 1234, "foo": "bar"} will still be accepted. If I however add a  (I did that wrong, somehow.)".validate": false rule to $other, nothing is accepted instead (e.g. {"price": 1234} is not allowed).
Is there some way to implement something similar to what I'm trying to do here? If not, what is the proper way of restricting a data structure in Firebase? Should I do it at all? What stops the user from filling my database with trash if I don't?
You're falling into a few common Firebase security pits here. The most common one is that permission cascades down: once you've granted read or write permission on a certain level in the tree, you cannot take that permission away at a lower level.
That means that these rules are ineffectual (since you've granted read/write one level higher already):
"$other": {
    ".read.": false,
    ".write": false,
}
To solve the problem you must realize that .validate rules are different: data is only considered valid when all validation rules are met. So you can reject the $other data with a validation rules:
{
    "rules": {
        "$product": {
            ".read": true,
            ".write": true,
            ".validate": "newData.hasChildren(['price'])",
            "price": {
                ".validate": "newData.isNumber()"
            },
            "$other": {
                ".validate": false
            }
        }
    }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With