Firebase read permissions - firebase

So I've set up a simple db/web form to collect some user data. Rn I am trying to figure out the rules thing but I am running into this problem - if my read flag is set to true then I can simply run this in the console
var ref = firebase.database().ref();
ref.on("value", function(snapshot) {
console.log(snapshot.val());
}, function (error) {
console.log("Error: " + error.code);
});
and expose the users which should not be the possibility. If I set read to false then I cant access the DB upfront to validate if email address is unique or not. I guess I need to achieve 2 things:
Prevent db snooping through the dev tools running any snippets
Make sure email address is unique.
p.s. My currents rules (prevent delete, prevent read, make sure POST request has certain fields):
{
"rules": {
"users": {
".read": false,
"$uid": {
".write": "!data.exists()",
".validate": "newData.hasChildren(['first_name', 'last_name', 'email', 'country', 'amount'])"
}
}
}
}

To avoid duplicates you will want a validation check in the DB rather than reading data from the client and checking (you can't trust the client).
Since there is no easy way to check for duplicate child values in a firebase base collection, you will need a separate collection to track emails and then validate your emails against that, i.e.:
{
"rules": {
"users": {
".read": false,
"$uid": {
".write": "!data.exists()",
".validate": "newData.hasChildren(['first_name', 'last_name', 'email', 'country', 'amount'])",
"email": {
".validate": "!root.child('emails').child(newData.val()).exists()"
}
}
},
"emails": {
".read": false,
".write": "!data.exists()"
}
}
}
You will then need to write the users' emails to the email collection as the users are added, e.g.:
var ref = firebase.database().ref('emails/'+email);
ref.set(uid)

Related

Couldn't read firebase child

Users can able to data when they login in. But I want everyone can able to this child.No matter logged in.
Child is "gunluksifreler"
Here are my rules
{
"rules": {
"Homeland": {
".indexOn": ["username","email","bakiyetl","yarismada","yarismadabb","splashmesaj","uygulama1tut","uygulama2tut","uygulama3tut","uygulama4tut","uygulama5tut","uygulama6tut","uygulama7tut","uygulama8tut","uygulama9tut","uygulama10tut"]
},
"Odultalepleri": {
".indexOn": ["username","odul1"]
},
"Yardim": {
".indexOn": ["id"]
},
"gunluksifreler": {
".read": true, // <-- allows every person
".write": true
},
"Devices": {
".read": true, // <-- allows every person
".write": true
},
".read": "auth !== null", // <-- allows read if logged in
".write": "auth !== null" // <-- allows write if logged in
}
}
It can be read for everyone when I set true last read line
".read": true, // <-- allows read everbody
".write": "auth !== null" // <-- allows write if logged in
But this time everyone can read every child. What I'm missing?
Code
var kullanici = firebase.database().ref();
kullanici.on('value' ,function(datasnapshot) {
if(datasnapshot.hasChild("gunluksifreler")){
alert("yes");
}});
But it's not about the code it's about the rules.
If you don't want anonymous users to be able to read the entire database, you should not allow "read": true at the root level in your security rules.
Then if you want to allow everyone to read kullanici, you should allow "read": true on that node in your rules.
So this part of your original rules looks fine to me:
{
"rules": {
"gunluksifreler": {
".read": true
},
".read": "auth !== null"
}
}
I highly recommend focusing the rules/code in your question similarly in the future, as it ensures we're both looking at the same minimal-but-complete fragment.
The problem is not in the rules, but in your code:
var kullanici = firebase.database().ref();
kullanici.on('value', ...
This is trying to read the root of the database, which the rules explicitly disallow. So this read gets rejected.
A good way to remember this is that Firebase security rules don't on their own filter any data. They merely check whether the read you're trying to do is allows. So the read above tries to read the root, which is not allowed. Firebase doesn't check every child node of the root to only return allowed child data, as the performance of that would be hard to guarantee.
If you want read the kullanici node, you should attach your listener to only that node:
var kullanici = firebase.database().ref("kullanici");
kullanici.on('value', ...

Can I set one child to be .write: true whilst others are restricted?

I have an object called 'Service' This can be read by anyone but changes can only be made by the owner. My firebase rule for this is:-
"Service": {
".read": true,
"$uid": {
".write": data.child('owner').val() == auth.uid"
}
}
Service has a child called 'owner' which is == to the users UI when logging in through firebase Auth.
I also have a User object which can make a Service as a favourite. I'm saving these in the user object as an array of [businessKey:true].
However, I've been asked to also save in the Service object the reverse relation of what users have favourited it. So an array However as a user isn't always the owner of a service I come into a permissions error. I'm trying to write rules that allow anyone to write to one child but not the others.
I have tried...
"Service": {
".read": true,
"$uid": {
".write": data.child('owner').val() == auth.uid" || data.child('users').val() != root.child('users').val()"
}
}
this is would allow a write if the user was the Service owner or just changing the child 'users' The effect was that any user was able to write to anything.
I've also tried
"Service": {
".read": true,
"$uid": {
"$users": {
".write" : true,
},
".write": "data.child('owner').val() == auth.uid"
}
}
thinking this would always allow a write to 'users' and any other child if user was owner.
I'm pretty new to firebase and this rules syntax in general so I'm probably making some glaring error! If what I want to achieve possible? what have I got wrong?

Firebase Database Rules: can't write to database

I tried to set some important write persmissions but I can't solve my problem. I got told that, if I add a write-rule to room, then I overwrite my room/$roomID/ingame rule.
What I'm trying to do is
Creating a room by auth users.
Set/update ingame of a room only by the creator of the room. (That works)
Rules:
{
"rules": {
".read": true,
"user": {
".indexOn": "displayname"
},
"room": {
"$roomID": {
"ingame":{
".write": "data.parent().child('creatorUid').val() == auth.uid"
}
}
}
}
}
How I call to create a new room:
let user = firebase.auth().currentUser
dbRoomRef.push().then((room) => {
room.set({
creatorUid: user.uid,
ingame: false,
})
}).catch(function(err) {
console.log(err.message)
}
)
Error message (as expected):
FIREBASE WARNING: set at /room/-L572bnuRv0_vntko-Bd failed: permission_denied
Thank you.
The error messages says that you're trying to write /room/-L572bnuRv0_vntko-Bd and have no permission to write there. That is correct, since your rules only give permission to write to /room/-L572bnuRv0_vntko-Bd/ingame.
If creatorUid is already set when you create the room, you don't have to include it in your write statement and can just do:
room.child("ingame").set(false);
If you're trying to allow everyone to create a new room (or write to an existing room) as long as they are the owner, you need to set your rules one level higher:
"room": {
"$roomID": {
".write": "newData.child('creatorUid').val() == auth.uid"
}
}

excluding children from a firebase node with read access

I'm creating a simple chat application using firebase and am running into some issues with the available security settings.
The data model for this application is very simple and is as follows
rooms:[
people:[
{
name: //string
status: // what the user is doing, typing, still connected etc.
secret: // the problem is with this
}
],
messages:[
{/* message to and payload*/}
]
]
the issue is that I only want the user that created the rooms[i].people[j] to be able to update the status of that person.
Being new to firebase I though I would be able to use the update function as follows
personRef.update({
'status': // newStatus
'secret': // used to authorize the update
})
the problem with this is I can't find any way to make the secret write only and give access to the people at the same time. That is I need anyone to be able to pull the data located at rooms[i].people - meaning rooms[i].people would have to have ".read":true (in firebases security rules). But this would give read access to every child and anyone in the room would be able to see any one else's update secret. I'm I thinking of this problem incorrectly?
Is there a way to give read access to a parent but exclude some of the children from the results?
Thanks!
It depends a bit on how you're using the secret to implement authorization, but I suspect denormalizing your data is going to do the trick. Try something like this:
people-secrets:[
<user's ID>: {
secret:
}, ...
],
rooms:[
people:[
{
name: //string
status: // what the user is doing, typing, still connected etc.
}
],
messages:[
{/* message to and payload*/}
]
]
That would allow you to segment the security rules:
{
"rules": {
"people-secrets": {
"$user_id": {
".read": "$user_id === auth.uid",
".write": "$user_id === auth.uid"
}
},
"rooms": {
"$room_id": {
"$user_id": {
".read": "auth.uid != null",
".write": "$user_id === auth.uid && root.child('people-secrets/' + auth.uid + "/secret") === <that token>"
}
}
}

Firebase security - newData() as a parameter of hasChildren() expression

I want allow users to create their account only if they have a token(invitation).
In my firebase Security i have:
{
"rules": {
".read": false,
".write": false,
"users": {
// allow to write if in the invitations there is child equal to token from newData()
".write":"root.child('invitations').hasChild(newData.child('token').val())",
},
"invitations":{
"$invitation":{
".read": "true"
}
}
}
}
as is in the comment i want allow to write if in the invitations there is child equal to token from newData().
From Simulator:
Attempt to write {"token":"evl6yky3vi0pmn29","name":"John"} to /users/99 with auth=null
/:.write: "false"
=> false
/users:.write: "root.child('invitations').hasChild(newData.child('token').val())"
6:52: hasChild() expects a string argument.
=> false
/users/99:<no rules>
how should I do this?
You've almost got it perfect. The only flaw here is that you're trying to write to users/99, but you've put the rule on users/.
Presumably, you meant this:
"users": {
"$user_id": {
// allow to write if in the invitations there is child equal to token from newData()
".write":"root.child('invitations').hasChild(newData.child('token').val())",
}
},

Resources