2

I am trying to make my read rules working in RTDB as follow :

{
  "rules": {
    "test": {
      "$uid": {
        ".read": "data.exists()",
      }
    },
  }
}

Here is the data in test node :

{
  "test" : {
    "anyuid" : {
      "content" : {
        "uid1" : "gnjdssd",
        "uid2" : "fnvdjkcx"
      },
      "at" : "2491795"
    }
  }
}

Here is my code that is denied (actually tested with and without orderByChild but in my real query i need the orderByChild so if this changes anything in the rule except the .indexOn i prefer to specify it) :

FirebaseDatabase.instance.ref().child("test").orderByChild("at").get()

The log : I/RepoOperation(29129): get for query /test falling back to disk cache after error: Permission denied

My goal is to allow read if there is a real condition such as :

data.child('content/'+auth.uid).exists()

But the data.exists() is is not reached in this case.

What i am missing here and how can i implement the rule i want please ?

EDIT :

my data structure is :

{
  "chats": {
    "$chatID": {
      members: {
        "userID1": true,
        "userID2": true,
      }
    }
  }
}

I want to allow userID1 to read only chats where his id is actually in the member map of the chat.

Tom3652
  • 2,540
  • 3
  • 19
  • 45

1 Answers1

3

Your code tries to read from /test, but if we look at the security rules you shared, nobody has permission to read from that node. Keep in mind that rules are not filters, but instead merely ensure that each operation does no more than what is allowed.

If you want to stuck to your current rules, you'll need to know the key of the test node you want to read:

FirebaseDatabase.instance.ref().child("test").child("uidOfUser").get()

If you want to allow the query in your code, you'll need to allow reading from /test:

{
  "rules": {
    "test": {
      ".read": "data.exists()",
    },
  }
}

Note though that this ".read": "data.exists()" is a noop, as there's no security risk in allowing someone to read data that doesn't exist.


Update based on your edit showing the chats structure. If you want to allow the user to read a chat if they are a member, you can do that with:

{
  "rules": {
    "chats": {
      "$chatid": {
        ".read": "data.child('members').child(auth.uid).exists()"
      }
    },
  }
}

There is no way to securely allow reading from /chats in this case though. To allow a user to find their own chats and read them securely, you'll need to add another, inverted data structure mapping the UIDs to their chat IDs. For more on this, see:

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thanks for the answer Frank i understand ! However how can i secure the access when i try to work with list of data ? Because it's actually what i want to achieve by querying "/test" followed by an `orderBy` clause and a `limitToFirst` probably too. I can edit the question with the real data in the node, because unfortunately it is not a user ID as child of `/test`, but it's a `testID` and i want the user to be allowed to read it if under testID there is the `userID` – Tom3652 Feb 26 '22 at 15:25
  • 2
    I'm not sure I understand, but are you looking for https://firebase.google.com/docs/database/security/rules-conditions#query-based_rules? – Frank van Puffelen Feb 26 '22 at 15:52
  • Yes thank you, actually i think there is maybe an issue with my data structure if i can't apply the rules that easily. I will edit the question to let you know my real use case – Tom3652 Feb 26 '22 at 15:58
  • That's quite different, but I added an update for that too. – Frank van Puffelen Feb 26 '22 at 16:27
  • Thanks for the update, but it's permission denied :(. I was doing this myself : `data.child('members/'+auth.uid).exists()` but also got permission denied while reading. That's why i came up with the `/test` structure to simplify things and understand how `data` is populated. When i use rules playground to test this structure, i have an error but no `details` from Firebase console... So it seems in my humble opinion that `.read` is too deep in the node when the query asks `/chat`, it seems it doesn't find any `.read` condition at this node level :/ – Tom3652 Feb 26 '22 at 16:53
  • Thanks for the links you provided, i actually have an inverted data structure like this but it's in another instance of RTDB. I am sharding my databases with the blaze plan and user info are stored in my `main shard`. So i can't (I believe) make security rules across different database instances – Tom3652 Feb 26 '22 at 16:57
  • I will ask another question regarding my whole databases structures and security rules. This question is only about making the `data.child('members').child(auth.uid).exists()` working, and i have checked twice that the auth.uid is indeed in the data :/. However, if i put the `data.exists()` above the `$chatID` it works, but inside it doesn't work. – Tom3652 Feb 26 '22 at 17:26