Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase database rules – `data.exists()` always seems to be true, possible bug?

I am trying to secure my firebase database to allow the creation of new records, but not allow the deletion of existing records. Ultimately, I plan to utilise Firebase authentication in my app as well, and allow users to update existing records if they are the author, but I am trying to get the simple case working first.

However! No matter what I try in the database rules simulator, despite what the documentation seems to suggest, the value of data.exists() seems to always be true. From what I what I can understand from the documentation, the variable data represents a record in the database as it did before an operation took-place. That is to say, for creates, data would not exist, and for updates/deletes, data would refer to a real record that exists in the database. This does not seem to be the case, to the point where I am actually suspecting a bug in Firebase, as when setting the following rules on my database, all write operations are disallowed:

{
  "rules": {
    ".read": true,
    ".write": "!data.exists()"
  }
}

No matter what values I put into the simulator, be it Location or Data. I have even written a small EmberJS app to verify if the Simulator is telling the truth and it too, is denied permission for all write operations.

I really have no idea where to go from here as I am pretty much out of things to try. I tried deleting all records from my database, which lets the simulator think it can perform write operations, but my test app still gets PERMISSION_DENIED, so I don't know what's causing inconsistencies there.

Is my understanding of the predefined data variable correct? If so, why can't I write the rules I want? I have seen snippets literally trying to achieve my "create only, no-delete" rule that seem to line up with my understanding.

Last note: I am trying this in a totally new Firebase project with JUST the rules above, and only ~a few records of junk data laying around my database.

like image 461
Peabnuts123 Avatar asked Jan 31 '23 01:01

Peabnuts123


1 Answers

Because you have placed the !data.exists() at the root location of your database, data refers to the entire database. You will only be able to write to the database when it is completely empty.

You indicate that you run your tests with only a few records of junk data laying around my database. Those records will cause data.exists() to be true.

You can achieve your goal by placing the !data.exists() rule in your tree at the specific location where you want to require that no data already exists. This is typically done at a location with a wildcard key, as in the example you linked:

{
  "rules": {
    // default rules are false if not specified

    "posts": {
      ".read": true, // everyone can read all posts

      "$postId": {
        // a new post can be created if it does not exist
        // existing posts can only be edited by their original "author"
        ".write": "!data.exists() && newData.exists() || data.child('author').val() == auth.uid",
        ".validate": "newData.hasChildren(['title', 'author', 'timestamp'])",
      }
    }
  }
}
like image 138
Bob Snyder Avatar answered Feb 13 '23 05:02

Bob Snyder