Reputation: 3986
I have a problem that I have found information on various websites or forums, but I cannot find a solution that works for me.
I have this realtime database structure in firebase:
I want to apply security rules. Applying the following rules, only correctly authenticated users can obtain the information.
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
So far, everything works correctly. Logically, it indicates that any authenticated user can access any information
Now i want the authenticated user to only access their nodes.
Based on the information I've found, this should be easy using these rules:
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
However, I always get the error:
W / SyncTree: Listen at / failed: DatabaseError: Permission denied
This is the connection made from the android code:
database.addListenerForSingleValueEvent(object :
ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val path = dataSnapshot.child(App.res.getString(R.string.word_user))
.child(prefs.user_id.toString()).child("expenses").children
for (snapshot in path) {
...
I don't understand where the problem is.
I know the request is made to the "expenses" field, but being included inside the "users" field, I suppose there should be no problem.
As I have said, I have read a lot about it, but I cannot get it to work.
Thanks in advance
Upvotes: 1
Views: 322
Reputation: 598648
From the error message it looks like you're attaching a listener to the root of the database. When you try to do this, Firebase checks if you have permission to read the root. Your rules grant nobody permission to read the root of the database, so the read is rejects.
It is important to realize that rules don't filter data, but they merely enforce that all access to the data follows the rules. Since you only grant access to /users/$uid
, you can only attach a listener to /users/$uid
or lower.
The solution is to move the path building that you now do inside onDataChange
to just outside of that:
database
.child(App.res.getString(R.string.word_user))
.child(prefs.user_id.toString())
.child("expenses")
.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (snapshot in dataSnapshot.children) {
...
This not only ensures your code is allowed to read the data, but also means it tries to download much less data (which saves both you and your users money and time).
Also see:
Upvotes: 2