How to structure my data in Firebase - firebase

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.

Related

Can I apply write rule for anyone for updating a particular item inside the object stored in firebase rt database?

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,
}
}
}
}
}

Read subset of firebase realtime database with rules restrictions

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?

How to define that only one field is written to a particular node?

example:
if a user is an admin and is not a company, I want the roles node to only have admin: true (and I do not want another child with company: false) or if the user is a company, true (not admin: false)
{
// BAD
{
"roles": {
"$key": {
"admin": true, // or admin: false
"company": false // or company: true
}
}
}
// GOOD (but how to make?)
{
"roles": {
"$key": {
"admin": true, // or company: true
}
}
}
}
You'll need to add a catch-all rule to reject unmatched child nodes:
{
"rules": {
"roles": {
"$key": {
"admin": {
".validate": "newData.isBoolean()"
},
"$other": {
".validate": false
}
}
}
}
So with the above rules, each role can only have an admin property with a boolean value. If a client tries to write any other properties, that write will be rejected.

Firebase database security rules and dollar signs

I'm creating a Google docs type of app where multiple users can edit a document in real-time.
These are my security rules:
{
"rules": {
"docs": {
"$doc_id": {
".read": "auth.token.doc_id === $doc_id",
".write": "auth.token.doc_id === $doc_id"
}
}
}
}
This is my Auth token payload (example)
{
"provider": "anonymous",
"uid": "e71afdf1-2b31-4c75-8f10-a9b0f916915e",
"token": {
"doc_id": 11
}
}
So when I try to read this path /docs/11/data I get a Simulated read denied error.
On the other hand when I put this rule instead
".read": "auth.token.doc_id === 11", (i.e. hard code $doc_id as 11)
The read passes.
But isn't $doc_id equal to 11 when I read /docs/11/data or am I misunderstanding something?
P.S. In case it matters, here is the data inside my database
{
"docs": {
"2": {
"data": {
"title": "test"
}
},
"11": {
"data": {
"title": "test"
}
}
}
}

Firebase complex rules

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
}
},

Resources