Rule definition and structure

Firebase rules provide predefined variables that can be used inside a rule definition:

Name Definition / Usage
auth It represents information of the authenticated user. It will be null for an unauthenticated user. It is an object that contains uid, token, and provider fields and corresponding values.
$ variables It represents wildcard path to refer to the dynamically generated keys and represent IDs.
root It represents data snapshot at the root path in the Firebase database before applying the given database operation.
data It represents the Data Snapshot before applying the given database operation. For example, in case of the update or write, the root represents the original data snapshot without the changes in the update or write.
newData It represents the Data Snapshot before applying the given database operation. However, it includes both the existing data as well as the new data, which includes data manipulated by the given data operation.
now

It represents current time in milliseconds—the number of seconds that have elapsed since January 1, 1970 (midnight UTC).

 

In the following section, we'll look at how we can use these predefined variables in our rules.

As we saw in the Authorization section, we need to see how rules will apply to the nested data. A rule of thumb is that we need to structure rules based on the structure of the data in the database.

We will extend our HelpDesk application developed in chapter 5User Profile and Access Management, of this book.

We have a data structure as follows:

"helpdesk" : {
"tickets" : {
"FlQefqueU2USLElL4vc5MoNUnu03" : {
"-L4L1BLYiU-UQdE6lKA_" : {
"comments" : "Need extra 4GB RAM in my system",
"date" : "Fri Feb 02 2018 15:51:10 GMT+0530 (India Standard
Time)",
"department" : "IT",
"email" : "[email protected]",
"issueType" : "Hardware Request",
"status" : "progress"
}
},
"KEEyErkmP3YE1BagxSci0hF0g8H2" : {
"-L4K01hUSDzPXTIXY9oU" : {
"comments" : "Not able to access my email",
"date" : "Fri Feb 02 2018 11:06:32 GMT+0530 (India Standard
Time)",
"department" : "IT",
"email" : "[email protected]",
"issueType" : "Email Related Issues",
"status" : "progress"
}
},
"all" : {
"-L4K01hUSDzPXTIXY9oU" : {
"comments" : "Not able to access my email",
"date" : "Fri Feb 02 2018 11:06:32 GMT+0530 (India Standard
Time)",
"department" : "IT",
"email" : "[email protected]",
"issueType" : "Email Related Issues",
"status" : "progress"
},
"-L4L1BLYiU-UQdE6lKA_" : {
"comments" : "Need extra 4GB RAM in my system",
"date" : "Fri Feb 02 2018 15:51:10 GMT+0530 (India Standard
Time)",
"department" : "IT",
"email" : "[email protected]",
"issueType" : "Hardware Request",
"status" : "progress"
}
}
}
}

Here, we can see that to have data to be secured at the user level, to show only the tickets relevant to the logged in user, we are storing them under userId, such as FlQefqueU2USLElL4vc5MoNUnu03 and KEEyErkmP3YE1BagxSci0hF0g8H2, and to show all the tickets to admin, we are storing them under all. However, this is not the ideal solution, since it has two issues: data is redundant, and to update any data, we will have to update it at two places. Luckily, we can handle this kind of security directly in the database with Rules.

We will change our data, and we will remove the all node from data. We will also add one variable under $userId to identify whether a user is an admin or not. So it will look like this:

"helpdesk" : {
"tickets" : {
"FlQefqueU2USLElL4vc5MoNUnu03" : {
"-L4L1BLYiU-UQdE6lKA_" : {
"comments" : "Need extra 4GB RAM in my system",
"date" : "Fri Feb 02 2018 15:51:10 GMT+0530 (India Standard
Time)",
"department" : "IT",
"email" : "[email protected]",
"issueType" : "Hardware Request",
"status" : "progress"
},
"isAdmin": true
},
"KEEyErkmP3YE1BagxSci0hF0g8H2" : {
"-L4K01hUSDzPXTIXY9oU" : {
"comments" : "Not able to access my email",
"date" : "Fri Feb 02 2018 11:06:32 GMT+0530 (India Standard
Time)",
"department" : "IT",
"email" : "[email protected]",
"issueType" : "Email Related Issues",
"status" : "progress"
},
"isAdmin": false
}
}
}
}

Our rules will look as follows:

{
"rules": {
"helpdesk": {
"tickets": {
".read": "data.child(auth.uid).child('isAdmin').val()==true",
".write": "data.child(auth.uid).child('isAdmin').val()==true",
"$uid": {
".read": "auth.uid == $uid",
".write": "auth.uid == $uid"
}
}
}

}
}

These rules essentially impose restrictions that if the user is Admin, that is, isAdmin is true, then they can read and write all the data. However, other users will only be able to read/write their own data.

Here, we have also used predefined variable data, which represents the DataSnapshot before applying a write operation. Similarly, we can use the root variable to refer to the root path and newData to refer to the data snapshot that will exist after a write operation.

Now, if you have observed, we have used .child, which is essentially used to refer to any child path/attribute. In our rule, we are checking that under $uid, the value of isAdmin is true, since we want to give access to all data to an admin. Similarly, we can use any data as a condition in our rules.

Also, an important thing to note here is that once we have defined .read and .write rules at parent level tickets, we are not checking the isAdmin condition under $uid, because rules do cascade, so once you have granted read/write permissions to admins, you don't need to repeat those conditions at the $uid level. At the same time, it is important to note that it is mandatory to have a rule defined at a parent location. If we don't define them at the parent location, your data operation will fail completely even though the child path is accessible.

For example, in the following rule, we can see that though we have access permissions at ticket level, we won't be able to access data since we haven't defined rules at $uid level:

{
"rules": {
"helpdesk": {
"tickets": {
"$ticketId": {
".read": true,
".write": true
}
}
}

}
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset