Firebase Rules - Nested ifs - firebase

I want to do a few nested if statements to achieve that every user's email is verified befor he gets read write access.
But I dont know how to do that because if I write it in
"rules" : {
"read" : "auth.token.email_verified",
"write" : "auth.token.email_verified"
"other_location" :{
"read" : true,
"write": false
}
}
The user will get write in other_location even through I set write to false in other_location.
I don't know why its like that but my simulation showed that.
Can someone help me?

Firebase RTDB Rules cascade from higher tiers down to the more specific ones. If you allow the ability to perform read/write operations on a given key, which in your case is the root of your database, any key nested under that one will share the same allowed read/write permissions even if your nested rule says otherwise.
To overcome this, you can use variables in the key's path to match unnamed locations. By convention, these "other keys" variables are called "$other".
{
"rules": {
"restricted-location": {
".read": true,
".write": false
},
"$other": { // any key not named above at this level
".read": "auth.token.email_verified",
".write": "auth.token.email_verified"
}
}
}

Related

How to add rule in Firebase to prevent read of parent element?

I have a Firebase database that I want to only allow users who have access to that application to be able to read from and write to.
My data structure is like so:
{
"applications": {
"id_1": {
"feature": {
"a": true,
"b": false
},
"users": {
"user_id_1": true,
"user_id_2": true
}
}
}
}
As you can see, each application can have many users who have read/write access.
I only want users in the users object to be able to retrieve that application.
I have rules like so:
{
"rules": {
"applications": {
".read": "auth != null",
".write": "auth != null",
"$appId": {
".write": "data.child('users').child(auth.uid).val() === true",
".read": "data.child('users').child(auth.uid).val() === true"
}
}
}
}
".read": "auth != null", allows any user who is logged in to be able to retrieve all applications. I only want users user_id_1 or user_id_2 to be able to read that application.
In pseudo code, I would do something like this:
{
"rules": {
"applications": {
".read": "only users in `root.applications.$appId.users` can read", // I need to replace `$appId` some how
".write": "auth != null",
"$appId": {
".write": "data.child('users').child(auth.uid).val() === true",
".read": "data.child('users').child(auth.uid).val() === true"
}
}
}
}
How can I restrict it so when user user_id_1 fetches their applications, they only see apps they have access to?
You're hitting a few common problems here, so let's go through them one by one.
1. Security rules can't filter data
You're trying to control in your rule on /applications what application will be returned when a user tries to read that node. Unfortunately that is not possible, because security rules grant all-or-nothing access.
So either the user has access to /applications (and all data under it), or they don't have access to it. You can't set a rule on /applications to grant them access to some child nodes.
In the documentation, this is referred to as rules are not filters and the fact that permission cascades.
2. Avoid nesting data
You're trying to grant access to /applications, but then store two types of data under there for each application. In cases like that, it is usually better to store each type of data as its own top-level list.
So in your case, that'd be:
{
"application_features": {
"id_1": {
"a": true,
"b": false
},
},
"application_users": {
"id_1": {
"user_id_1": true,
"user_id_2": true
}
}
}
This allows you to grant separate access permissions for the application users and its features. While it means you'll have to read from both branches to get all information of each user, the performance difference there is negligible as Firebase pipelines those requests over a single socket
For controlling access and the most scalable data structure, Firebase recommends that you avoid nesting data and flatten your data structure.
3. Model the data in your database to reflect the screens of your app
Since granting anyone access on /applications gives them access to all data under that, you'll likely need another place to store the list of applications for each user.
I usually make this list explicit in my databases, as another top-level list:
{
...
"user_applications": {
"user_id_1": {
"id_1": true
},
"user_id_2": {
"id_1": true
}
}
}
So now when you want to show the list of applications for the current user, you load the IDs from /user_applications/$uid and then look up the additional information for each app with extra calls (which in turn can be pipelined again).
This one is not in the documentation, but a common pattern with NoSQL databases. I recommend checking out my answers to Many to Many relationship in Firebase and Firebase query if child of child contains a value.

Firebase Database rules ignore child rule

I am trying to add a simple rule to firebase database. the child node activities have proper access but the leads don't. if I sent the parent read true and then child rules are ignored. how can I set parent true to public but child restricted?
{
"rules": {
"leads": {
"$activity": {
".read":
"root.child('users_business_activities').
child(auth.uid).hasChild(data.child('category').val())",
".write" :
"root.child('users').child(auth.uid).child('isAdmin').val() == true"
}
},
"users": {
"$uid": {
".read": "auth.uid == true",
".write": "auth.uid == $uid"
}
},
"business_activities": {
".read": "auth.uid == true",
".write": "false"
}
}
}
In the Firebase Realtime Database permissions cascade downwards. Once you give a user read permission on /leads, you can't take that permission away lower in the JSON tree.
This has a few consequences:
Security rules cannot be used to filter data. This is known as rules are not filters in the Firebase documentation. I also recommend reading some of the many existing questions on this topic.
You will often need to create a lookup list of the activity IDs that a user has access to. Such a lookup list is often known as an "index" in Firebase terms. The documentation on creating scalable data structures has a good example of such a structure.
If part of the data for an activity/lead needs to be publicly readable, and part needs to remain private, you'll want to split the public and private parts into separate top-level nodes. This splitting is known as flattening you data structure in the docs. I also gave an answer here to show how to use this for user profile information.

