Search code examples
firebase-realtime-databasefirebase-security

Realtime Database rules to allow parameters to be written by one user but one parameter be written by everyone


I have the following structure in my database:

{
   "UserData":{
      "123456789":{
         "Nickname":"bestUser",
         "RecieveUpdates":"YES",
         "Status":"157",
      }
   }
}

uid = 123456789

I want rules to achieve the following:

  • All authenticated users can create a new record in UserData.
  • Only the owner of the record(auth.uid = uid) can delete the record.
  • Only the owner of the record(auth.uid = uid) can edit all parameters(Nickname, RecieveUpdates, Status).
  • All authenticated users can edit the Status parameter of any user.

Currently the rules i have are as follows:

{
   "rules":{
      "UserData":{
         "$uid":{
            ".read":"auth != null",
            ".write":"auth != null && (!newData.exists() && $uid == auth.uid) || (!data.exists() && newData.exists())",
            "Nickname":{
               ".write":"$uid == auth.uid",
               ".validate":true
            },
            "RecieveUpdates":{
               ".write":"$uid == auth.uid",
               ".validate":true
            },
            "Status":{
               ".write":"auth != null",
               ".validate":true
            },
            "$other":{
               ".validate":false
            }
         }
      }
   }
}

I am using these rules with the following logic:

  • (!newData.exists() && $uid == auth.uid) -> Allows only the owner to delete the record.
  • (!data.exists() && newData.exists()) -> Allows any authenticated user to create a record.
  • ".write":"auth != null" -> Allows any authenticated user to edit.
  • ".write":"$uid == auth.uid" -> Allows only the owner to edit.

It seems that users succesfully create/delete records.
But the "per parameters" rules(Nickname...) can also be edited by any user.
Probably because the main node's rules are overpowering the "per parameters" rules.

It seems that the solution would be using a combination of newData.exists/data.exists but i can't figure in which way.


Solution

  • In Firebase security rules, permission cascades down. So once you give a user permission to perform a certain action on a node, you cannot take that permission away on a lower-level node in the same branch. If you want something like that, you should typically have multiple top-level nodes: one for the private parts, and one for the shared parts.


    But in your case, it seems that:

    • A user can only write (so create or modify) their own node.
    • Except that any user can write another user's Status property.

    It seems to me that you can implement that with:

    {
       "rules":{
          "UserData":{
             "$uid":{
                ".read":"auth != null",
                ".write":"auth != null && $uid == auth.uid",
                "Nickname":{
                   ".validate":true
                },
                "RecieveUpdates":{
                   ".validate":true
                },
                "Status":{
                   ".write":"auth != null",
                   ".validate":true
                },
                "$other":{
                   ".validate":false
                }
             }
          }
       }
    }