da1lbi3
da1lbi3

Reputation: 4519

Allow delete and write with Firebase Rules

I am using Firebase Realtime Database. And I have a structure to prevent to many children. I have 2 keys. One for the max(max_people), and one that contains how many children there are (registered_people).

My structure:

enter image description here

I have the max_people key. This is the max children that can be in the approved key. Registered_people is counting how many childs there in the approved key.

This is my security rule:

"registrations": {
     "approved": {
        ".write": "(root.child('/agenda/activitys').child($room_id).child('max_people').val() > root.child('/agenda/activitys').child($room_id).child('registered_people').val())"
}

What I do at my firebase rules, is that I check if there not are to many rows. If there are to many I block the writing. This works as expected.

But now the problem, the max_people is for example 20, and the registered_people is also 20. No writes can happen. Because I have declared that. But how can I allow a delete than. Because I want to delete at every moment.

So in short, I want to delete no matter what. And I want to write with my current rule.

Some more information:

I add data to the key with the following code:

let data = [
    "full_name": "test",
    "email_addres": "test",
]

Database.database().reference().child("agenda/activitys/" + event + "/registrations/approved").child(Auth.auth().currentUser!.uid).setValue(data) { (error, ref) in
            print("got from subscribing \(String(describing: error))")
            if((error) != nil){
                completionHandler(Result.failure(ApiError.noMorePlaces))
                return
            } else {
                completionHandler(Result.success("succes \(event)"))
                return
            }
        }

Adding works as expected.

And I delete data with the following code:

Database.database().reference().child("agenda/activitys/" + event + "/registrations/approved").child(Auth.auth().currentUser!.uid).removeValue { (error, ref) in
            if((error) != nil){
                completionHandler(Result.failure(ApiError.unknownError))
                return
            } else {
                completionHandler(Result.success("succes \(event)"))
                return
            }
        }

Deleting does not work as expected. I get a permission_denied error when there are for example a max of 20 keys. And that max is fully used (so I have 20 keys with data under the approved child).

Error:

[Firebase/Database][I-RDB038012] setValue: or removeValue: at /agenda/activitys/Jumping Fitness/registrations/approved/exCnADF43AdFUzGsi0GllEsZJZY2 failed: permission_denied

However deleting works when there a less than the max amount of keys.

Edit: I have tested the suggested solution on the firebase rules simulator. I got the simulated write error there also.

I think that I have the solution. I delete on the key, so on the rules I need the key also. My solution:

"registrations": {
  "approved": {
    ".write":  "(root.child('/agenda/activitys').child($room_id).child('max_people').val() > root.child('/agenda/activitys').child($room_id).child('registered_people').val())",
        "$key" : {
           ".write" : "!newData.exists()"
        }
  }

Upvotes: 0

Views: 1343

Answers (1)

Rosário P. Fernandes
Rosário P. Fernandes

Reputation: 11326

When data is being deleted, the newData variable will be null. So you can check if it exists using the exists() method:

"registrations": {
     "approved": {
        ".write": "(root.child('/agenda/activitys').child($room_id).child('max_people').val() > root.child('/agenda/activitys').child($room_id).child('registered_people').val()) || !newData.exists()"
}

Update: After looking at the code you provided, I can see that you do the write operations against the child key. But have in mind that, according to the documentation:

Shallower security rules override rules at deeper paths.

This means that the write rule under "approved" will overwrite the write rule at "$key". So you should join both rules and keep them under the "$key" rule. Like this:

"registrations": {
  "approved": {
        "$key" : {
           ".write" : "(root.child('/agenda/activitys').child($room_id).child('max_people').val() > root.child('/agenda/activitys').child($room_id).child('registered_people').val()) || !newData.exists()"
        }
  }

Upvotes: 2

Related Questions