Using Firebase RuleDataSnapshot Methods to access user data - firebase

If I have data like this:
root{
PrivateRooms{
<PrivateRoomName>{
...
}
}
users{
<uid>{
permissionFrom{
<PrivateRoomName>:value is not private room name
}
...
}
}
}
...how do I write rules to grant access to < PrivateRoomName > based on the user's permissionFrom children?
I thought this would work:
{
"rules": {
"PrivateRooms" : {
"$roomName":{
".read" : "root.child('users').child(auth.uid).child('permissionFrom').hasChild('$roomName') == true",
".write" : "root.child('users').child(auth.uid).child('permissionFrom').hasChild('$roomName') == true"
}
},
"users" : {
".read" : true,
".write" : true
}
}
}
...but something must be wrong with the rules as this data snapshot is skipped over:
var testRef = new Firebase('https://my.firebaseio.com/privateRooms/<privateRoomName>/privRoomChild');
testRef.once("value", function(snapshot){
var testVal = snapshot.val();
console.log(testVal);
});

In your rules, $roomName is a variable, so it should not be wrapped in quotes.
"$roomName":{
".read" : "root.child('users').child(auth.uid).child('permissionFrom').hasChild($roomName)",
".write" : "root.child('users').child(auth.uid).child('permissionFrom').hasChild($roomName)"
}

Related

Securing Firebase RTDB with multiple databases for chat app

I am on the blaze plan and building a chat functionality in my app using Firebase realtime database.
I have 3 databases for now :
Main instance that contains sharding information and users chats' info.
The 2 other instances will contain the chats and messages.
The database structure for the main instance looks like this :
{
"shards" : {
"shard1" : {
"full" : false,
"name" : "shard1",
"num" : 1,
"url" : "shard1Url"
},
"shard2" : {
"full" : false,
"name" : "shard2",
"num" : 0,
"url" : "shard2Url"
}
}
}
And here is the data structure of one of the shard :
{
"users" : {
"user_id_1" : {
"chat_id_1" : true
},
"user_id_2" : {
"chat_id_1" : true
}
},
"chats" : {
"chat_id_1" : {
"created" : true,
"createdAt" : 1645888918717,
"members" : {
"user_id_1" : true,
"user_id_2" : true
},
"name" : "",
"uid" : "chat_id_1",
"updatedAt" : 1645895862214
}
},
"messages" : {
"chat_id_1" : {
"mess_id_1" : {
"content" : "Hello",
"chatID" : "chat_id_1",
"createdAt" : 1645895862214,
"senderID" : "user_id_1",
"uid" : "mess_id_1"
},
"mess_id_2" : {
"content" : "Bye",
"chatID" : "chat_id_1",
"createdAt" : 1645889441313,
"senderID" : "user_id_2",
"uid" : "mess_id_2"
}
}
}
}
I want the following rules to apply :
Reads :
A user can listen for changes under /users/$user_id/ where $user_id is auth.uid
A user can get all the chats from the above IDs when he is in the member map (under /chats/$chat_id)
A user can read all the messages from a chat when he is a member (under /messages/$chat_id)
Writes :
A user can create a chat if there is no data under the chat_id node
A user can update a chat if he is a member under the chat_id
A user can create a message if a chat_id exists and if he is a member under the chat_id and if the senderID matches the auth.uid
A user can delete a message if he is the sender of the message (not sure i can do this with security rules but only client side)
A user can add only an item like {"$chat_id": true} under any other user under /users/$user_id/
So far, here are my rules that are not working :
-> Main shard :
{
"rules": {
"shards": {
".read": "auth.uid != null",
".indexOn": ["full"],
"$shardName": {
"num": {
".write": "auth.uid != null"
},
"$other": {
".write": false
}
}
},
}
}
-> For other shards :
{
"rules": {
"users": {
"$userID": {
".write": "auth.uid != null && !data.exists()",
".read": "auth.uid == $userID",
}
},
"chats": {
"$chat_id": {
".read": "data.child('members/'+auth.uid).exists()",
".write": "(!data.exists() && newData.hasChild('members')) || data.child('members/'+auth.uid).exists()",
}
},
"messages": {
"$chat_id": {
".read": "root.child('chats').child($chat_id).child('members').child(auth.uid).exists()",
".indexOn": ["createdAt"],
"$mess_id": {
".write": "(!data.exists() && newData.child('senderID').val() == auth.uid) || (data.exists() && data.child('senderID').val() == auth.uid)"
}
}
}
}
}
EDIT :
I have made many edit regarding the database structure.
I especially need help with reads rather than writes, as writes seem to be working fine now but i would like confirmation and / or changes if you see mistakes.

