Firebase Database rules - wildcard for dynamic key - firebase

I have this usecase where I want to give permission to other users read data under my ID key only if there is a key === to their Id.
Let’s say I have user 123 and 456 who writes json object under “
orders-
I
-123
I
-456
And I want only user 123 and 456 to be able to read the 456 nodes. With the rules that I provide only 123 is able to read the node:
"orders": {
"$uid": {
".read": "auth.uid === $uid || root.child('orders').child($uid).child(auth.uid).exists()",
".write": "auth.uid != null",
}
}
What am I doing wrong here? Can anyone help me with this, please?
EDIT: Two screenshots of the simulator

I solved it setting these rules (note $id is nested in $uid):
{
"rules": {
"orders": {
"$uid": {
".read": "auth.uid === $uid",
".write": "auth.uid === $uid",
"$id": {
".read": "auth.uid === $id",
".write": "auth.uid === $id"
},
}
}
}
}
Previously I tought this line should do the work:
".read": "auth.uid === $uid || root.child('orders').child($uid).child(auth.uid).exists()",

Related

Firebase Database Rules to match row

I am using a Firebase Realtime Database. I have the following data:
I also have the rules:
{
"rules": {
".read": "auth != null",
".write": "auth != null",
"chat": {
"$key": {
".read": "data.child('memberId1').val() === auth.uid && data.child('memberId2').val() === auth.uid",
".write": "data.child('memberId1').val() === auth.uid || data.child('memberId2').val() === auth.uid"
}
},
The initial rule works perfectly:
".read": "auth != null",
".write": "auth != null",
Problem
The following 2 rules have no effect.
"chat": {
"$key": {
".read": "data.child('memberId1').val() === auth.uid && data.child('memberId2').val() === auth.uid",
".write": "data.child('memberId1').val() === auth.uid || data.child('memberId2').val() === auth.uid"
}
},
As you can see, in order to test these rules, in the first rule, I have made an impossible condition of memberId1 and memberId2 both equal to the users uid. As a result I would expect it to fail.
If I remove:
".read": "auth != null",
".write": "auth != null",
and just have:
"chat": {
"$key": {
".read": "data.child('memberId1').val() === auth.uid || data.child('memberId2').val() === auth.uid",
".write": "data.child('memberId1').val() === auth.uid || data.child('memberId2').val() === auth.uid"
}
},
Then access is denied. Even if I change it to:
"data.child('memberId1').val() === 'h6qQg5YfQveTaCyBEXwDMSJPqwk1'
The following is also denied:
"chat": {
"Ko7w9XTtuRVN4p6CMp7": {
".read": true,
Question
How should I structure the rules to allow that a user may only access a row where their uid matches either memberId1 or memberId2?
Thanks
UPDATE
I have the following code:
findChats(): Observable<any[]> {
return this.af.database.list('/chat/', {
query: {
orderByChild: 'negativtimestamp'
}
}).map(items => {
const filtered = items.filter(
item => (item.memberId1 === this.me.uid || item.memberId2 === this.me.uid)
);
return filtered;
});
}
My question is similar to this one. I try the following with no success:
{
"rules": {
"chat": {
"$id": {
".read": true
}
},
Firebase rules are atomic. So if you try to read /chat (and thats what you are currently doing) it will only check the /chat branch rules. Since you dont have any rule in /chat it goes for the default thats is not giving access. Therefore, your rules would only be evaluated in case you were trying to read /chat/chatId.
One possible solution you could go for is to store a list of chats which each user is part of. So you can keep your current chat branch but store another branch in the database with the following structure:
user_chats: {
uid1: {
chatId1: true,
chatId2: false
}
uid2: ...
}
And rules:
"user_chats": {
"$uid": {
".read": "auth.uid === $uid",
".write": "auth.uid === $uid"
}
}
Then you could keep your chat rules like you already have them but first get the data from /user_chats/uid and then for each chatId retrieved you you will need to read on chat/chatId.

Firebase user permission

I have two mega parent nodes, but I am currently unable to write to the second one.
-users
uid
some other info
uid
-secondParent
child
uid
child
uid
I would like the user to be able to write to his own child of secondParent. Read and write permissions are working correctly for the users parent node, but I can't get it working for the secondParent.
The rule I have currently is:
{
"rules": {
"users": {
"$uid": {
".read": "auth != null",
".write": "$uid === auth.uid",
}
},
"parentdNode": {
"$childID":{
".read" : "auth != null",
"$uid":{
".write": "$uid === auth.uid",
}
}
}
}
}
Simulation failure below:
You have to write rules for each parents to enable permission
{
"rules": {
"users": {
"$uid": {
".read": "auth != null",
".write": "$uid === auth.uid",
}
},
"parentdNode": {
"$childID":{
"$uid":{
".read" : "auth != null",
".write": "$uid === auth.uid",
}
}
}
}
}
According to the screenshot, you are trying to write {"uid":"X","property":"Y"} to /parentNode/someNode. The rules give permission to write {"property":"Y"} to /parentNode/someNode/65ef..aa62.
JSON trees come with key/value pairs. There is no key uid in the scheme you presented, there is a $uid which is replaced by the a value from the path you are accessing.
It may still be a bit confusing, but I hope this points to clarifying ideas.

How prevent retrieve a child in same parent node?

I want to read all usernames from users parent node, because I'm using search feature in my app (if provided searchActive: true child in users node). But email need to be reachable only by owner.
I have just tried like below, but I'm still getting email. I'm worried about security not only email, What I'm missing and How Can I organize all of them?
"rules": {
"users": {
".read": "auth !== null",
"$uid": {
".write": "auth !== null && auth.uid === $uid",
".read": "auth !== null && auth.uid === $uid",
"username": {
".validate": "
!root.child('usernames').child(newData.val()).exists() ||
root.child('usernames').child(newData.val()).val() == $uid"
},
"email": {
".read": "auth.uid == 'facebook:'+$uid || auth.uid == $uid"
}
}
},
"usernames": {
".write": "auth !== null",
".read": "auth !== null"
},
First the reason why you can still access email is because rules cascade meaning when you set read to true for the parent node all the children can also be read. In your case:
"users": {
//Read is being set to true here for everything in this node
".read": "auth !== null",
"$uid": {
".write": "auth !== null && auth.uid === $uid",
//This will be ignored, since read was allowed already
".read": "auth !== null && auth.uid === $uid",
"username": {
".validate": "
!root.child('usernames').child(newData.val()).exists() ||
root.child('usernames').child(newData.val()).val() == $uid"
},
"email": {
//This will be ignored, since read was allowed already
".read": "auth.uid == 'facebook:'+$uid || auth.uid == $uid"
}
}
},
"usernames": {
".write": "auth !== null",
".read": "auth !== null"
},
I suggest you take some time to read all the documentation about firebase security. It can really help you avoid situations like this and perhaps give you some idea's about implementing a good security for your case.
A possible solution is to use a seperate username node where you store all the usernames for your search feature. You can use the rules to make sure everyone can read it but only the owner of a specific username can change it.

Security rules for posting to a user's subdirectory in Firebase

The following are my Firebase security rules:
security-rules.json
{
"rules": {
"users": {
"$uid": {
".write": "auth.uid === $uid",
".read": "auth.uid === $uid"
}
}
}
}
It works fine when my path ends with the users directory. As in:
https://my-firebase.firebaseio.com/users/my-user-id.json
But when I try to post directly to a subdirectory, as follows:
https://my-firebase.firebaseio.com/users/my-user-id/settings.json
it doesn't work.
Question
What do I need to do to the security-rules.json file (or anything else) to be able to write directly to a user's subdirectory?
Edit:
Someone suggested, "just extend your rule to include settings." So I tried this:
security-rules.json
{
"rules": {
"users": {
"$uid": {
".write": "auth.uid === $uid",
".read": "auth.uid === $uid"
},
"settings": {
".write": "auth.uid === $uid",
".read": "auth.uid === $uid"
}
}
}
}
Which throws the following error:
9:30: Unknown variable '$uid'.
10:31: Unknown variable '$uid'.
This works in the simulator:
{
"rules": {
"users": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid",
"settings": {
}
}
}
}
}
After further testing, I found the security rules contained in the OP also work in the simulator:
security-rules.json
{
"rules": {
"users": {
"$uid": {
".write": "auth.uid === $uid",
".read": "auth.uid === $uid"
}
}
}
}
There is no need to add additional rules for writing deeper into the node tree. The highest level permissions are sufficient.
Aside: My problem appears to be something other than the security rules I'm using. I must do more research, experimentation and testing.

Access a node/path by two specific users only

I've read up and tried denormalization and while it makes perfect sense for cases like comments/messages that are accessible publicly while a user can only write to his own path/node, I'm having a hard time restricting ".read" rule to the user/owner AND to another user/admin. My use case doesn't publicly post all messages. To expound, for example, messages by user1 are only readable by user1 and admin, while still retaining write-only to user1.
How is this achieved? In security rules, I tried:
"messages": {
".read": "auth !== null",
".write": "auth !== null",
"$message": {
".read": "data.child('userID').val() === auth.uid"
}
}
OR
"messages": {
".write": "auth !== null",
"$message": {
".read": "data.child('userID').val() === auth.uid"
}
}
OR
"messages": {
"$user_id": {
".read": "auth.uid === $user_id"
".write": "auth.uid === $user_id"
}
}
While the last one does restrict reading and writing to the authenticated user, say user1, I had no luck getting the admin user to get all users' messages. The first, I can't circumvent the cascading/top-down rule.
I'm a firebase newbie so if this is really simple to do, I appreciate any helping hand.
Thanks!
This depends on how you define an administrator.
Let's first say that your admin is a known user with uid SOF.
"messages": {
"$user_id": {
".read": "auth.uid === $user_id || auth.uid == 'SOF'"
".write": "auth.uid === $user_id"
}
}
If you want the administrator to be configurable, you'll probably store their ID somewhere else, say in a node called administrators:
administrators
SOF: true
Frank: true
messages
...
In that case your can check if the current uid is either in the current node or it exists in the list of administrators:
"messages": {
"$user_id": {
".read": "auth.uid === $user_id || root.child('administrators/'+auth.uid).exists()"
".write": "auth.uid === $user_id"
}
}
Update
Since you want administrators to be able to read all messages, you'll end up with:
"messages": {
".read": "root.child('administrators/'+auth.uid).exists()",
"$user_id": {
".read": "auth.uid === $user_id",
".write": "auth.uid === $user_id"
}
}

Resources