I'm trying to display data only for vip users. I use the code below, but every user can saw the data anyway.
I want to hide actors data from normal users and display for vip users.
"tvshow1": {
".indexOn": ["actors"],
".read": "data.child('users').child(auth.uid).child('VIP').val() == 'Yes'",
".write": "data.child('users').child(auth.uid).child('VIP').val() == 'Yes'"
},
Database structure:
tvshow1
| title:
| date:
| actors:
Members
|id:
|VIP:
You need to use "root"
".read": "root.child('users').child(auth.uid).child('VIP').val() == 'Yes'"
same goes for write
Related
The following is the database structure and i want to make sure the owner of the comment whose user_id is part of the comment object to have read and write access to the comment and all other users have read access to comment and the ability to like it to increase like_count:
The following is the security rule I came up with:
{
"rules": {
"comments": {
".read": "root.child('users').child(auth.uid).val() != null",
".write": "(newData.parent().child('users').child(auth.uid).val() != null && newData.parent().child('comments').hasChildren().hasChildren().child('user_id').val() == auth.uid)",
"$commentId": {
".read": "root.child('users').child(auth.uid).val() != null",
".write": "(newData.parent().parent().child('users').child(auth.uid).val() != null && !newData.parent().parent().child('comments').hasChildren().hasChildren().child('like_count'))"
}
}
}
}
So, for read better will be:
".read": "root.child('users').hasChild(auth.uid)"
".write" is more complicated:
You need to only allow edit in like_count, and you need to allow for only one like per user
Proper way to do this will be expand structure of your "post", something like like_list, here or in other place to protect data transfer from growing when post will be download by client
So in "comment" node you want to allow every auth user to .write and .read, they will be able to add new comment then (upper rules will be fine for this).
For $commentId node rules will look like:
".write":".data.child('user_id').val() == auth.uid"
You can link like_list to a clinet by adding user_id to the "like / $ commentId" node and setting listener in firebase funtions for this node. adding new user_id will fire "write" event and then call function to secure increase the value of like_count.
You can archive it with a firebase-function and transaction.
https://www.tutorialspoint.com/firebase/firebase_write_transactional_data.htm
If you really don`t want to change schema then you need to secure every single child of comment, like:
"$commentID":{
"comment":{ ".write":"".data.parent().child('user_id').val() == auth.uid" ),
...//things allowed to edit by owner
"like_count":{
".write":"root.child('users').hasChild(auth.uid)",
".validate":"newData.val() == data.val() + 1"
}
}
But this will not secure post from giving more than one like per user.
I have a database node called (people) that looks like this:
people
|
|
-------UserID1 //which is a random id
| |
| |
| ----UserId2 //which is a random id
| |
| |
| name:"some_name"
| id:"UserId2"
| image:"image_url"
|
|
|
-------UserId2
|
|
----UserId3
|
|
name:"some_name"
id:"UserId3"
image:"image_url"
If we look at the (people / UserID1 / UserId2) node :
Since UserId1 and UserId2 are 2 random ids, then if we want to write a rule to UserId2 we will notice that it is 2 random id level deep.
What I want is to write a rule at this specified path that says these:
1) people / UserId1 : can be written by (UserID1) and (UserId2).
2) people / UserId1 : can be read by (UserID1) and (UserId2).
3) people / UserId1 / UserId2 : must end up with a newData that has (name, id, image).
How do I do this?
Thanks.
Due to the way Firebase Realtime Database rules cascade into deeper keys, allowing people/UserId1 to be writable by UserId2 is not advised, as this would allow UserId2 write access to the data of other users stored under people/UserId1 like people/UserId1/UserId3.
But using this trait, we can "add" users that are allowed read & write permissions as we go deeper into the data structure.
So the new conditions are:
people/UserId1 - UserId1 has read & write access
people/UserId1/UserId2 - UserId2 has read & write access
people/UserId1/UserId2 - must always contain 'name', 'id' and 'image' keys
people/UserId1/UserId3 - cannot be read/written by UserId2
{
"rules": {
"people": {
"$userId1": {
"$userId2": {
".read": "auth.uid == $userId2", // add $userId2 to those granted read permission, cascades into deeper keys
".write": "auth.uid == $userId2", // add $userId2 to those granted write permission, cascades into deeper keys
".validate": "newData.hasChildren(['name', 'id', 'image'])" // any new data must have 'name', 'id' and 'image' fields.
},
".read": "auth.uid == $userId1", // add $userId1 to those granted read permission, cascades into deeper keys
".write": "auth.uid == $userId1" // add $userId1 to those granted write permission, cascades into deeper keys
}
}
}
Lastly, if it is also required that people/UserId1/UserId2/id is equal to UserId2, you can change the ".validate" rule to enforce this:
".validate": "newData.hasChildren(['name', 'id', 'image']) && newData.child('id').val() == $userId2" // any new data must have 'name', 'id' and 'image' fields and 'id' must have a value of $userId2
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.
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.
I'm trying to use Firebase as the backend for a corporate situation where there will be clients who need to have access limited to the content they create; and employees who need to be able to look at all data. Is that possible in Firebase. I could not immediately see how to create such a rule if, say, I had an array of firebase uIds of employees.
Yes, this is definitely possible in Firebase.
Given a simple Firebase structure:
users
uid_0
name: "Rupert"
uid_1
name: "Buffy"
uid_2
name: "Zoran"
admins
uid_2: true
data
uid_0: "something interesting"
uid_1: "look ma, no hands"
uid_2: "Beam me up"
and rules
"data": {
"$uid": {
".read": "auth != null && ( $uid == auth.uid || root.child('admins').child(auth.uid).exists() )"
".write": "auth != null && ( $uid == auth.uid || root.child('admins').child(auth.uid).exists() )"
}
}
This rule will ensure the user is
1) authenticated and
2) the node they are accessing has their uid as a key
or
their uid exists in the admins node.
So Buffy who is uid_1 can only access "look ma, no hands" and Zoran with uid_2 (an admin) can access any node.
This probably could be simplified but it demonstrates the concept.
The Firebase documentation has some recipes one of them shows a specific example of role based security implementation
I trying to verify if an user profile has an specific property in order to allow .write data in a path of my Firebase, but I haven't found the way.
This is the user profiles structure in my Firebase:
root
|--user_profiles
| |--uid
| | |--name
| | |--email
| | |--invite // How could I reach the properties of this path with my .validate rules?
And these are the rules I'm trying to make it work:
"invitations": {
".write": true,
".validate": "auth.provider == 'provider' && auth.id == '123456'",
"$invitation": {
".read": true,
"used": {
".validate": "root.child('user_profiles').hasChild(auth.uid).(...)" // I'm stuck here.
}
}
}
The idea is to allow .write in "used" if, and only if, the property "invite" match with my requirements (Whether they are equal or not, for example).
I'm really stuck in this and I don't want to make it works without understand it.
You can simply concatenate the paths to the invite:
".validate": "root.child('user_profiles/'+auth.uid+'/invite').val() === ???"
Replacing ??? with your criterium, such as data.val() (for the current value at this path), or newData.val() (for the value being set at this path).