Firebase Database Rules to match row

2020-07-18 08:00发布

问题:

I am using a Firebase Realtime Database. I have the following data:

I also have the rules:

{
  "rules": {
      ".read": "auth != null",
      ".write": "auth != null",
      "chat": {
        "$key": {
            ".read": "data.child('memberId1').val() === auth.uid && data.child('memberId2').val() === auth.uid",         
            ".write": "data.child('memberId1').val() === auth.uid || data.child('memberId2').val() === auth.uid"          
        }
      },

The initial rule works perfectly:

      ".read": "auth != null",
      ".write": "auth != null",

Problem

The following 2 rules have no effect.

      "chat": {
        "$key": {
            ".read": "data.child('memberId1').val() === auth.uid && data.child('memberId2').val() === auth.uid",         
            ".write": "data.child('memberId1').val() === auth.uid || data.child('memberId2').val() === auth.uid"          
        }
      },

As you can see, in order to test these rules, in the first rule, I have made an impossible condition of memberId1 and memberId2 both equal to the users uid. As a result I would expect it to fail.

If I remove:

      ".read": "auth != null",
      ".write": "auth != null",

and just have:

      "chat": {
        "$key": {
            ".read": "data.child('memberId1').val() === auth.uid || data.child('memberId2').val() === auth.uid",         
            ".write": "data.child('memberId1').val() === auth.uid || data.child('memberId2').val() === auth.uid"          
        }
      },

Then access is denied. Even if I change it to:

 "data.child('memberId1').val() === 'h6qQg5YfQveTaCyBEXwDMSJPqwk1' 

The following is also denied:

  "chat": {
    "Ko7w9XTtuRVN4p6CMp7": {
        ".read": true,   

Question

How should I structure the rules to allow that a user may only access a row where their uid matches either memberId1 or memberId2?

Thanks

UPDATE

I have the following code:

   findChats(): Observable<any[]> {
        return this.af.database.list('/chat/', {
            query: {
                orderByChild: 'negativtimestamp'
            }
        }).map(items => {
            const filtered = items.filter(
                item => (item.memberId1 === this.me.uid || item.memberId2 === this.me.uid)
            );
            return filtered;
        });
    }

My question is similar to this one. I try the following with no success:

{
  "rules": {
      "chat": {
        "$id": {
            ".read": true
        }
      },

回答1:

Firebase rules are atomic. So if you try to read /chat (and thats what you are currently doing) it will only check the /chat branch rules. Since you dont have any rule in /chat it goes for the default thats is not giving access. Therefore, your rules would only be evaluated in case you were trying to read /chat/chatId.

One possible solution you could go for is to store a list of chats which each user is part of. So you can keep your current chat branch but store another branch in the database with the following structure:

user_chats: {
    uid1: {
        chatId1: true,
        chatId2: false
    }
    uid2: ...
}

And rules:

"user_chats": {
   "$uid": {
       ".read": "auth.uid === $uid",
       ".write": "auth.uid === $uid"
   }
}

Then you could keep your chat rules like you already have them but first get the data from /user_chats/uid and then for each chatId retrieved you you will need to read on chat/chatId.