Let's assume I have the following data structure in my Firebase database:
{
"allProjects": {
"foo": true,
"bar": true,
"baz": true
},
"allUsers": {
"user1": true,
"user2": true,
"user3": true
},
"projects": {
"foo": {
"name": "foo",
"members": {
"user1": true
}
},
"bar": {
"name": "bar",
"members": {
"user2": true
}
},
"baz": {
"name": "baz",
"members": {
"user1": true,
"user3": true
}
}
},
"users": {
"user1": {
"name": "user1"
},
"user2": {
"name": "user2"
},
"user3": {
"name": "user3"
}
}
}
Problem
I'm trying to write a few rules so I can protect my data. Here's a few requirements:
Users should only see projects they are part of (i.e. /project/:id/members/:userId === true)
Users should only see users that belong to the same projects as them.
Here's what I was able to come up with:
{
".read": false,
".write": false,
"allProjects": {
".read": "auth !== null"
},
"allUsers": {
".read": "auth !== null"
},
"projects": {
"$projectId": {
// requirement 1
".read": "data.child('members').hasChild(auth.uid)"
}
},
"users": {
"$userId": {
// requirement 2
".read": "???",
}
}
}
The typical approach would be to keep a list of users that each user can see.
user_friends: {
"user1": {
"user3": true
}
"user3": {
"user1": true
}
}
Then as users get added to/removed from projects, you'll need to keep this derived list up to date. Alternatively you can calculate this derived list in a schedule maintenance operation.
Duplicating data like this is one of the big differences between SQL databases and most NoSQL databases. In Firebase we often store extra data to allow for our use-cases. For a great introduction I recommend reading NoSQL data modeling and watching Firebase for SQL developers.
By the way, I noticed that you've combined metadata and the list of users under /projects/$projectid. This is not recommended. For best results, split them into two top-level lists.
"projects": {
"foo": {
"name": "foo",
},
"bar": {
"name": "bar",
},
"baz": {
"name": "baz",
}
},
"members": {
"foo": {
"user1": true
},
"bar": {
"user2": true
},
"baz": {
"user1": true,
"user3": true
}
},
Related
I would like to add firebase database rule to allow anyone to increment the "count" inside the message object. Is it possible to do so? If so, how should I write the rule for updating the count? Thanks!
{
"messages": {
"message0": {
"content": "Hello",
"count": 5
},
"message1": {
"content": "Goodbye",
"count": 10
},
...
}
}
I tried below rule, but it doesn't seem to work:
{
"rules": {
"messages": {
"$message": {
"count": {
".read": true,
".write": true,
}
}
}
}
}
allowed access with blank UID
DATA
{
"roles": {
"admins": {
"123456789": {
"name": "Max"
}
}
},
"meta": {},
"events": {
"3423556": {
},
"meta": {
}
},
}
}
RULES
"rules": {
"events": {
".read": "root.child('roles').child('admins').hasChild(auth.uid)",
}
}
it returns faile if I'm trying
Type read Location /events/ Data null Auth { "provider": "anonymous",
"uid": "1" } Admin false
and true if UID is blank
Type read Location /events/ Data null Auth { "provider": "anonymous",
"uid": "" } Admin false
I have a firebase real-time database structured like this:
{
"USERS": {
"user-1": { ... },
"user-2": { ... }
}
"GROUPS": {
"group-1": {
"id": "group-1",
"AUTH": {
"user-1": true,
}
},
"group-2": {
"id": "group-2",
"AUTH": {
"user-2": true
}
},
"group-3": {
"id": "group-3",
"AUTH": {
"user-1": true,
"user-2": true
}
}
}
}
I know that I can give read permissions that would give read access to users that only belong to a specific group, like this:
{
"rules": {
"GROUPS": {
"$groupId": {
".write": false,
".read": "auth != null && data.child('AUTH').child(auth.uid).exists()"
}
}
}
}
I'm trying to find a query + rules combination that would return me all the groups that the logged in user belongs to.
For example:
A query and firebase rule change that for logged in 'user-1' returns a snapshot of:
{
"group-1": {
"id": "group-1",
"AUTH": {
"user-1": true,
}
},
"group-3": {
"id": "group-3",
"AUTH": {
"user-1": true,
"user-2": true
}
}
}
OR
[
{
"id": "group-1",
"AUTH": {
"user-1": true,
}
},
{
"id": "group-3",
"AUTH": {
"user-1": true,
"user-2": true
}
}
]
querying for the "GROUPS" reference now give me a 'permissions denied' error.
adding '.read=true' to the GROUPS:
{
"rules": {
"GROUPS": {
".read": true,
"$groupId": {
".write": false,
".read": "auth != null && data.child('AUTH').child(auth.uid).exists()"
}
}
}
}
or any combination for '.read' that passes the condition, gives everyone access to all the data.
Is there a way (in the current structure) to query the database and get from the "GROUPS" branch all the groups that I belong to by adding some restriction to the rules?
or do I need to maintain under each user the list of the group that he belongs to?
I'm new to the data structure with Firebase.
I read this: https://firebase.google.com/docs/database/ios/structure-data
I understood that it was necessary to use a flattened structure.
But I'm not sure how to do that in real world.
And I think that to use the Firebase rules correctly I need a suitable structure (but I have no experience with that).
I have 2 types of data:
A list of parts :
parts: {
part1: {
chapter1: {
lesson1,
lesson2,
...etc
},
chapter2: {
lesson1,
lesson2,
...etc
},
chapterX: {
...etc
},
},
part2: {
chapter1: {
lesson1,
lesson2,
...etc
},
chapterX: {
...etc
},
},
partX: {
...etc
}
}
And a list of users :
users: {
user1: {
name,
email,
...etc
},
user2: {
name,
email,
...etc
},
user3: {
...etc
},
etc...
}
Each user can validate a lesson and its progress must be saved.
If the user validates all the lessons: he validates the chapter.
And if he validates all chapters: he validates the part.
I am thinking about to structure my Firebase database like this:
{
users: {
$uid: {
name,
email,
...
}
},
checked: {
user1: {
parts: {
part1: true
},
chapters: {
chapter1: true,
chapter2: true
},
lessons: {
lesson1: true,
lesson7: true,
lesson15: true,
}
},
user2: {
parts: {
part3: true
},
chapters: {
chapter1: true
},
lessons: {
lesson18: true,
lesson57: true,
}
}
}
}
And for Firebase rules:
{
"rules": {
"users": {
"$uid": {
".write": "$uid === auth.uid"
}
},
"checked": {
"$uid": {
".write": "$uid === auth.uid"
}
}
}
}
Is this the right way to go?
Are the rules OK?
Thanks!
Based on the information you provided, you are doing the right way. And the rules are OK. The structure of your database fits the way you want to read them so you are doing the right way.
I’m fairly new to Firebase and its json database. So right now I’m designing the tree hierarchy but when reading the documentation it says that “when you fetch data at a location in your database, you also retrieve all of its child nodes”.
I understand that this means that if you have this
a1
—b1
——c1
——c2
———d1
———d2
———d3
———d4
—b2
——c1
——c2
———etc…
and you want to fetch “c1”, you will download not just “c1” but “c2” and all “d”s. And it makes sense BUT in the example in the documentation it says that to list all “c1”s requires to download the whole tree from “a1”.
Am I correct? And if so, how can I model a tree where I need to isolate data by groups of users and all their internal shared info?
You can address each node in your tree by creating a reference to it. But if you retrieve the value for a node, you will also get each node under it.
So if you attach a listener to /a1/b1:
var root = firebase.database().ref();
root.child('a1/b1').on('value', ...
You will get c1, c2, d1, d2, d3 and d4.
But if you address the c1 directly, you will get only that node:
root.child('a1/b1/c1').on('value', ...
I have some nice examples for you.
You can't access a user's "name" without also downloading all of it's posts in this case:
{
"users": {
"user1": {
"name": "John",
"posts": {
"post1": {...},
"post2": {...}
}
},
"user2": {
"name": "Nate",
"posts": {
"post1": {...},
"post2": {...}
}
},
"user3": {
"name": "Buck",
"posts": {
"post1": {...},
"post2": {...}
}
}
}
}
Denormalize your database for better data querying and fetching:
{
"users": {
"user1": {
"name": "John",
"posts": {
"post2": true,
"post5": true
}
},
"user2": {
"name": "Nate",
"posts": {
"post1": true,
"post6": true
}
},
"user3": {
"name": "Buck",
"posts": {
"post3": true,
"post4": true
}
}
},
"posts":{
"post1":{
...,
"user":"user2"
},
"post2":{
...,
"user":"user1"
},
"post3":{
...,
"user":"user3"
},
"post4":{
...,
"user":"user3"
},
"post5":{
...,
"user":"user1"
},
"post6":{
...,
"user":"user2"
},
}
}