1

I have the following Firebase database:

enter image description here

I would like to add database rules to protect the that only the appropriate users can access their messages.

When I add the following, I get an error:

enter image description here

Please can anyone advise?

Thanks

UPDATE

findMessages(chatItem: any): Observable<any[]> { // populates the firelist
    return this.af.database.list('/message/', {
        query: {
            orderByChild: 'negativtimestamp'
        }
    }).map(items => {
        const filtered = items.filter(
            item => ((item.memberId1 === chatItem.memberId1 && item.memberId2 === chatItem.memberId2)
                || (item.memberId1 === chatItem.memberId2 && item.memberId2 === chatItem.memberId1))
        );
        return filtered;
    });
}
Richard
  • 8,193
  • 28
  • 107
  • 228
  • well the system dont know what variable memberId1/2, its just a wild card. you should have this members saved in list of users / in the message, etc, etc and then refer it – yotam hadas Mar 30 '17 at 06:17

2 Answers2

6

Within the .read rule's expression, the variables that begin with $ represent keys in the path.

However, the member IDs are not keys; they are data members. To use them in the expression, you can use the data snapshot's child and val methods. Like this:

{
  "rules": {
    "message": {
      "$key": {
        ".read": "data.child('memberId1').val() === auth.uid || data.child('memberId2').val() === auth.uid"
      }
    }
  }
}
cartant
  • 57,105
  • 17
  • 163
  • 197
  • This saves into the Firebase Rules, but when I run my app, I get the error below. My user is authenticated with Firebase, and does have a correct `uid`. `EXCEPTION: Uncaught (in promise): Error: permission_denied at /message: Client doesn't have permission to access the desired data. Error: permission_denied at /message: Client doesn't have permission to access the desired data.` – Richard Mar 30 '17 at 06:52
  • You will need additional rules for the `chat` path. This answer relates only to the `message` path that was mentioned in the question; it does not contain a complete set of rules. You will need to add `chat` to the rules JSON - with an appropriate `.read`, etc. – cartant Mar 30 '17 at 06:56
  • Thanks, yes, I have added the equivalent `chat` rules. I thin this error is related to my `auth`, because if I have the following rule, then I get the error intermittently: `auth != null` – Richard Mar 30 '17 at 06:58
  • Be aware that you cannot read the list of `messages` with the rule. You can only read individual messages of which the authenticated user is a member. This is usually referred to as not being able to use rules as filters and is the expected behaviour. See http://stackoverflow.com/a/14298525/6680611 – cartant Mar 30 '17 at 07:03
  • Yes, that's the behaviour I want. If I try access an another persons message, it gets the above error. The filter is being done on the `Observable` in the app itself. The rules are to stop someone from hacking the app, and getting access to someone else's messages. – Richard Mar 30 '17 at 07:07
  • Perhaps, that's my problem? I have made an update above. Perhaps, even though I have a filter on the Observable, it is initially still trying to access unauthorised data (i.e. It is still querying Firebase for all memberId's)? – Richard Mar 30 '17 at 07:08
1

You should have variables that define the members and then refer to the variables.

here is chat example from Firebase security docs.

{
  "rules": {
    "room_names": {
      // any logged in user can get a list of room names
      ".read": "auth !== null",
  "$room_id": {
    // this is just for documenting the structure of rooms, since
    // they are read-only and no write rule allows this to be set
    ".validate": "newData.isString()"
  }
},

"members": {
   // I can join or leave any room (otherwise it would be a boring demo)
   // I can have a different name in each room just for fun
   "$room_id": {
      // any member can read the list of member names
      ".read": "data.child(auth.uid).exists()",

      // room must already exist to add a member
      ".validate": "root.child('room_names/'+$room_id).exists()",

      "$user_id": {
         ".write": "auth.uid === $user_id",
         ".validate": "newData.isString() && newData.val().length > 0 && newData.val().length < 20"
      }
   }
},

"messages": {
  "$room_id": {
    // the list of messages for a room can be read by any member
    ".read": "root.child('members/'+$room_id+'/'+auth.uid).exists()",

    // room we want to write a message to must be valid
    ".validate": "root.child('room_names/'+$room_id).exists()",

    "$message_id": {
      // a new message can be created if it does not exist, but it
      // cannot be modified or deleted
      // any member of a room can write a new message
      ".write": "root.child('members/'+$room_id+'/'+auth.uid).exists() && !data.exists() && newData.exists()",

      // the room attribute must be a valid key in room_names/ (the room must exist)
      // the object to write must have a name, message, and timestamp
      ".validate": "newData.hasChildren(['user', 'message', 'timestamp'])",

      // the message must be written by logged in user
      "user": {
         ".validate": "newData.val() === auth.uid"
      },

      // the message must be longer than 0 chars and less than 50
      "message": { ".validate": "newData.isString() && newData.val().length > 0 && newData.val().length < 50" },

      // messages cannot be added in the past or the future
      // clients should use firebase.database.ServerValue.TIMESTAMP
      // to ensure accurate timestamps
      "timestamp": { ".validate": "newData.val() <= now" },

      // no other fields can be included in a message
      "$other": { ".validate": false }
    }
  }
}

} }

yotam hadas
  • 702
  • 3
  • 14