Firebase rules - Allow one user top-level write and other users to write to own child nodes

I'm having a hard time figuring out how the Firebase rules should be in order for me to restrict the reading/writing as I'd like.
I'm working on a queue system in Firebase, where the structure is as follows
"eventQueues": {
"event1": {
"owner": "simplelogin:1",
"queue": {
"simplelogin:2": {"someinfo": "..."},
"simplelogin:3": {"someinfo": "..."},
"simplelogin:4": {"someinfo": "..."}
}
},
"event2": {...},
...
}
I would like to achieve the following:
Anybody can read the data
".read": true
Any user can create (and edit any part of) an event and but has to be the owner of it.
I believe this can be achieved by:
".write" : "auth != null &&
(data.child('owner').val() == auth.uid ||
newData.child('owner').val() == auth.uid)"
Any user can add their own entry to the queue of an event but may not change anything else in the queue, nor remove themselves from the queue.
This is where I am having trouble, and the best I have been able to come up with is the following (which will be combined with the write-rule above)
".write": "!data.child(auth.uid).exists()"
But as far as I can tell, this still allows the users to write to anything but the queue-entry corresponding to their own uid.
So can I in any way make a rule that allows non-event-owners only to add themselves to the queue, and nothing else?

How do I give read/write access to all nodes with Firebase rules?

Default setting gives full access:
{
"rules": {
".read": true,
".write": true
}
}
For the sake of testing my understanding of rule-writing from the Firebase guide and documentation, I'm (now retreating to) trying to achieve the same results by writing rules for the 4 parent nodes.
If it makes a difference, the first two nodes only have values, no children. **Sidequest: are they still called nodes?
The rules below cause the same behavior as when the rules above are changed to false read and write.
{
"rules": {
"myNode1": {
".read" : true,
".write" : true
},
"myNode2" : {
".read" : true,
".write" : true
},
"myNode3" : {
".read" : true,
".write" : true
},
"myNode4" : {
".read" : true,
".write" : true
}
}
}
What is wrong with my rules?
UPDATE/context:
I have an authDataCallback that stops running here (within the if (authData) { clause):
var ref = new Firebase("https://<my>.firebaseio.com")
ref.once("value", function(snapshot){
Found that if I change the ref var to something more specific: var ref = new Firebase("https://<my>.firebaseio.com/myNode1"), the authDataCallback runs in entirety.
Surely it won't be necessary to produce snapshots of the entire database; this was a confused way of getting the data I need. I've updated, but I'm still confused about why the rules held up the callback considering I gave read and write to the whole database.
I think this is the answer based on the info provided:
The two sets of rules you posted are different. The first set
{
"rules": {
".read": true,
".write": true
}
}
allows everyone read and write access to every node, including the parent node, within your firebase at
https://<my>.firebaseio.com
The second set of rules allows everyone access to specific nodes within your firebase reference but blocks access to all other nodes including access to the parent node. In the update the code is trying to read the parent node which has no rules defined for it so by default read and write are false.
So say you have the following structure:
https://whirlygig.firebaseio.com
Whirlygig
someRandomData: "3.141"
otherRandomData: "6.02"
myNode1
firstName: "first"
lastName: "last"
myNode2
first: "first"
last: "last"
With your first set of rules, everyone can access someRandomData as well as the myNode1, myNode2 etc.
With the second set of rules, everyone can access ONLY myNode1 and myNode2 but cannot access someRandomData

Firebase: Permission denied on demo example [duplicate]

I am trying to display a list of messages based on the recipient but for now, let's keep it simple. I am just trying to display a list of messages.
My rule looks like this
{
"rules": {
"communications" : {
"$communication":{
".read" : true,
".write": true
}
}
}
For some reason though, my application does not want to read it
fireRef = new Firebase(url);
fireRef.auth(MY_TOKEN);
commsRef = fireRef.child('communications')
$scope.communications = $firebase(commsRef)
It only works if I have a rule looking like
{
"rules": {
"communications" : {
".read" : true,
".write": true
}
}
But that will cause problem as I would like to add condition on the children node of my communication. Something like:
{
"rules": {
"communications" : {
".read" : true, ### I would like to get rid of this line as well and have the child handling it
".write": true,
"$communication":{
".read" : "data.child('to').val() == auth.uid"
}
}
}
I am assuming that is because I have a $firebase on the communications and it needs some read or write rules but how do I get the event when a new message is added otherwise
Thanks
With respect to security rules, Firebase operations are all-or-nothing.
That means that lists of data sent to the client will never be incomplete, or filtered views of the complete server data. As a result, attempting to load all of the data at /communications will fail when using your first set of security rules, even though you do have permission to read some of the data there as governed by the child rule at /communications/$communication.
To handle this use case, consider restructuring your data such that each communication is indexed by recipient, i.e. /communications/$recipient/$communication, which will simplify your security rules.
Additionally, you could even make that bucket read-only by the recipient (i.e. .read: auth.id == $recipient) while allowing anyone to send a message to that user (i.e. .write: auth != null && !data.exists()). That last rule ensures that the sending client is authenticated and writing to a location that does not yet exist, such as a new push id.

Resources