Initial approach/problem
The user will be able to write to his own user node. Then the user has to be able to write as many buildings he/she wants and as many depts as he/she wants (there are also rooms but I will leave that aside for now for clarity's sake). The user should be able to read (and write) his own user node, buildings and departments but not the other users' node, buildings and departments.
Basically:
User > user's Building > building's Department (TOTAL read and write permissions)
User > Another User's Stuff (NO Permissions at all)
Here is the database strucure:
{
"buildings" : {
"-L9Bc9aazn3mNiW1elJk" : {
"address" : "",
"comments" : "",
"hasDepts" : {
"-L9FwBmYEnkZQzdFJ4lU" : true
},
"name" : "J house",
"ownerID" : "6hwNde08Wuaa9bfReR28niSbOsF3"
}
},
"depts" : {
"-L9FwBmYEnkZQzdFJ4lU" : {
"comments" : "",
"inBuilding" : "-L9Bc9aazn3mNiW1elJk",
"name" : "Dep 1"
},
},
"users" : {
"6hwNde08Wuaa9bfReR28niSbOsF3" : {
"isAdmin" : {
"-L9Bc9aazn3mNiW1elJk" : true,
}
}
}
Initial approach
Each users node has a child node isAdmin that contains all push keys of the buildings this user has created. Using the same logic, the buildings' node contains a hasDepts node with all the push keys from the depts that the user has created in that building.
If you can help I would really appreciate. Anybody out there?
I am using vue.js to write to firebase like this:
addBuilding: function () {
let userId = firebase.auth().currentUser.uid;
let buildingKey = buildingsRef.push().key;
this.newBuilding.ownerID = userId;
buildingsRef.child(buildingKey).set(this.newBuilding);
usersRef.child(userId).child('isAdmin').child(buildingKey).set(true);
}
Closest solution so far / by @André Kool (partially using the initial approach)
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
},
},
"buildings": {
"$pushKey" : {
".read": "root.child('buildings').child($pushKey).child('ownerID').val() === auth.uid",
".write": "!data.exists() || root.child('buildings').child($pushKey).child('ownerID').val() === auth.uid"
}
},
"depts": {
"$pushKey": {
".read": "root.child('buildings').child(root.child('depts').child($pushKey).child('inBuilding').val()).child('ownerID').val() === auth.uid",
".write": "root.child('buildings').child(root.child('depts').child($pushKey).child('inBuilding').val()).child('ownerID').val() === auth.uid"
}
},
}
With @André Kool's attempt the simulator allows it to read/write to the buildings/$pushKey node. However while it shows the node on the frontend as soon as it it is created, when we refresh the browser or add a new building the node disappears from the frontend (remains on the database). Firebase it's also not allowing to write to the depts node. Any clues and possible solutions?
UPDATE: another approach.
1) making sure both the buildings and depts nodes both have the ownerId child node, like this:
{
"buildings" : {
"-L9HIbKu5fIe8rfoePgi" : {
"address" : "",
"comments" : "",
"hasDepts" : {
"-L9HIdScisDItysCnMlm" : true
},
"name" : "building 1",
"ownerID" : "6hwNde08Wuaa9bfReR28niSbOsF3"
}
},
"depts" : {
"-L9HIdScisDItysCnMlm" : {
"comments" : "",
"inBuilding" : "-L9HIbKu5fIe8rfoePgi",
"name" : "dep 1",
"ownerID" : "6hwNde08Wuaa9bfReR28niSbOsF3"
}
},
"users" : {
"6hwNde08Wuaa9bfReR28niSbOsF3" : {
"isAdmin" : {
"-L9HIbKu5fIe8rfoePgi" : true
},
"name" : "João Alves Marrucho",
"userEmail" : "joaomarrucho@hotmail.com"
}
}
}
2) Use the ownerId to authorise read and writing on all buildings and depts:
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
},
},
"buildings": {
"$id": {
".read": "data.child('ownerID').val() == auth.uid" ,
".write": "data.child('ownerID').val() == auth.uid"
}
},
"depts": {
"$id": {
".read": "data.child('ownerID').val() == auth.uid" ,
".write": "data.child('ownerID').val() == auth.uid"
}
},
}
3) New problem with this approach!
With the rules above, using the simulator, it seems to me like firebase is not allowing the user to read/write to the buildings node but it's allowing to read/write to buildings/$pushKey node. Does firebase needs the user to be able read/write to both parent (buildings) and child node (buildings/$pushKey)?. And if so how can you prevent the user from deleting (.set) the whole buildings node?
I am asking this because if we add ".read": true, ".write": true before the $wildcards it writes the intended database structure while rendering the next rules on the cascade completely useless... So that's no good, but at least it kind of hints where part of the solution may reside.
"users": {
".read": true, <<<<<<
".write": true, <<<<<<
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
},
}, // but is should also be able to write to his own buildings
"buildings": {
".read": true, <<<<<<
".write": true, <<<<<<
"$id": {
".read": "data.child('ownerID').val() == auth.uid" ,
".write": "data.child('ownerID').val() == auth.uid"
}
},
"depts": {
".read": true, <<<<<<
".write": true, <<<<<<
"$id": {
".read": "data.child('ownerID').val() == auth.uid" ,
".write": "data.child('ownerID').val() == auth.uid"
}
},
}
(Other thoughts) I don't see why the user shouldn't be able to read/write to the buildings parent node (provided the rules prevent him from deleting the whole buildings node and only grant him full access to the buildings/$pushKeys he creates). Is that complicated? I may be getting this wrong, but doesn't firebase need to scan the buildings node before it knows which one belongs to the user?
If firebase rules can't resolve this issue like this, that means that in theory, every firebase app in which the user needs to read and write its own content to the realtime database, which is often the case, needs to store the information under a users.uid node to make it available to him/her. That seems to go against the "keep your database as flat as possible" firebase general instruction, and it also doesn't play well with the fact that any function with database references that have children in firebase require that data.snapshots nightmarish iteration. Moreover if you can't design a shallow structure, and the user id will constantly change, how would you go about writing the database references in the Apps firebase configuration: const buildingsRef = db.ref('users/'+ userId!!!!+'/buildings'); : >How to constrain read/write rules to the users that create the nodes while keeping this structure? There must be an good way to do this without bending over backwards. ?