Reputation: 4519
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:
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
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