It is unclear what the value of $key is expected to be. So I'm going to assume it's just some random string that isn't used in the rules.
For each security rule, the current data of the node is accessible using the predefined variable data. ".write" and ".validate" rules also have access to the to-be-written data as newData. These are both RuleDataSnapshot objects.
Assuming that a user making changes must be the user given by the uid property, the following rules can be used.
"rules": {
"users": {
"$key": {
".read": "auth != null && data.child('uid').val() == auth.uid",
".write": "auth != null && ((data.exists() && data.child('uid').val() == auth.uid) || (!data.exists() && newData.child('uid').val() == auth.uid)"
}
}
}
The above rules use a fail-fast approach. If a user is not logged in, the check aborts. Otherwise, the user's ID is matched against the existing data at the given node. If the data doesn't yet exist, the newly updated data must also match the current user's ID.
In case the "cafe" role is important, the following rules also require that the "cafe" is set to true to allow read/write operations.
"rules": {
"users": {
"$key": {
".read": "auth != null && data.child('uid').val() == auth.uid && data.child('role/cafe').val() == true",
".write": "auth != null && ((data.exists() && data.child('uid').val() == auth.uid && data.child('role/cafe').val() == true) || (!data.exists() && newData.child('uid').val() == auth.uid && newData.child('role/cafe') == true))"
}
}
}
Note: If $key is storing user info/data, I highly recommend using the user's ID as the key as security rules cannot perform queries like "does userid have admin role?" without structuring your data to allow it. If $key is meant to be a username, instead use a username-to-userID map as it will prevent future problems. One such example is if a user wants to change their username, you can remove and link the new username to the user without ever having to move all their data.
$key? How do you get the value of$keywhen you want to look up a user's data (such as in another rule)? For these reasons, I highly recommend against making it anything other than the user ID and using lookup indexes (e.g. username: uid). - samthecodingman