What is rules in firebase? & What $ mean in the value incode?

What does $uid mean in the code ?
is $uid a column name or key name?
{
"rules": {
"users": {
"$uid": {
".write": "$uid === auth.uid"
}
}
}
}
as in document
A wildcard path used to represent ids and dynamic child keys.
more detail , let say you have this data
{
users : {
"SOME_KEY_1" : {"name" : 'test 1' , "private" : {...}} ,
"SOME_KEY_2" : {"name" : 'test 2' , "private" : {...}} ,
"SOME_KEY_3" : {"name" : 'test 3' , "private" : {...}} ,
}
}
and these rule
{
"rules" : {
"users" : {
"$uid" : {
"private" : "auth != null && auth.uid === $uid"
}
}
}
}
you cannot know the keys of users node but you need to prevent other users to private node except their own so you put $uid to represent dynamic child keys
in your case you just allowing user to write to their own data

firebase web - update database where a certain value is found

My database looks as below :
I need to update the division_name where the index_num is 3.
I tried the following code but it did not work -
var division_index_found="3";
var division_name_given="new div";
var query_update=firebase.database().ref("/divisions")
.orderByChild('index_num').equalTo(division_index_found);
query_update.once("child_added", function(snapshot) {
snapshot.ref.update({ division_name: division_name_given });
});
What approach to adopt here ?
EDIT1:
I get warning in chrome console :
FIREBASE WARNING: Using an unspecified index. Consider adding ".indexOn": "index_num" at /divisions to your security rules for better performance
EDIT2:
From the firebase database the districts node (?) looks like :
"districts" : {
"-KbVYCSO8wrMoXD3vL81" : {
"district_name" : "Rangpur",
"division_index" : "3",
"index_num" : 2
},
"-KbVYHgbWMDMtGsnmvei" : {
"district_name" : "jessore",
"division_index" : "3",
"index_num" : 3
},
"-KbVYKtSnPMFDkx9z0cU" : {
"district_name" : "district 1",
"division_index" : "3",
"index_num" : 4
}
}
Now I want to update district_name for a certain index_num and division_index. I use the following code :
var district_index="3";
var division_index="3"
var district_index_parsed = parseInt(district_index);
var query_update=firebase.database().ref("/districts")
.orderByChild('index_num').equalTo(district_index_parsed);
query_update.once("child_added", function(snapshot) {
snapshot.forEach(function(snapshot_indiv){
if(parseInt(snapshot_indiv.division_index)==parseInt(division_index)){
var district_name_again="new district name";
snapshot_indiv.ref.update({ district_name: district_name_again },function(error){
});
}// end of if division_index compare
});// end of forEach
});// end of query_update once
But the console shows :
Uncaught Error: No index defined for index_num
at je.get (firebase-database.js:94)
...
...
at edit_districts.php:359
...
which ultimately hints at the following line of code in my edit_districts.php file:
snapshot.forEach(function(snapshot_indiv){
inside the query_update.once part.The forEach method seems to make the problem.
And the security rules are defined as
{
"rules": {
".read": "auth != null",
".write": "auth != null",
"divisions": {
".indexOn": "index_num"
},
"districts": {
".indexOn": "index_num"
},
}
}
How to get rid of the error to update the database ?
You'll need to add an index to your Firebase Database security rules. I recommend reading the Firebase documentation on security rules, specifically the section on indexes.
From the documentation comes this first sample:
{
"rules": {
"dinosaurs": {
".indexOn": ["height", "length"]
}
}
}
Modified to your data structure, it'll look something like this:
{
"rules": {
"divisions": {
".indexOn": "index_num"
}
}
}

Firebase unable to write Security Rules for Unique Data

I want my vanNumber to be unique
//vanwithagent is after root "/vanwithagent"
vanwithagent : {
"-KSHJyDyI49RpZwSkdg1" : { //
"agentMobile" : "sdfs",
"agentName" : "sdfsdf",
"isAgentAssignedWithTask" : false,
"vanName" : "fsdf",
"vanNumber" : "sf",
"vanPresentLocation" : {
"currLattitude" : "N/A",
"currLongitude" : "N/A",
"pin" : "N/A"
}
}
}
Rules I have written:
{
"rules": { //Rules
".read": "auth != null",
".write": "auth != null",
"vanwithagent": {
"$vanwithagentId": {
"vanNumber":{
".validate":"!(root.child('vanwithagent').child(data.child('vanNumber').val()).exists())"
}
}
}
}
}
You cannot check if a value exists in a certain collection of values within Firebase's security rules. You can check if a certain key exists and if a key has a certain value. But this don't help you in this data model.
As usual the solution is to pick a data model that matches your requirements. If vans are unique and have an id, then store the list keyed under that id:
vanwithagent : {
"sf" : { // vanNumber
"agentMobile" : "sdfs",
"agentName" : "sdfsdf",
"isAgentAssignedWithTask" : false,
"vanName" : "fsdf",
"vanPresentLocation" : {
"currLattitude" : "N/A",
"currLongitude" : "N/A",
"pin" : "N/A"
}
}
}
This data structure guarantees that the van number is unique, without writing any security rules for it.

Firebase: permission_denied -- cannot access newData.child()

This is the structure of my data
{
"projects" : {
"proj1" : {
"-JccS4StrTnJdTficR-u" : {
"name" : "consultation",
"status" : false
},
"-Jd6JlJUfyyZ2U0NTQEs" : {
"name" : "Words",
"status" : false
}
},
"proj2" : {
"-JccS6nwYHRrxvjZtCkt" : {
"name" : "Rework",
"status" : false
},
"-Jd6Jqa7-EPJuWxbtJAV" : {
"name" : "Review",
"status" : false
}
},
"proj3" : {
"-JccSJ5lEBQEg3XCbG-u" : {
"name" : "translation",
"status" : false
}
}
}
}
Here are my rules
{
"rules": {
".read": true,
"projects": {
"proj1": {
".write": "auth != null"
},
"proj2": {
".write": true
},
"proj3": {
".write": true,
".validate": "newData.child('name').isString()
&& newData.child('name').val().length < 10"
}
}
}
}
When I created the database, there was no rule. Now, I've created the rules. I want the length of the name to be less than 10 characters.
When I run the code, I'm getting the following error: FIREBASE WARNING: set at /projects/proj3/-Jd4n6XditTHLWKVmhC6 failed: permission_denied
I can't exactly tell why it's not working. I've also tried just this portion newData.child('name').isString(), but I'm still getting the same error.
Here is the code that push data to FireBase
function saveToFB(taskName, proj) {
// this will save data to Firebase
var project=projTasks.child(proj);
project.push({
name: taskName,
status: false
});
};
Looking at the structure of your data, as well as your rules, it looks like there is a missing level of data between "proj1"/"proj2"/"proj3" and the object that has name and status attributes.
Specifically, the .validate rules you have configured is checking the length of the name attribute for "proj3", not the new element added to the list there.
To get around this, try increasing the depth of the security rules definitions, and make use of the $wildcard functionality, where any security rules key prefixed with $ is treated as a wildcard with a name that you can reuse.
{
"rules": {
".read": true,
"projects": {
"proj1": {
".write": "auth != null"
},
"proj2": {
".write": true
},
"proj3": {
".write": true,
"$someChildElementHere": {
".validate": "newData.child('name').isString()
&& newData.child('name').val().length < 10"
}
}
}
}
}

Resources