Reputation: 667
I am really confused with Firebase rules and need some help. I googled a lot but actually got only more confused and still don't get it work.
Lets say I have a Firebase db object like this:
root/orders/$id
and inside the $id
, I have a child user_id
:
root/orders/$id/user_id
Now I want a rule which only allow the user to READ his own orders (but not write anymore in existing once, how ever he need to create new once) and additional I want the users which are admins to READ/WRITE all orders at any time.
I come up with this so far
"orders": {
"$id": {
".read": "root.child('orders').child($id).child('user_id').val() == auth.uid || root.child('users').child(auth.uid).child('admin').val() == 'user_is_admin'",
".write": "root.child('users').child(auth.uid).child('admin').val() == 'user_is_admin'"
},
".write": "root.child('users').child(auth.uid).child('admin').val() == 'user_is_admin'",
".read": "root.child('users').child(auth.uid).child('admin').val() == 'user_is_admin'",
".indexOn": ["status", "user_id"]
},
My admins are marked as admins in my user table:
root/users/$id/admin (== user_is_admin)
My intention was for the first part to allow users with the same auth.uid
as the requested /orders/$id/user_id
to read their orders and for admins to read and write. The admin part is working, but my user has no access for some reason.
The second part was for admins to have read/write access to all orders (without specific $id
) which also works fine plus a normal user need the write to CREATE a new order here.
To resume the admin part of my rules works, but the user part does not. 1. my user cant read is own orders 2. my user cant create a new order
I would be really happy if somebody can help me out here.
Upvotes: 0
Views: 1648
Reputation: 58400
The Rules:
The following rules should enforce the policies you've outlined in your question:
"orders": {
"$id": {
".read": "data.child('user_id').val() === auth.uid",
".write": "!data.exists() && newData.child('user_id').val() === auth.uid"
},
".write": "root.child('users').child(auth.uid).child('admin').val() === 'user_is_admin'",
".read": "root.child('users').child(auth.uid).child('admin').val() === 'user_is_admin'",
".indexOn": ["status", "user_id"]
}
Note that Firebase security rules cascade, so once you've granted read/write permissions to admins for orders
, you don't need to repeat the rules for orders/$id
. (Something to remember - although it's not an issue with your rules - is that once you grant a permission on a parent you cannot revoke it on a child.)
The orders/$id/.read
rule uses data.child
to compare the user_id
and the auth.uid
. This is the same as your rule, it's just a little shorter and does not include the admin check (which cascades).
In addition to checking that newData
(the value being written) contains the user_id
, the orders/$id/.write
rule checks to see that data
(the previous value) does not exist. That will allow creates, but will deny updates and deletes.
Orders for Users:
As noted in your comment, it's not possible for a user to query a list of orders under the orders
key, as the user won't have permission to read the orders of other users (they'd need to be readable to be filtered out). You could solve the problem by storing a mapping of orders by user, like this:
"orders": {
"order_1": {
"user_id": "user_1",
...
},
"order_2": {
"user_id": "user_1",
...
},
"order_3": {
"user_id": "user_2",
...
}
},
"ordersByUser": {
"user_1": {
"order_1": true,
"order_2": true
},
"user_2": {
"order_3": true
}
}
You can use Firebase's multi-location updates to make maintaining the mapping less tedious. For example, to add another order for user_1
, you could do this:
firebase.database().ref().update({
"orders/order_4": {
"user_id": "user_1",
...
},
"ordersByUser/user_1/order_4": true
});
This would let users obtain a list of order IDs (and, from those, the orders themselves) and admin users could still obtain a list of all orders, etc.
Also, you should include a rule so that users can only read their own order IDs under ordersByUser
, etc.
Upvotes: 2